bug fixes and added more audio codecs

This commit is contained in:
steveseguin 2022-12-03 05:02:39 -05:00
parent 13d5e80d7c
commit ea52a3d164
17 changed files with 7769 additions and 390 deletions

View File

@ -510,7 +510,7 @@
user-select: none;
}
.pressed>.group{
border: solid 2px black;
box-shadow: inset 2px 2px 10px #0007, inset -2px -2px 10px #0007;
background-color: #276022aa;
}
button.pressed {

View File

@ -19,7 +19,6 @@ h1 {
font-size: 1rem;
padding: 10px;
position: relative;
user-select: none;
background: #d0d0d0;
border-radius: 4px;
}

View File

@ -57,7 +57,7 @@
<meta property="twitter:image" content="./media/vdoNinja_logo_full.png" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<link rel="stylesheet" href="./main.css?ver=229" />
<link rel="stylesheet" href="./main.css?ver=235" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.js"></script>
<style id="lightbox-animations" type="text/css"></style>
<!-- <link rel="manifest" href="manifest.json" /> -->
@ -73,7 +73,7 @@
<meta name="apple-mobile-web-app-status-bar" content="#db4938" />
-->
</head>
<body id="main" class="hidden">
<body id="main" class="hidden" onload="main()">
<span itemprop="image" itemscope itemtype="image/png">
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
@ -81,9 +81,9 @@
<span itemprop="thumbnail" itemscope itemtype="http://schema.org/ImageObject">
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=43"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=45"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=544"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=549"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:2%;-webkit-app-region: drag;min-height:20px;"></span>
<div id="header">
@ -109,6 +109,7 @@
data-drag="1"
onclick="copyFunction(this, event)"
class="task grabLinks"
data-menu="context-menu"
style="font-weight: bold; color: #afa !important; cursor: grab; background-color: #0000; font-size: 115%; min-width: 335px; max-width: 800px;"
></a>
<i class="las la-paperclip" style="color: #DDD;" onclick="copyFunction(document.getElementById('reshare'), event);" onmouseover="this.style.cursor='pointer'"></i>
@ -179,7 +180,9 @@
Stop Sharing Website
</div>
</div>
<div id="fullscreenPage" onmousedown="event.preventDefault(); event.stopPropagation();" title="Full-screen the page" alt="Full-screen the page" aria-label="Full screen" onclick="fullscreenPageToggle()" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<i id="fullscreenPageToggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-expand-arrows-alt my-float"></i>
</div>
<div id="flipcamerabutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Cycle the Cameras" onclick="cycleCameras()" class="hidden float" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="Cycle Cameras" alt="Cycle the Cameras">
<i id="settingstoggle" class="toggleSize las la-sync-alt my-float"></i>
</div>
@ -882,7 +885,7 @@
<font style="color:#daad09;">Welcome to VDO Ninja! We've rebranded! Nothing else is changing and we're staying 100% free.</font>
</h4>
<br />
🚀🚀 Site updated November 18th, 2022 to v22. If having new sudden issues, please try clearing your browser's cache, including refreshing the cache within any OBS browser source. You can also still access the previous version, which <a href="https://vdo.ninja/v21/">is hosted here</a>. Development <a target="_blank" href='https://updates.vdo.ninja/'>updates are here.</a>
🚀🚀 Site last updated on Nov.18th (<a target='_blank' href='https://docs.vdo.ninja/releases/v22'>v22 release notes</a>). You can also still access the previous version, which <a href="https://vdo.ninja/v21/">is hosted here</a>. Development <a target="_blank" href='https://updates.vdo.ninja/'>updates are here.</a>
<br />
<br />
<h3>
@ -943,7 +946,7 @@
<div class='directorBlock'>
<h2 title="Invite a guest or camera source to publish into the group room" style="margin-top: 5px;"><i class="las la-video director-link-icons" ></i><span data-translate="invite-a-guest">INVITE A GUEST</span></h2>
<span style="margin:5px; line-height: 1.6;" data-translate='invite-users-to-join'>Guests can use the link to join the group room</span>
<a onclick='copyFunction(this,event)' id="director_block_1" class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<a onclick='copyFunction(this,event)' id="director_block_1" class='task grabLinks' data-menu="context-menu" style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative; display:inline-block; max-width: 45%;">
<label class="switch" title="If disabled, the invited guest will not be able to see or hear anyone in the room.">
@ -962,7 +965,7 @@
<div class='directorBlock' style="background-color: var(--green-accent);" >
<h2 title="Use this link in the OBS Browser Source to capture the video or audio" style="margin-left: 1px;margin-top: 5px;"><i class="las la-th-large director-link-icons" style="margin-right: 6px;" ></i> <span data-translate="capture-a-group-scene">CAPTURE A GROUP SCENE</span></h2>
<span style="margin:5px; line-height: 1.6;" data-translate='this-is-obs-browser-source-link'>Use in OBS or other studio software to capture the group video mix</span>
<a onclick='copyFunction(this,event)' data-drag="1" draggable="true" id="director_block_3" class='task grabLinks' style='cursor:grab;background-color: #0003;'></a>
<a onclick='copyFunction(this,event)' data-drag="1" draggable="true" id="director_block_3" data-menu="context-menu" class='task grabLinks' style='cursor:grab;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative; display:inline-block; max-width: 45%;">
<label class="switch" title="If disabled, you must manually add a video to a scene for it to appear.">
@ -1318,8 +1321,8 @@
</div>
<div id="hiddenElements"></div>
<div id="overlayClockContainer" data-initial="600" class="hidden"><span id="overlayClock"></span></div>
<div id="overlayClockContainer2" data-initial="600" class="hidden"><span id="overlayClock2"></span></div>
<div id="overlayClockContainer" data-menu='context-menu-clock' data-initial="600" class="hidden"><span id="overlayClock"></span></div>
<div id="overlayClockContainer2" data-menu='context-menu-clock' data-initial="600" class="hidden"><span id="overlayClock2"></span></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;">
@ -1502,7 +1505,7 @@
</button>
<span class="hidden advanced" data-cluster="2">
<span class="hidden advanced audiocluster1" data-cluster="2">
<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>
@ -1515,7 +1518,7 @@
</button>
</span>
<span class="hidden advanced" data-cluster="2">
<span class="hidden advanced audiocluster2" data-cluster="2">
<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>
@ -1528,7 +1531,7 @@
</button>
</span>
<span class="hidden advanced" data-cluster="2">
<span class="hidden advanced groupcluster1" data-cluster="2">
<button style="width:35.2px;" data-action-type="toggle-group" data-group="1" title="Add/remove from group 1" onclick="changeGroup(this);">
<span >G1</span>
</button>
@ -1540,7 +1543,7 @@
</button>
</span>
<span class="hidden advanced" data-cluster="2">
<span class="hidden advanced groupcluster2" data-cluster="2">
<button style="width:35.2px;" data-action-type="toggle-group" data-group="4" title="Add/remove from group 4" onclick="changeGroup(this);">
<span >G4</span>
@ -1765,7 +1768,7 @@
<button id="pIpStartButton" style="width: 135px; background-color:#EFEFEF;padding:20px;text-align:center;display:none;"><b>Preview PiP VIdeo</b><br /><i style="padding:5px; font-size:300%;color:black;" class="las la-compress-arrows-alt"></i></button>
<div class="hidden" id="grabDirectorSoloLinkParent" title="The solo view link of the Director's video."><i class="las la-user"></i> Director's solo link:<a onclick="copyFunction(this,event)" data-drag="1" draggable="true" id="grabDirectorSoloLink" class="task" ></a></div>
<div class="hidden" id="grabDirectorSoloLinkParent" title="The solo view link of the Director's video."><i class="las la-user"></i> Director's solo link:<a onclick="copyFunction(this,event)" data-drag="1" draggable="true" id="grabDirectorSoloLink" data-menu="context-menu" class="task" ></a></div>
<br />
<button onclick="toggleSettings()" class="toggleSettings"><i class="chevron right" style="font-size:150%;top:3px;position:relative;"></i> <b><span data-translate="close-settings">Close Settings</span></b></button>
@ -1865,6 +1868,16 @@
</li>
</ul>
</nav>
<nav id="context-menu-clock" class="context-menu">
<ul class="context-menu__items">
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="pip-clock">
<i class="las la-external-link"></i>
<span data-translate="detach-clock2-pip">Pop-out clock toggle</span>
</a>
</li>
</ul>
</nav>
<nav id="context-menu-video" class="context-menu">
<ul class="context-menu__items">
<li class="context-menu__item">
@ -2206,7 +2219,7 @@
</u>
</div>
<div id="meshcastMenu" class="hidden">
Publishing Region: <select name="edgelist" id="edgelist" onchange="selectMeshcast(this);" title="Select a location that is closest to both you and your audience."></select>
Meshcast publishing region: <select name="edgelist" id="edgelist" onchange="selectMeshcast(this);" title="Select a location that is closest to both you and your audience."></select>
</div>
<script>
@ -2220,7 +2233,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 = "22.7"; // nov 18th
session.version = "22.9"; // nov 18th
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
@ -2292,11 +2305,11 @@
// session.defaultBackgroundImages = ["./media/bg_sample1.webp", "./media/bg_sample2.webp"]; // for &effects=5 (virtual backgrounds)
// session.hidehome = true; // If used, 'hide home' will make the landing page inaccessible, along with hiding a few go-home elements.
</script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=554"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=572"></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=485"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=497"></script>
</body>
</html>

998
lib.js

File diff suppressed because it is too large Load Diff

View File

@ -119,7 +119,10 @@ tr {
th {
padding:4px;
}
.preSelectButton {
margin: 4px 0 4px 8px;
padding: 1px 4px;
}
.meter {
display: inline-block;
width: 0px;
@ -356,11 +359,20 @@ button.white:active {
text-align: center;
position: fixed;
overflow-wrap: anywhere;
pointer-events: none;
cursor: pointer;
user-select: none;
}
#overlayClock{
padding:2px 20px;
background-color: #0009;
background-color: #0009;
}
#overlayClock video {
width: calc(22vh + 22vw / 2);
max-width: 100%;
max-height:25%;
}
#overlayClock:empty{
display:none;
}
#overlayClockContainer2{
margin: 0 auto;
@ -377,12 +389,16 @@ button.white:active {
right:0;
bottom:0;
overflow-wrap: anywhere;
pointer-events: none;
cursor: pointer;
user-select: none;
}
#overlayClock2{
padding:0 5px;
background-color: #0009;
}
#overlayClock2:empty{
display:none;
}
#overlayMsgs{
margin:0 auto;
background-color: #0000;
@ -486,7 +502,7 @@ body.darktheme .credits>a:visited {
}
.advDirectGuestSettings {
padding: 10px;
padding: 10px 5px;
max-height: 400px;
overflow-y: auto;
}
@ -1262,6 +1278,11 @@ input[type='radio'] { cursor:pointer; }
z-index: 10;
}
#recordLocalbutton.la-spinner {
animation: spin-animation 3s infinite;
display: inline-block;
}
.retry-spinner {
border: 1vh solid #7f838666;
border-top: 1vh solid #f0f0f066;
@ -3330,7 +3351,7 @@ a#reshare {
margin: 0px var(--regular-margin);
}
span#guestTips {
#guestTips {
margin: 0 auto 15px auto;
width: 450px;
display: flex;
@ -4202,6 +4223,8 @@ input:checked + .slider:before {
content: "\f1e6"; }
.la-reply:before {
content: "\f3e5"; }
.la-expand-arrows-alt:before {
content: "\f31e"; }
.la-headset:before {
content: "\f590"; }
.la-check:before {

165
main.js
View File

@ -535,14 +535,34 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
document.addEventListener('fullscreenchange', event => {
log("full screen change event");
log(event);
if (session.orientation && session.mobile){
if (document.fullscreenElement) {
document.exitFullscreen();
getById("fullscreenPageToggle").classList.add("la-expand-arrows-alt");
getById("fullscreenPageToggle").classList.remove("la-compress-arrows-alt");
}
} else {
updateMixer();
return;
}
if (document.fullscreenElement) {
getById("fullscreenPageToggle").classList.remove("la-expand-arrows-alt");
getById("fullscreenPageToggle").classList.add("la-compress-arrows-alt");
} else {
getById("fullscreenPageToggle").classList.add("la-expand-arrows-alt");
getById("fullscreenPageToggle").classList.remove("la-compress-arrows-alt");
}
updateMixer();
});
if (urlParams.has('fullscreenbutton') || urlParams.has('fsb')){ // just an alternative; might be compoundable
if (!(iOS || iPad)){
session.fullscreenButton = true;
getById("fullscreenPage").classList.remove("hidden");
}
}
// fullScreenPage
if (urlParams.has('midi') || urlParams.has('hotkeys')) {
session.midiHotkeys = urlParams.get('midi') || urlParams.get ('hotkeys') || 1;
@ -712,6 +732,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.layout = {};
}
}
console.warn("Warning: If using &layout with &broadcast, only the director's video will appear in the custom layout, which is likely not intended.");
}
if (urlParams.has('layouts')) { // an ordered array of layouts, which can be used to switch between using the API layouts action.
@ -2420,6 +2441,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
} else if (session.h264profile=="false"){
session.h264profile = false;
}
} else if ((session.codec==="hardware") && Android){ // same as &h264profile, but easier for me to remember. I'll try to automate this in the future.
session.codec = "h264";
session.h264profile = "42e01f";
}
if (urlParams.has('nofec')){ // disables error control / throttling -- currently on audio
@ -3108,6 +3132,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('showall')){ // just an alternative; might be compoundable
session.showall = true;
}
if (urlParams.has('samplerate') || urlParams.has('sr')) {
session.sampleRate = parseInt(urlParams.get('samplerate')) || parseInt(urlParams.get('samplerate')) || 48000;
@ -3119,8 +3145,39 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
});
session.audioEffects = true;
}
// if (session.audioCodec === "lyra"){ // WIP. does not work
// try {
// var { default: Module } = await import('./thirdparty/lyra/webassembly_codec_wrapper.js');
// await Module().then((module) => {
// console.log("Initialized codec's wasmModule.");
// session.lyraCodecModule = module;
// }).catch(e => {
// console.log(`Module() error: ${e.name} message: ${e.message}`);
// });
// } catch(e){
// errorlog(e);
// }
// if (session.lyraCodecModule){
// console.log("Lyra module loaded");
// session.micSampleRate = 16000;
// session.encodedInsertableStreams = true;
// } else {
// console.log("Lyra module failed to load");
// }
// }
if (urlParams.has("insertablestreams")){
session.encodedInsertableStreams = true;
}
if (urlParams.has('micsamplerate') || urlParams.has('msr')) {
session.micSampleRate = parseInt(urlParams.get('micsamplerate')) || parseInt(urlParams.get('msr')) || 48000;
}
if (urlParams.has('noaudioprocessing') || urlParams.has('noap')) {
session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers?
session.disableViewerWebAudioPipeline = true; // this has the potential to break things.
session.audioEffects = false; // disable audio inbound effects also.
session.audioMeterGuest = false;
if (session.noisegate===null){
@ -3414,8 +3471,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
}
if (session.roomid || urlParams.has('roomid') || urlParams.has('r') || urlParams.has('room') || filename || (session.permaid !== false)) {
var roomid = "";
if (urlParams.has('room')) { // needs to be first; takes priority
roomid = urlParams.get('room');
@ -3438,13 +3495,13 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('effects') || urlParams.has('effect')) {
session.effect = urlParams.get('effects') || urlParams.get('effect') || null;
}
if (window.FaceDetector !== undefined){
document.querySelectorAll(".facetracker").forEach(ele=>{
ele.disabled = null;
ele.removeAttribute("disabled");
ele.title = "Will slowly pan, tilt, and zoom in on the first face detected";
});
}
@ -5172,27 +5229,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
});
}
window.onload = function winonLoad() { // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending
window.addEventListener("beforeunload", confirmUnload);
window.addEventListener("unload", function(e) {
try {
session.ws.close();
if (session.videoElement.recording) {
session.videoElement.recorder.writer.close();
session.videoElement.recording = false;
}
for (var i in session.rpcs) {
if (session.rpcs[i].videoElement) {
if (session.rpcs[i].videoElement.recording) {
session.rpcs[i].videoElement.recorder.writer.close();
session.rpcs[i].videoElement.recording = false;
}
}
}
session.hangup();
} catch (e) {}
});
};
var lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
@ -5229,6 +5265,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
AltPressed = false;
}
if (event.key === "Escape") {
if (document.fullscreenElement) {
document.exitFullscreen();
//updateMixer();
}
return;
}
if (session.disableHotKeys){return;}
if (PPTHotkey){
@ -5366,31 +5410,54 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
});
setTimeout(function(){ // lets lazy load the following..
window.addEventListener("beforeunload", confirmUnload); // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending
window.addEventListener("unload", function(e) {
try {
session.ws.close();
if (session.videoElement.recording) {
session.videoElement.recorder.writer.close();
session.videoElement.recording = false;
}
for (var i in session.rpcs) {
if (session.rpcs[i].videoElement) {
if (session.rpcs[i].videoElement.recording) {
session.rpcs[i].videoElement.recorder.writer.close();
session.rpcs[i].videoElement.recording = false;
}
}
}
session.hangup();
} catch (e) {
errorlog(e);
}
});
try {
navigator.serviceWorker.getRegistrations().then(registrations => { // getting rid of old service workers.
try {
log(registrations);
for(let registration of registrations) {
if (registration.scope != "https://"+window.location.hostname+window.location.pathname+"thirdparty/"){
registration.unregister();
}
}
} catch(e){}
}).catch(errorlog);
} catch(e){}
var script = document.createElement('script');
document.head.appendChild(script);
script.onload = function() {
var script = document.createElement('script');
document.head.appendChild(script);
script.src = "./thirdparty/StreamSaver.js?v=13"; // dynamically load this only if its needed. Keeps loading time down.
};
script.src = "./thirdparty/polyfill.min.js"; // dynamically load this only if its needed. Keeps loading time down.
},100);
}
main(); // asyncronous load
try {
navigator.serviceWorker.getRegistrations().then(registrations => { // getting rid of old service workers.
try {
log(registrations);
for(let registration of registrations) {
if (registration.scope != "https://"+window.location.hostname+window.location.pathname+"thirdparty/"){
registration.unregister();
}
}
} catch(e){}
}).catch(errorlog);
} catch(e){}
setTimeout(function(){ // lazy load
var script = document.createElement('script');
document.head.appendChild(script);
script.onload = function() {
var script = document.createElement('script');
document.head.appendChild(script);
script.src = "./thirdparty/StreamSaver.js?v=13"; // dynamically load this only if its needed. Keeps loading time down.
};
script.src = "./thirdparty/polyfill.min.js"; // dynamically load this only if its needed. Keeps loading time down.
},0);
// main(); //calling this now from body tag.

View File

@ -2,13 +2,6 @@ body{
zoom: 75%;
background-color: #1F1E1F;
}
.hidden{
display:unset!important;
visibility: visible;
width:unset;
height:unset;
opacity: 1;
}
button[data-action-type='solo-chat'] {
display:none! important;
}
@ -54,7 +47,66 @@ span[data-action-type='sceneCluster2']{
span[data-action-type='sceneCluster1']{
display:none! important;
}
button[data-action-type='recorder-remote']{
display:none! important;
}
button[data-action-type='advanced-camera-settings']{
display:inline-block! important;
}
button[data-action-type='force-keyframe']{
display:none! important;
}
body {
font-size: 0.9em! important;
}
button[data-action-type='mute-scene']{
display:inline-block! important;
visibility: visible;
width: unset;
height: unset;
opacity: 1;
}
button[data-action-type='stats-remote']{
display:inline-block! important;
visibility: visible;
width: unset;
height: unset;
opacity: 1;
}
button[data-action-type='advanced-audio-settings']{
display:inline-block! important;
visibility: visible;
width: unset;
height: unset;
opacity: 1;
}
button[data-action-type='advanced-camera-settings']{
display:inline-block! important;
visibility: visible;
width: unset;
height: unset;
opacity: 1;
}
button[data-action-type='advanced-camera-settings']{
display:inline-block! important;
visibility: visible;
width: unset;
height: unset;
opacity: 1;
}
.groupcluster1 {
display:inline-block! important;
visibility: visible;
width: unset;
height: unset;
opacity: 1;
}
.hideDropMenu:empty{
display:none;
}
.hideDropMenu {
display:none;
}
.orderspan{
display:none! important;
}
@ -64,9 +116,8 @@ span[data-action-type='sceneCluster1']{
.directorContainer {
display:none!important;
}
.hideDropMenu{
display:none!important;
:root {
--advanced-mode: inline;
}
#header{

View File

@ -507,9 +507,13 @@
user-select: none;
}
.pressed>canvas{
border: solid 2px black;
box-shadow: inset 2px 2px 10px #0007, inset -2px -2px 10px #0007;
background-color: #FFFA;
}
.pressed>.group{
box-shadow: inset 2px 2px 10px #0007, inset -2px -2px 10px #0007;
background-color: #276022aa;
}
button.pressed {
background-color: #CEF;
}
@ -2450,7 +2454,7 @@
additional += "&broadcast";
toggleBroadcast = true;
} else {
additional += "&layout";
additional += "&layout"; // do not use &broadcast with &layout, else you will get broken results.
toggleBroadcast = false;
}

View File

@ -28,6 +28,10 @@ Copyright (c) 2012-2020 [Muaz Khan](https://github.com/muaz-khan)
var CodecsHandler = (function() {
function preferCodec(sdp, codecName) {
if (codecName){
codecName = codecName.toLowerCase();
}
var info = splitLines(sdp);
if (!info.videoCodecNumbers) {
return sdp;
@ -41,18 +45,45 @@ var CodecsHandler = (function() {
return sdp;
} else if (codecName === 'av1' && info.av1LineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'red' && info.redLineNumber === info.videoCodecNumbers[0]) {
return sdp;
} else if (codecName === 'fec' && info.fecLineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
}
//} else if (codecName === 'red' && info.redLineNumber === info.videoCodecNumbers[0]) {
// return sdp;
//} else if (codecName === 'fec' && info.fecLineNumber === info.videoCodecNumbers[0]) {
// return sdp;
//}
sdp = preferCodecHelper(sdp, codecName, info);
return sdp;
}
function preferAudioCodec(sdp, codecName) {
if (codecName){
codecName = codecName.toLowerCase();
}
var info = splitAudioLines(sdp);
if (!info.audioCodecNumbers) {
return sdp;
} else if (codecName === 'opus' && info.opusLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'isac' && info.isacLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'g722' && info.g722LineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'pcmu' && info.pcmuLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'pcma' && info.pcmaLineNumber === info.audioCodecNumbers[0]) {
return sdp;
} else if (codecName === 'red' && info.redLineNumber === info.audioCodecNumbers[0]) {
return sdp;
}
function preferCodecHelper(sdp, codec, info, ignore) {
sdp = preferAudioCodecHelper(sdp, codecName, info);
return sdp;
}
function preferCodecHelper(sdp, codec, info) {
var preferCodecNumber = '';
if (codec === 'vp8') {
@ -82,28 +113,26 @@ var CodecsHandler = (function() {
return sdp;
}
preferCodecNumber = info.av1LineNumber;
} else {
return sdp;
}
//} else if (codec === 'red') {
// if (!info.redLineNumber) {
// return sdp;
// }
// preferCodecNumber = info.redLineNumber;
} else if (codec === 'red') {
if (!info.redLineNumber) {
return sdp;
}
preferCodecNumber = info.redLineNumber;
} else if (codec === 'fec') {
if (!info.fecLineNumber) {
return sdp;
}
preferCodecNumber = info.fecLineNumber;
}
//} else if (codec === 'fec') {
// if (!info.fecLineNumber) {
// return sdp;
// }
// preferCodecNumber = info.fecLineNumber;
// }
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
var newOrder = [preferCodecNumber];
if (ignore) {
newOrder = [];
}
info.videoCodecNumbers.forEach(function(codecNumber) {
if (codecNumber === preferCodecNumber) return;
newOrder.push(codecNumber);
@ -114,6 +143,55 @@ var CodecsHandler = (function() {
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
return sdp;
}
function preferAudioCodecHelper(sdp, codec, info) {
var preferCodecNumber = '';
if (codec === 'opus') {
if (!info.opusLineNumber) {
return sdp;
}
preferCodecNumber = info.opusLineNumber;
} else if (codec === 'isac') {
if (!info.isacLineNumber) {
return sdp;
}
preferCodecNumber = info.isacLineNumber;
} else if (codec === 'g722') {
if (!info.g722LineNumber) {
return sdp;
}
preferCodecNumber = info.g722LineNumber;
} else if (codec === 'pcmu') {
if (!info.pcmuLineNumber) {
return sdp;
}
preferCodecNumber = info.pcmuLineNumber;
} else if (codec === 'pcma') {
if (!info.pcmaLineNumber) {
return sdp;
}
preferCodecNumber = info.pcmaLineNumber;
} else if (codec === 'red') {
if (!info.redLineNumber) {
return sdp;
}
preferCodecNumber = info.redLineNumber;
}
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
var newOrder = [preferCodecNumber];
info.audioCodecNumbers.forEach(function(codecNumber) {
if (codecNumber === preferCodecNumber) return;
newOrder.push(codecNumber);
});
newLine += newOrder.join(' ');
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
return sdp;
}
function splitLines(sdp) {
var info = {};
@ -127,36 +205,82 @@ var CodecsHandler = (function() {
info.videoCodecNumbersOriginal = line;
});
}
var LINE = line.toUpperCase();
if (line.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
if (LINE.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
if (LINE.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
if (LINE.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('H265/90000') !== -1 && !info.h265LineNumber) {
if (LINE.indexOf('H265/90000') !== -1 && !info.h265LineNumber) {
info.h265LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
if (LINE.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
} else if (line.indexOf('AV1/90000') !== -1 && !info.av1LineNumber) {
} else if (LINE.indexOf('AV1/90000') !== -1 && !info.av1LineNumber) {
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('red/90000') !== -1 && !info.redLineNumber) {
//if (LINE.indexOf('RED/90000') !== -1 && !info.redLineNumber) {
// info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
// }
// if (LINE.indexOf('FEC/90000') !== -1 && !info.fecLineNumber) {
// info.fecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
// }
});
return info;
}
function splitAudioLines(sdp) {
var info = {};
sdp.split('\n').forEach(function(line) {
if (line.indexOf('m=audio') === 0) {
info.audioCodecNumbers = [];
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
codecNumber = codecNumber.trim();
if (!codecNumber || !codecNumber.length) return;
info.audioCodecNumbers.push(codecNumber);
info.audioCodecNumbersOriginal = line;
});
}
var LINE = line.toLowerCase();
if (LINE.indexOf('opus/48000') !== -1 && !info.opusLineNumber) {
info.opusLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('isac/32000') !== -1 && !info.isacLineNumber) {
info.isacLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('g722/8000') !== -1 && !info.g722LineNumber) {
info.g722LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('pcmu/8000') !== -1 && !info.pcmuLineNumber) {
info.pcmuLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('pcma/8000') !== -1 && !info.pcmaLineNumber) {
info.pcmaLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (LINE.indexOf('red/48000') !== -1 && !info.redLineNumber) {
info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('ulpfec/90000') !== -1 && !info.fecLineNumber) {
info.fecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
});
return info;
@ -494,25 +618,25 @@ var CodecsHandler = (function() {
appendOpusNext += ';cbr=' + params.cbr; // default is 0 (vbr)
}
}
if (typeof params.dtx != 'undefined') {
if (params.dtx){
if (sdpLines[opusFmtpLineIndex].split("usedtx=").length==1){
appendOpusNext += ';usedtx=1';
}
}
}
if (typeof params.useinbandfec != 'undefined') { // useful for handling packet loss
if (sdpLines[opusFmtpLineIndex].split("useinbandfec=").length==1){
appendOpusNext += ';useinbandfec=' + params.useinbandfec; // Defaults to 0
} else {
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].replace("useinbandfec="+(params.useinbandfec ? 0 : 1), "useinbandfec="+params.useinbandfec);
}
}
if (typeof params.usedtx != 'undefined') { // Default is 0
if (sdpLines[opusFmtpLineIndex].split("usedtx=").length==1){
appendOpusNext += ';usedtx=' + params.usedtx; // if decoder prefers the use of DTX.
}
}
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext);
sdp = sdpLines.join('\r\n');
return sdp;
}
@ -548,6 +672,73 @@ var CodecsHandler = (function() {
}
return 32768;
}
function modifyDescLyra(modifiedSDP) { // WIP
if (!modifiedSDP.includes("m=audio")){ // don't bother modifying if no audio line found
return modifiedSDP;
}
///// Snippet based on Apache 2.0 licenced code. Source: https://github.com/Flash-Meeting/lyra-webrtc //////////
modifiedSDP = modifiedSDP.replace("SAVPF 111", "SAVPF 109 111").replace("a=rtpmap:111", "a=rtpmap:109 L16/16000/1\r\na=fmtp:109 ptime=20\r\na=rtpmap:111");
modifiedSDP = modifiedSDP.replace("a=rtpmap:106 CN/32000\r\n", "").replace("a=rtpmap:105 CN/16000\r\n", "").replace("a=rtpmap:13 CN/8000\r\n", "").replace(" 106 105 13", "");
///////////////////////////////
return modifiedSDP;
}
function modifyDescPCM(modifiedSDP, rate=32000, stereo=false, ptimeOverride=false) {
if (!modifiedSDP.includes("m=audio")){ // don't bother modifying if no audio line found
return modifiedSDP;
}
var ptime = 10;
if (ptimeOverride){
ptime = parseInt(ptimeOverride); // 10 seems to work with 48000, so might as well make it default
}
ptime = parseInt(ptime/10)*10;
if (ptime<10){
ptime = 10;
}
rate = parseInt(rate) || 32000;
if (!stereo && (rate>=48000)){
rate = 48000; // 44100 doesn't want to work for me, so we'll skip it.
ptime = 10; // 48000 only works with ptime=10
} else if (!stereo && rate>=44100){
rate = 44100; // 44100 doesn't want to work for me, so we'll skip it.
ptime = 10;
} else if (rate>=32000){
rate = 32000;
if (stereo){
ptime=10; // can be ptime = 20 if not stereo
} else if (ptime>20){
ptime=20;
}
} else if (rate>=16000){
rate = 16000;
if (stereo){
if (ptime>20){
ptime=20; // can be ptime = 20 if not stereo
}
} else if (ptime>40){
ptime=40;
}
} else {
rate = 8000;
if (stereo){
if (ptime>40){
ptime=40; // can be ptime = 20 if not stereo
}
}
}
if (stereo){
modifiedSDP = modifiedSDP.replace("SAVPF 111", "SAVPF 109 111").replace("a=rtpmap:111", "a=rtpmap:109 L16/"+rate+"/2\r\na=fmtp:109 ptime="+ptime+"\r\na=rtpmap:111");
} else {
modifiedSDP = modifiedSDP.replace("SAVPF 111", "SAVPF 109 111").replace("a=rtpmap:111", "a=rtpmap:109 L16/"+rate+"/1\r\na=fmtp:109 ptime="+ptime+"\r\na=rtpmap:111");
}
modifiedSDP = modifiedSDP.replace("a=rtpmap:106 CN/32000\r\n", "").replace("a=rtpmap:105 CN/16000\r\n", "").replace("a=rtpmap:13 CN/8000\r\n", "").replace(" 106 105 13", "");
return modifiedSDP;
}
return {
@ -556,6 +747,10 @@ var CodecsHandler = (function() {
disablePLI: disablePLI,
disableREMB: disableREMB,
modifyDescPCM: modifyDescPCM,
modifyDescLyra: modifyDescLyra,
getVideoBitrates: function(sdp) {
return getVideoBitrates(sdp);
@ -572,7 +767,9 @@ var CodecsHandler = (function() {
return getOpusBitrate(sdp);
},
preferCodec: preferCodec
preferCodec: preferCodec,
preferAudioCodec: preferAudioCodec
};
})();

25
thirdparty/lyra/README.md vendored Normal file
View File

@ -0,0 +1,25 @@
## WebRTC snippets for Lyra sourced from meeting.dev
Apache 2.0 Licenced
Sourced from: https://meeting.dev/lab/lyra-webrtc/loopback.html
## NOTE FROM STEVE
At the moment Lyra isn't actually being used, but I'm trying to get it working with VDO.Ninja. It may be removed at some future point - steve
## TFLITE MODELS SOURCED FROM GOOGLE ON GITHUB
google/lyra is licensed under the
Apache License 2.0
https://github.com/google/lyra
https://github.com/google/lyra/tree/f079e8c4dd1c61c87de1852178976ee3bdf15561/model_coeffs
## Further Acknowledgments
Thanks to [the team that developed Lyra](https://ai.googleblog.com/2021/02/lyra-new-very-low-bitrate-codec-for.html) and to [mayitayew for making it work with WASM](https://github.com/mayitayew/soundstream-wasm).
We are using a modified Lyra WASM. It is available [here](https://github.com/Flash-Meeting/lyra-wasm)

View File

@ -0,0 +1,6 @@
TFLITE MODELS SOURCED FROM GOOGLE ON GITHUB
google/lyra is licensed under the
Apache License 2.0
https://github.com/google/lyra
https://github.com/google/lyra/tree/f079e8c4dd1c61c87de1852178976ee3bdf15561/model_coeffs

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long