mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-11 13:48:38 +00:00
improved &chunked mode; other features
for experimentation
This commit is contained in:
parent
518ac2a4ad
commit
c24dac0253
16
iframe.html
16
iframe.html
@ -233,6 +233,11 @@
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "get media device list";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"getDeviceList":true}, '*');};
|
||||
iframeContainer.appendChild(button);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = "Start AutoMixer";
|
||||
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":true}, '*');};
|
||||
@ -347,6 +352,17 @@
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
if ("deviceList" in e.data){
|
||||
var outputWindow = document.createElement("div");
|
||||
outputWindow.innerHTML = "child-page-action: deviceList<br />";
|
||||
for (var i = 0;i<e.data.deviceList.length;i++){
|
||||
outputWindow.innerHTML += e.data.deviceList[i].label + "<br />";
|
||||
}
|
||||
outputWindow.style.border="1px dotted black";
|
||||
iframeContainer.appendChild(outputWindow);
|
||||
}
|
||||
|
||||
|
||||
if ("loudness" in e.data){
|
||||
console.log(e.data);
|
||||
if (document.getElementById("loudness")){
|
||||
|
||||
@ -115,7 +115,7 @@
|
||||
<div id="controlButtons" >
|
||||
<div id="obsState" class="advanced" >ACTIVE</div>
|
||||
<div id="subControlButtons">
|
||||
<div id="queuebutton" title="Load the next guest in queue" alt="Load the next guest in queue" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="session.nextQueue()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="advanced float" style="cursor: pointer;" >
|
||||
<div id="queuebutton" title="Load the next guest in queue" alt="Load the next guest in queue" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="nextQueue()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="advanced float" style="cursor: pointer;" >
|
||||
<i id="queuetoggle" class="toggleSize las la-stream my-float"></i>
|
||||
<div id="queueNotification"></div>
|
||||
</div>
|
||||
|
||||
872
lib.js
872
lib.js
@ -316,6 +316,156 @@ function submitDebugLog(msg){
|
||||
}
|
||||
|
||||
|
||||
var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); // used by main.js also
|
||||
var iPad = (navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform));
|
||||
|
||||
|
||||
function isAlphaNumeric(str) {
|
||||
var code, i, len;
|
||||
for (i = 0, len = str.length; i < len; i++) {
|
||||
code = str.charCodeAt(i);
|
||||
if (!(code > 47 && code < 58) && // numeric (0-9)
|
||||
!(code > 64 && code < 91) && // upper alpha (A-Z)
|
||||
!(code > 96 && code < 123)) { // lower alpha (a-z)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function convertStringToArrayBufferView(str){
|
||||
var bytes = new Uint8Array(str.length);
|
||||
for (var iii = 0; iii < str.length; iii++){
|
||||
bytes[iii] = str.charCodeAt(iii);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function toHexString(byteArray){
|
||||
return Array.prototype.map.call(byteArray, function(byte){
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
function toByteArray(hexString){
|
||||
var result = [];
|
||||
for (var i = 0; i < hexString.length; i += 2){
|
||||
result.push(parseInt(hexString.substr(i, 2), 16));
|
||||
}
|
||||
return new Uint8Array(result);
|
||||
}
|
||||
|
||||
function playAllVideos(){
|
||||
for (var i in session.rpcs){
|
||||
try{
|
||||
if (session.rpcs[i].videoElement){
|
||||
if (session.rpcs[i].videoElement.paused){
|
||||
session.rpcs[i].videoElement.play().then(_ => {
|
||||
log("playing");
|
||||
}).catch(warnlog);
|
||||
}
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
}
|
||||
|
||||
var videoElements = Array.from(document.querySelectorAll("video"));
|
||||
var audioElements = Array.from(document.querySelectorAll("audio"));
|
||||
var mediaStreamCounter = 0;
|
||||
|
||||
|
||||
function createMediaStream(){
|
||||
mediaStreamCounter+=1;
|
||||
return new MediaStream();
|
||||
}
|
||||
|
||||
function deleteOldMedia(){
|
||||
warnlog("CHECKING FOR OLD MEDIA");
|
||||
var i = videoElements.length;
|
||||
while (i--) {
|
||||
//if ((videoElements[i].id == "videosource") || (videoElements[i].id == "previewWebcam")){continue;} // exclude this one, for safety reasons. (Also, iOS safari blanks the video if streams are detached and moved between video elements)
|
||||
if (videoElements[i].isConnected === false){
|
||||
if ((videoElements[i].srcObject==null) || (videoElements[i].srcObject && videoElements[i].srcObject.active === false)){
|
||||
if (videoElements[i].dataset && videoElements[i].dataset.UUID){
|
||||
if (videoElements[i].dataset.UUID in session.rpcs){continue;} // still active, so lets not delete it.
|
||||
}
|
||||
videoElements[i].pause();
|
||||
videoElements[i].removeAttribute("id");
|
||||
videoElements[i].removeAttribute('src'); // empty source
|
||||
videoElements[i].load();
|
||||
videoElements[i].remove();
|
||||
videoElements[i] = null;
|
||||
videoElements.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
i = audioElements.length;
|
||||
while (i--) {
|
||||
if (audioElements[i].isConnected === false){
|
||||
if ((audioElements[i].srcObject==null) || (audioElements[i].srcObject && audioElements[i].srcObject.active === false)){
|
||||
if (audioElements[i].dataset && audioElements[i].dataset.UUID){
|
||||
if (audioElements[i].dataset.UUID in session.rpcs){continue;} // still active, so lets not delete it.
|
||||
}
|
||||
audioElements[i].pause();
|
||||
audioElements[i].id = null;
|
||||
audioElements[i].removeAttribute('src'); // empty source
|
||||
audioElements[i].load();
|
||||
audioElements[i].remove();
|
||||
audioElements[i] = null;
|
||||
audioElements.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createAudioElement(){
|
||||
try{
|
||||
deleteOldMedia();
|
||||
} catch(e){errorlog(e);}
|
||||
var a = document.createElement("audio");
|
||||
audioElements.push(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
function compare_deltas( a, b ) {
|
||||
var aa = a.delta || 0;
|
||||
var bb = b.delta || 0;
|
||||
if ( aa > bb ){
|
||||
return 1;
|
||||
}
|
||||
if ( aa < bb ){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function createVideoElement(){
|
||||
try{
|
||||
deleteOldMedia();
|
||||
} catch(e){errorlog(e);}
|
||||
var v = document.createElement("video");
|
||||
videoElements.push(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
function getTimezone(){
|
||||
if (session.tz!==false){
|
||||
return session.tz;
|
||||
}
|
||||
const stdTimezoneOffset = () => {
|
||||
var jan = new Date(0, 1);
|
||||
var jul = new Date(6, 1);
|
||||
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
|
||||
}
|
||||
var today = new Date();
|
||||
const isDstObserved = (today) => {
|
||||
return today.getTimezoneOffset() < stdTimezoneOffset();
|
||||
}
|
||||
if (isDstObserved(today)) {
|
||||
return today.getTimezoneOffset()+60;
|
||||
} else {
|
||||
return today.getTimezoneOffset();
|
||||
}
|
||||
}
|
||||
|
||||
function promptUser(eleId, UUID=null){
|
||||
if (session.beepToNotify){
|
||||
@ -1337,6 +1487,100 @@ function getStorage(cname) {
|
||||
return item.value;
|
||||
}
|
||||
|
||||
function play(streamid=null, UUID=false){ // play whatever is in the URL params; or filter by a streamID option
|
||||
log("play stream: "+session.view);
|
||||
if (session.view===""){
|
||||
log("Setting view to null disables all playback");
|
||||
} else if (session.view !== false){
|
||||
var viewlist = session.view.split(",");
|
||||
var played = false;
|
||||
for (var j in viewlist){
|
||||
if (viewlist[j]==""){
|
||||
played=true;
|
||||
// view={blank} implies do not play anything. Useful for setting a default bitrate I guess
|
||||
} else if (streamid===null){ // play what is in the view list ; not a group room probably
|
||||
session.watchStream(viewlist[j]);
|
||||
played=true;
|
||||
} else if (streamid === viewlist[j]){ // plays if the group room list matches the explicit list
|
||||
session.watchStream(viewlist[j]);
|
||||
played=true;
|
||||
}
|
||||
}
|
||||
if (!played){
|
||||
if (session.scene!==false){
|
||||
if (!session.permaid){
|
||||
if (!session.queue){ // I don't want to deal with queues.
|
||||
if (session.exclude===false || (!session.exclude.includes(streamid))){
|
||||
if (UUID){
|
||||
if (session.directorList.indexOf(UUID)>=0){
|
||||
warnlog("stream ID added to badStreamList: "+streamid);
|
||||
session.badStreamList.push(streamid);
|
||||
session.watchStream(streamid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (session.exclude !== false){
|
||||
if (session.exclude.includes(streamid)){
|
||||
// we don't play it at all. (if explicity listed as VIDEO, then OKay.)
|
||||
} else {
|
||||
session.watchStream(streamid); // I suppose we do play it.
|
||||
}
|
||||
} else if (streamid){
|
||||
session.watchStream(streamid);
|
||||
}
|
||||
}
|
||||
|
||||
function nextQueue(){
|
||||
if (!session.queue){return;}
|
||||
if (!session.director){return;}
|
||||
if (session.queueList.length==0){
|
||||
getById("queuebutton").classList.add("float2");
|
||||
getById("queuebutton").classList.add("red");
|
||||
getById("queuebutton").classList.remove("float");
|
||||
setTimeout(function(){
|
||||
getById("queuebutton").classList.add("float");
|
||||
getById("queuebutton").classList.remove("float2");
|
||||
getById("queuebutton").classList.remove("red");
|
||||
},50);
|
||||
return;
|
||||
}
|
||||
var nextStream = session.queueList.shift();
|
||||
|
||||
|
||||
getById("queuebutton").classList.add("float2");
|
||||
getById("queuebutton").classList.remove("float");
|
||||
setTimeout(function(){
|
||||
getById("queuebutton").classList.add("float");
|
||||
getById("queuebutton").classList.remove("float2");
|
||||
},200);
|
||||
|
||||
updateQueue();
|
||||
|
||||
session.watchStream(nextStream);
|
||||
log("next stream loading: "+nextStream);
|
||||
}
|
||||
|
||||
function updateQueue(){
|
||||
if (!session.queue){return;}
|
||||
if (!session.director){return;}
|
||||
if (session.queueList.length) {
|
||||
if (session.queueList.length>10){
|
||||
getById("queueNotification").innerHTML = "‼";
|
||||
} else {
|
||||
getById("queueNotification").innerHTML = session.queueList.length;
|
||||
}
|
||||
getById("queueNotification").classList.add("queueNotification");
|
||||
} else {
|
||||
getById("queueNotification").innerHTML = "";
|
||||
getById("queueNotification").classList.remove("queueNotification");
|
||||
}
|
||||
}
|
||||
|
||||
function setupIncomingScreenTracking(v, UUID){ // SCREEN element.
|
||||
|
||||
if (session.directorList.indexOf(UUID)>=0){
|
||||
@ -1635,7 +1879,7 @@ function setupIncomingScreenTracking(v, UUID){ // SCREEN element.
|
||||
|
||||
}
|
||||
|
||||
setTimeout(session.processStats, 1000, UUID);
|
||||
setTimeout(processStats, 1000, UUID);
|
||||
}
|
||||
|
||||
function setupIncomingVideoTracking(v, UUID){ // video element.
|
||||
@ -1914,7 +2158,7 @@ function setupIncomingVideoTracking(v, UUID){ // video element.
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(session.processStats, 1000, UUID);
|
||||
setTimeout(processStats, 1000, UUID);
|
||||
}
|
||||
|
||||
function mediaSourceUpdated(UUID, streamID, videoTrack){
|
||||
@ -1968,6 +2212,34 @@ function updateVolume(update=false){
|
||||
}
|
||||
}
|
||||
|
||||
var updateMixerTimer = null;
|
||||
var updateMixerActive = false;
|
||||
//var cleanupTimeout = null;
|
||||
function updateMixer(e=false){
|
||||
clearInterval(updateMixerTimer);
|
||||
if (updateMixerActive){
|
||||
if (session.mobile){
|
||||
updateMixerTimer = setTimeout(function(){updateMixer();},200);
|
||||
} else {
|
||||
updateMixerTimer = setTimeout(function(){updateMixer();},50);
|
||||
}
|
||||
return;
|
||||
}
|
||||
updateMixerActive=true;
|
||||
log("updating mixer");
|
||||
try{
|
||||
updateMixerRun(e);
|
||||
// clearInterval(cleanupTimeout);
|
||||
// cleanupTimeout = setTimeout(function(){deleteOldMedia();},60000);
|
||||
|
||||
} catch(e){}
|
||||
|
||||
if (session.mobile){
|
||||
setTimeout(function(){updateMixerActive=false;},500);
|
||||
} else {
|
||||
setTimeout(function(){updateMixerActive=false;},100);
|
||||
}
|
||||
}
|
||||
|
||||
function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a giant function that runs when there are changes to screensize, video track statuses, etc.
|
||||
try {
|
||||
@ -4990,6 +5262,16 @@ function drawFace() {
|
||||
}
|
||||
//////// END CANVAS EFFECTS ///////////////////
|
||||
|
||||
|
||||
function getNativeOutputResolution(){
|
||||
var tracks = session.videoElement.srcObject.getVideoTracks();
|
||||
if (tracks.length && tracks[0].getSettings){
|
||||
return tracks[0].getSettings();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function remoteStats(msg, UUID){
|
||||
if (session.director){
|
||||
var output = "";
|
||||
@ -5034,6 +5316,338 @@ function remoteStats(msg, UUID){
|
||||
}
|
||||
}
|
||||
|
||||
function processStats(UUID){
|
||||
|
||||
// for (pc in session.pcs){session.pcs[pc].getStats().then(function(stats) {stats.forEach(stat=>{if (stat.id.includes("RTCIce")){console.log(stat)}})})};
|
||||
|
||||
if (!session.rpcs || !(UUID in session.rpcs)){
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (session.rpcs[UUID].videoElement.paused){
|
||||
log("trying to play");
|
||||
session.rpcs[UUID].videoElement.play().then(_ => {
|
||||
log("playing");
|
||||
session.firstPlayTriggered=true;
|
||||
}).catch(warnlog);
|
||||
}
|
||||
} catch (e){};
|
||||
|
||||
if (session.rpcs[UUID].mc){
|
||||
processMeshcastStats(UUID);
|
||||
}
|
||||
|
||||
try {
|
||||
session.rpcs[UUID].getStats().then(function(stats){
|
||||
if (!(UUID in session.rpcs)){return;}
|
||||
|
||||
setTimeout(processStats, 3000, UUID);
|
||||
|
||||
if (!session.rpcs[UUID].stats['Peer-to-Peer Connection']){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'] = {};
|
||||
}
|
||||
|
||||
stats.forEach(stat=>{
|
||||
|
||||
if ((stat.type == "candidate-pair") && (stat.nominated==true)){
|
||||
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection']._local_ice_id = stat.localCandidateId;
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection']._remote_ice_id = stat.remoteCandidateId;
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].Round_Trip_Time_ms = stat.currentRoundTripTime*1000;
|
||||
|
||||
} else if ((stat.type=="track") && (stat.remoteSource==true)){
|
||||
|
||||
|
||||
if (stat.id in session.rpcs[UUID].stats){
|
||||
session.rpcs[UUID].stats[stat.id]._trackID = stat.trackIdentifier;
|
||||
session.rpcs[UUID].stats[stat.id].Buffer_Delay_in_ms = parseInt(1000*(parseFloat(stat.jitterBufferDelay) - session.rpcs[UUID].stats[stat.id]._jitter_delay)/(parseInt(stat.jitterBufferEmittedCount) - session.rpcs[UUID].stats[stat.id]._jitter_count)) || 0;
|
||||
session.rpcs[UUID].stats[stat.id]._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0;
|
||||
session.rpcs[UUID].stats[stat.id]._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0;
|
||||
if ("frameWidth" in stat){
|
||||
if ("frameHeight" in stat){
|
||||
session.rpcs[UUID].stats[stat.id].Resolution = stat.frameWidth+" x "+stat.frameHeight;
|
||||
session.rpcs[UUID].stats[stat.id]._frameWidth = stat.frameWidth;
|
||||
session.rpcs[UUID].stats[stat.id]._frameHeight = stat.frameHeight;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var media = {};
|
||||
media._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0;
|
||||
media._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0;
|
||||
media.Buffer_Delay_in_ms = 0;
|
||||
media._trackID = stat.trackIdentifier;
|
||||
session.rpcs[UUID].stats[stat.id] = media;
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.type=="remote-candidate"){
|
||||
|
||||
if (("_remote_ice_id" in session.rpcs[UUID].stats['Peer-to-Peer Connection']) && (session.rpcs[UUID].stats['Peer-to-Peer Connection']._remote_ice_id != stat.id )){return;} // not matched to nominated one
|
||||
|
||||
if ("candidateType" in stat){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].remote_candidateType = stat.candidateType;
|
||||
if (stat.candidateType === "relay"){
|
||||
if ("relayProtocol" in stat){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].remote_relayProtocol = stat.relayProtocol;
|
||||
}
|
||||
if ("ip" in stat){session.rpcs[UUID].stats['Peer-to-Peer Connection'].remote_relay_IP = stat.ip;}
|
||||
} else {
|
||||
try {
|
||||
delete session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_relayIP;
|
||||
delete session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_relayProtocol;
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ("networkType" in stat){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].remote_networkType = stat.networkType;
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.type=="local-candidate"){
|
||||
|
||||
if (("_local_ice_id" in session.rpcs[UUID].stats['Peer-to-Peer Connection']) && (session.rpcs[UUID].stats['Peer-to-Peer Connection']._local_ice_id != stat.id )){return;} // not matched to nominated one
|
||||
|
||||
if ("candidateType" in stat){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_candidateType = stat.candidateType;
|
||||
if (stat.candidateType === "relay"){
|
||||
if ("relayProtocol" in stat){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_relayProtocol = stat.relayProtocol;
|
||||
}
|
||||
if ("ip" in stat){session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_relayIP = stat.ip;}
|
||||
} else {
|
||||
try {
|
||||
delete session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_relayIP;
|
||||
delete session.rpcs[UUID].stats['Peer-to-Peer Connection'].local_relayProtocol;
|
||||
} catch(e){}
|
||||
}
|
||||
}
|
||||
|
||||
if ("networkType" in stat){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].remote_networkType = stat.networkType;
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.type == "transport"){
|
||||
if ("bytesReceived" in stat) {
|
||||
if ("_bytesReceived" in session.rpcs[UUID].stats['Peer-to-Peer Connection']){
|
||||
if (session.rpcs[UUID].stats['Peer-to-Peer Connection']._timestamp){
|
||||
if (stat.timestamp){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].total_recv_bitrate_kbps = parseInt(8*(stat.bytesReceived - session.rpcs[UUID].stats['Peer-to-Peer Connection']._bytesReceived)/(stat.timestamp - session.rpcs[UUID].stats['Peer-to-Peer Connection']._timestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection']._bytesReceived = stat.bytesReceived;
|
||||
}
|
||||
if ("timestamp" in stat) {
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection']._timestamp = stat.timestamp;
|
||||
if (!session.rpcs[UUID].stats['Peer-to-Peer Connection']._timestampStart){
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection']._timestampStart = stat.timestamp;
|
||||
} else {
|
||||
session.rpcs[UUID].stats['Peer-to-Peer Connection'].time_active_minutes = parseInt((stat.timestamp - session.rpcs[UUID].stats['Peer-to-Peer Connection']._timestampStart)/600)/100;
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((stat.type=="inbound-rtp") && ("trackId" in stat)){
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId] = session.rpcs[UUID].stats[stat.trackId] || {};
|
||||
session.rpcs[UUID].stats[stat.trackId].Bitrate_in_kbps = parseInt(8*(stat.bytesReceived - session.rpcs[UUID].stats[stat.trackId]._last_bytes)/( stat.timestamp - session.rpcs[UUID].stats[stat.trackId]._last_time));
|
||||
session.rpcs[UUID].stats[stat.trackId]._last_bytes = stat.bytesReceived || session.rpcs[UUID].stats[stat.trackId]._last_bytes;
|
||||
session.rpcs[UUID].stats[stat.trackId]._last_time = stat.timestamp || session.rpcs[UUID].stats[stat.trackId]._last_time;
|
||||
|
||||
|
||||
session.rpcs[UUID].stats._codecId = stat.codecId;
|
||||
session.rpcs[UUID].stats._codecIdTrackId = stat.trackId;
|
||||
|
||||
if (stat.mediaType=="video"){
|
||||
session.rpcs[UUID].stats[stat.trackId].type = "Video Track"
|
||||
session.rpcs[UUID].stats[stat.trackId]._type = "video";
|
||||
if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP8")){
|
||||
session.rpcs[UUID].stats[stat.trackId].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[stat.trackId].keyFramesRequested_pli) || 0;
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[stat.trackId].streamErrors_nackCount + session.rpcs[UUID].stats[stat.trackId].nackTrigger) || 0;
|
||||
|
||||
log("OBS PLI FIX MODE ON");
|
||||
if ((session.rpcs[UUID].stats[stat.trackId].pliDelta===0) && (session.rpcs[UUID].stats[stat.trackId].nackTrigger >= session.obsfix)){ // heavy packet loss with no pliCount?
|
||||
session.requestKeyframe(UUID);
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
log("TRYING KEYFRAME");
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].pliDelta>0){
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
}
|
||||
} else if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP9")){
|
||||
session.rpcs[UUID].stats[stat.trackId].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[stat.trackId].keyFramesRequested_pli) || 0;
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[stat.trackId].streamErrors_nackCount + session.rpcs[UUID].stats[stat.trackId].nackTrigger) || 0;
|
||||
|
||||
log("OBS PLI FIX MODE ON");
|
||||
if ((session.rpcs[UUID].stats[stat.trackId].pliDelta===0) && (session.rpcs[UUID].stats[stat.trackId].nackTrigger >= (session.obsfix*4) )){ // heavy packet loss with no pliCount? well, VP9 will trigger hopefully not as often.
|
||||
session.requestKeyframe(UUID);
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
log("TRYING KEYFRAME");
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].pliDelta>0){
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
}
|
||||
}
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId].keyFramesRequested_pli = stat.pliCount || 0;
|
||||
session.rpcs[UUID].stats[stat.trackId].streamErrors_nackCount = stat.nackCount || 0;
|
||||
|
||||
//warnlog(stat);
|
||||
|
||||
if ("framesPerSecond" in stat){
|
||||
session.rpcs[UUID].stats[stat.trackId].FPS = parseInt(stat.framesPerSecond);
|
||||
} else if (("framesDecoded" in stat) && (stat.timestamp)){
|
||||
|
||||
var lastFramesDecoded = 0;
|
||||
var lastTimestamp = 0;
|
||||
try{
|
||||
lastFramesDecoded = session.rpcs[UUID].stats[stat.trackId]._framesDecoded;
|
||||
lastTimestamp = session.rpcs[UUID].stats[stat.trackId]._timestamp;
|
||||
} catch(e){}
|
||||
session.rpcs[UUID].stats[stat.trackId].FPS = parseInt(10*(stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp))/10;
|
||||
|
||||
//session.rpcs[UUID].stats[stat.trackId].FPS = parseInt((stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp));
|
||||
session.rpcs[UUID].stats[stat.trackId]._framesDecoded = stat.framesDecoded;
|
||||
session.rpcs[UUID].stats[stat.trackId]._timestamp = stat.timestamp/1000;
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.mediaType=="audio"){
|
||||
//log("AUDIO LEVEL: "+stat.audioLevel);
|
||||
session.rpcs[UUID].stats[stat.trackId].type = "Audio Track";
|
||||
session.rpcs[UUID].stats[stat.trackId]._type = "audio";
|
||||
if ("audioLevel" in stat){
|
||||
session.rpcs[UUID].stats[stat.trackId].audio_level = parseInt(parseFloat(stat.audioLevel)*10000)/10000.0;
|
||||
}
|
||||
}
|
||||
|
||||
if ("packetsLost" in stat && "packetsReceived" in stat){
|
||||
|
||||
if (!("_packetsLost" in session.rpcs[UUID].stats[stat.trackId])){
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsLost = stat.packetsLost;
|
||||
}
|
||||
if (!("_packetsReceived" in session.rpcs[UUID].stats[stat.trackId])){
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsReceived = stat.packetsReceived;
|
||||
}
|
||||
|
||||
if (!("packetLoss_in_percentage" in session.rpcs[UUID].stats[stat.trackId])){
|
||||
session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage = 0;
|
||||
}
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage = session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage*0.35 + 0.65*((stat.packetsLost-session.rpcs[UUID].stats[stat.trackId]._packetsLost)*100.0)/((stat.packetsReceived-session.rpcs[UUID].stats[stat.trackId]._packetsReceived)+(stat.packetsLost-session.rpcs[UUID].stats[stat.trackId]._packetsLost)) || 0;
|
||||
|
||||
if (session.rpcs[UUID].signalMeter && (session.rpcs[UUID].stats[stat.trackId]._type==="video")){
|
||||
if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<0.01){
|
||||
if (session.rpcs[UUID].stats[stat.trackId].Bitrate_in_kbps==0){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 0;
|
||||
} else {
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 5;
|
||||
}
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<0.3){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 4;
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<1.0){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 3;
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<3.5){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 2;
|
||||
} else {
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 1;
|
||||
}
|
||||
}
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsReceived = stat.packetsReceived;
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsLost = stat.packetsLost;
|
||||
}
|
||||
|
||||
} else if (("_codecId" in session.rpcs[UUID].stats) && (stat.id == session.rpcs[UUID].stats._codecId)){
|
||||
|
||||
if ("mimeType" in stat){
|
||||
if (session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId]){
|
||||
session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType;
|
||||
} else {
|
||||
session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId] = {};
|
||||
session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType;
|
||||
}
|
||||
}
|
||||
if ("frameHeight" in stat){
|
||||
if ("frameWidth" in stat){
|
||||
session.rpcs[UUID].stats.Resolution = parseInt(stat.frameWidth)+" x "+parseInt(stat.frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
if (session.buffer!==false){
|
||||
playoutdelay(UUID);
|
||||
}
|
||||
setTimeout(function(){
|
||||
session.directorSpeakerMute();
|
||||
session.directorDisplayMute();
|
||||
},0);
|
||||
});
|
||||
} catch (e){errorlog(e);}
|
||||
};
|
||||
|
||||
function playoutdelay(UUID){ // applies a delay to all videos
|
||||
try {
|
||||
var target_buffer = session.buffer || 0;
|
||||
target_buffer = parseFloat(target_buffer);
|
||||
|
||||
if (session.buffer!==false){
|
||||
|
||||
// if buffer is set, then session.sync will be set; at least to 0.
|
||||
|
||||
var receivers = getReceivers2(UUID).reverse(); //session.rpcs[UUID].getReceivers().reverse();
|
||||
var video_delay = 0;
|
||||
receivers.forEach(function(receiver){
|
||||
try {
|
||||
for (var tid in session.rpcs[UUID].stats){
|
||||
|
||||
if ((typeof( session.rpcs[UUID].stats[tid])=="object") && ("_trackID" in session.rpcs[UUID].stats[tid]) && (session.rpcs[UUID].stats[tid]._trackID===receiver.track.id) && ("Buffer_Delay_in_ms" in session.rpcs[UUID].stats[tid])){
|
||||
|
||||
var sync_offset = 0.0;
|
||||
|
||||
if (session.rpcs[UUID].stats[tid]._sync_offset){
|
||||
sync_offset = session.rpcs[UUID].stats[tid]._sync_offset;
|
||||
} else {
|
||||
session.rpcs[UUID].stats[tid]._sync_offset = 0;
|
||||
}
|
||||
|
||||
sync_offset += target_buffer - session.rpcs[UUID].stats[tid].Buffer_Delay_in_ms; // target_Butt
|
||||
|
||||
if (sync_offset>target_buffer){
|
||||
sync_offset=target_buffer;
|
||||
}
|
||||
|
||||
if (session.rpcs[UUID].stats[tid]._type=="audio"){
|
||||
if (receiver.track.id in session.rpcs[UUID].delayNode){
|
||||
log("updating audio delay");
|
||||
var audio_delay = video_delay - session.rpcs[UUID].stats[tid].Buffer_Delay_in_ms + session.sync; // video is typically showing greater delay than video
|
||||
if (audio_delay<0){audio_delay=0;}
|
||||
log("audio_delay : "+audio_delay);
|
||||
log("audioCtx : "+ session.audioCtx.currentTime);
|
||||
session.rpcs[UUID].delayNode[receiver.track.id].delayTime.setValueAtTime(parseFloat(audio_delay/1000.0), session.audioCtx.currentTime+1);
|
||||
session.rpcs[UUID].stats[tid].Audio_Sync_Delay_ms = audio_delay;
|
||||
|
||||
}
|
||||
} else if (session.rpcs[UUID].stats[tid]._type=="video"){
|
||||
//log("THIS SHOULD BE BEFORE AUDIO - video track"+session.rpcs[UUID].stats[tid].type);
|
||||
video_delay = session.rpcs[UUID].stats[tid].Buffer_Delay_in_ms;
|
||||
if(sync_offset<0){sync_offset=0;}
|
||||
session.rpcs[UUID].stats[tid]._sync_offset = sync_offset;
|
||||
receiver.playoutDelayHint = parseFloat(sync_offset/1000); // only the video we are going to do the playout delay for; doesn't work well with audio.
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e){errorlog(e);}
|
||||
});
|
||||
}
|
||||
} catch (e){
|
||||
errorlog("device does not support playout delay");
|
||||
}
|
||||
};
|
||||
|
||||
function printViewStats(menu, UUID) { // Stats for viewing a remote video
|
||||
if (!session.rpcs[UUID]){
|
||||
@ -5189,6 +5803,248 @@ function printValues(obj) { // see: printViewStats
|
||||
return out;
|
||||
}
|
||||
|
||||
function processMeshcastStats(UUID){
|
||||
try {
|
||||
session.rpcs[UUID].mc.getStats().then(function(stats){
|
||||
if (!(UUID in session.rpcs)){return;}
|
||||
|
||||
if (!session.rpcs[UUID].stats['Meshcast Connection']){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'] = {};
|
||||
}
|
||||
|
||||
stats.forEach(stat=>{
|
||||
|
||||
if ((stat.type == "candidate-pair") && (stat.nominated==true)){
|
||||
|
||||
session.rpcs[UUID].stats['Meshcast Connection']._local_ice_id = stat.localCandidateId;
|
||||
session.rpcs[UUID].stats['Meshcast Connection']._remote_ice_id = stat.remoteCandidateId;
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].Round_Trip_Time_ms = stat.currentRoundTripTime*1000;
|
||||
|
||||
} else if ((stat.type=="track") && (stat.remoteSource==true)){
|
||||
|
||||
|
||||
if (stat.id in session.rpcs[UUID].stats){
|
||||
session.rpcs[UUID].stats[stat.id]._trackID = stat.trackIdentifier;
|
||||
session.rpcs[UUID].stats[stat.id].Buffer_Delay_in_ms = parseInt(1000*(parseFloat(stat.jitterBufferDelay) - session.rpcs[UUID].stats[stat.id]._jitter_delay)/(parseInt(stat.jitterBufferEmittedCount) - session.rpcs[UUID].stats[stat.id]._jitter_count)) || 0;
|
||||
session.rpcs[UUID].stats[stat.id]._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0;
|
||||
session.rpcs[UUID].stats[stat.id]._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0;
|
||||
if ("frameWidth" in stat){
|
||||
if ("frameHeight" in stat){
|
||||
session.rpcs[UUID].stats[stat.id].Resolution = stat.frameWidth+" x "+stat.frameHeight;
|
||||
session.rpcs[UUID].stats[stat.id]._frameWidth = stat.frameWidth;
|
||||
session.rpcs[UUID].stats[stat.id]._frameHeight = stat.frameHeight;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var media = {};
|
||||
media._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0;
|
||||
media._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0;
|
||||
media.Buffer_Delay_in_ms = 0;
|
||||
media._trackID = stat.trackIdentifier;
|
||||
session.rpcs[UUID].stats[stat.id] = media;
|
||||
}
|
||||
|
||||
} else if (stat.type=="remote-candidate"){
|
||||
|
||||
if (("_remote_ice_id" in session.rpcs[UUID].stats['Meshcast Connection']) && (session.rpcs[UUID].stats['Meshcast Connection']._remote_ice_id != stat.id )){return;} // not matched to nominated one
|
||||
|
||||
if ("candidateType" in stat){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].remote_candidateType = stat.candidateType;
|
||||
if (stat.candidateType === "relay"){
|
||||
if ("relayProtocol" in stat){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].remote_relayProtocol = stat.relayProtocol;
|
||||
}
|
||||
if ("ip" in stat){session.rpcs[UUID].stats['Meshcast Connection'].remote_relay_IP = stat.ip;}
|
||||
} else {
|
||||
try {
|
||||
delete session.rpcs[UUID].stats['Meshcast Connection'].local_relayIP;
|
||||
delete session.rpcs[UUID].stats['Meshcast Connection'].local_relayProtocol;
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ("networkType" in stat){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].remote_networkType = stat.networkType;
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.type=="local-candidate"){
|
||||
|
||||
if (("_local_ice_id" in session.rpcs[UUID].stats['Meshcast Connection']) && (session.rpcs[UUID].stats['Meshcast Connection']._local_ice_id != stat.id )){return;} // not matched to nominated one
|
||||
|
||||
if ("candidateType" in stat){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].local_candidateType = stat.candidateType;
|
||||
if (stat.candidateType === "relay"){
|
||||
if ("relayProtocol" in stat){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].local_relayProtocol = stat.relayProtocol;
|
||||
}
|
||||
if ("ip" in stat){session.rpcs[UUID].stats['Meshcast Connection'].local_relayIP = stat.ip;}
|
||||
} else {
|
||||
try {
|
||||
delete session.rpcs[UUID].stats['Meshcast Connection'].local_relayIP;
|
||||
delete session.rpcs[UUID].stats['Meshcast Connection'].local_relayProtocol;
|
||||
} catch(e){}
|
||||
}
|
||||
}
|
||||
|
||||
if ("networkType" in stat){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].remote_networkType = stat.networkType;
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.type == "transport"){
|
||||
if ("bytesReceived" in stat) {
|
||||
if ("_bytesReceived" in session.rpcs[UUID].stats['Meshcast Connection']){
|
||||
if (session.rpcs[UUID].stats['Meshcast Connection']._timestamp){
|
||||
if (stat.timestamp){
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].total_recv_bitrate_kbps = parseInt(8*(stat.bytesReceived - session.rpcs[UUID].stats['Meshcast Connection']._bytesReceived)/(stat.timestamp - session.rpcs[UUID].stats['Meshcast Connection']._timestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
session.rpcs[UUID].stats['Meshcast Connection']._bytesReceived = stat.bytesReceived;
|
||||
}
|
||||
if ("timestamp" in stat) {
|
||||
session.rpcs[UUID].stats['Meshcast Connection']._timestamp = stat.timestamp;
|
||||
if (!session.rpcs[UUID].stats['Meshcast Connection']._timestampStart){
|
||||
session.rpcs[UUID].stats['Meshcast Connection']._timestampStart = stat.timestamp;
|
||||
} else {
|
||||
session.rpcs[UUID].stats['Meshcast Connection'].time_active_minutes = parseInt((stat.timestamp - session.rpcs[UUID].stats['Meshcast Connection']._timestampStart)/600)/100;
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((stat.type=="inbound-rtp") && ("trackId" in stat)){
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId] = session.rpcs[UUID].stats[stat.trackId] || {};
|
||||
session.rpcs[UUID].stats[stat.trackId].Bitrate_in_kbps = parseInt(8*(stat.bytesReceived - session.rpcs[UUID].stats[stat.trackId]._last_bytes)/( stat.timestamp - session.rpcs[UUID].stats[stat.trackId]._last_time));
|
||||
session.rpcs[UUID].stats[stat.trackId]._last_bytes = stat.bytesReceived || session.rpcs[UUID].stats[stat.trackId]._last_bytes;
|
||||
session.rpcs[UUID].stats[stat.trackId]._last_time = stat.timestamp || session.rpcs[UUID].stats[stat.trackId]._last_time;
|
||||
|
||||
|
||||
session.rpcs[UUID].stats._codecId = stat.codecId;
|
||||
session.rpcs[UUID].stats._codecIdTrackId = stat.trackId;
|
||||
|
||||
if (stat.mediaType=="video"){
|
||||
session.rpcs[UUID].stats[stat.trackId].type = "Video Track"
|
||||
session.rpcs[UUID].stats[stat.trackId]._type = "video";
|
||||
if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP8")){
|
||||
session.rpcs[UUID].stats[stat.trackId].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[stat.trackId].keyFramesRequested_pli) || 0;
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[stat.trackId].streamErrors_nackCount + session.rpcs[UUID].stats[stat.trackId].nackTrigger) || 0;
|
||||
|
||||
log("OBS PLI FIX MODE ON");
|
||||
if ((session.rpcs[UUID].stats[stat.trackId].pliDelta===0) && (session.rpcs[UUID].stats[stat.trackId].nackTrigger >= session.obsfix)){ // heavy packet loss with no pliCount?
|
||||
session.requestKeyframe(UUID);
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
log("TRYING KEYFRAME");
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].pliDelta>0){
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
}
|
||||
} else if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP9")){
|
||||
session.rpcs[UUID].stats[stat.trackId].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[stat.trackId].keyFramesRequested_pli) || 0;
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[stat.trackId].streamErrors_nackCount + session.rpcs[UUID].stats[stat.trackId].nackTrigger) || 0;
|
||||
|
||||
log("OBS PLI FIX MODE ON");
|
||||
if ((session.rpcs[UUID].stats[stat.trackId].pliDelta===0) && (session.rpcs[UUID].stats[stat.trackId].nackTrigger >= (session.obsfix*4) )){ // heavy packet loss with no pliCount? well, VP9 will trigger hopefully not as often.
|
||||
session.requestKeyframe(UUID);
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
log("TRYING KEYFRAME");
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].pliDelta>0){
|
||||
session.rpcs[UUID].stats[stat.trackId].nackTrigger = 0;
|
||||
}
|
||||
}
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId].keyFramesRequested_pli = stat.pliCount || 0;
|
||||
session.rpcs[UUID].stats[stat.trackId].streamErrors_nackCount = stat.nackCount || 0;
|
||||
|
||||
//warnlog(stat);
|
||||
|
||||
if ("framesPerSecond" in stat){
|
||||
session.rpcs[UUID].stats[stat.trackId].FPS = parseInt(stat.framesPerSecond);
|
||||
} else if (("framesDecoded" in stat) && (stat.timestamp)){
|
||||
|
||||
var lastFramesDecoded = 0;
|
||||
var lastTimestamp = 0;
|
||||
try{
|
||||
lastFramesDecoded = session.rpcs[UUID].stats[stat.trackId]._framesDecoded;
|
||||
lastTimestamp = session.rpcs[UUID].stats[stat.trackId]._timestamp;
|
||||
} catch(e){}
|
||||
session.rpcs[UUID].stats[stat.trackId].FPS = parseInt(10*(stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp))/10;
|
||||
|
||||
//session.rpcs[UUID].stats[stat.trackId].FPS = parseInt((stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp));
|
||||
session.rpcs[UUID].stats[stat.trackId]._framesDecoded = stat.framesDecoded;
|
||||
session.rpcs[UUID].stats[stat.trackId]._timestamp = stat.timestamp/1000;
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else if (stat.mediaType=="audio"){
|
||||
//log("AUDIO LEVEL: "+stat.audioLevel);
|
||||
session.rpcs[UUID].stats[stat.trackId].type = "Audio Track";
|
||||
session.rpcs[UUID].stats[stat.trackId]._type = "audio";
|
||||
if ("audioLevel" in stat){
|
||||
session.rpcs[UUID].stats[stat.trackId].audio_level = parseInt(parseFloat(stat.audioLevel)*10000)/10000.0;
|
||||
}
|
||||
}
|
||||
|
||||
if ("packetsLost" in stat && "packetsReceived" in stat){
|
||||
|
||||
if (!("_packetsLost" in session.rpcs[UUID].stats[stat.trackId])){
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsLost = stat.packetsLost;
|
||||
}
|
||||
if (!("_packetsReceived" in session.rpcs[UUID].stats[stat.trackId])){
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsReceived = stat.packetsReceived;
|
||||
}
|
||||
|
||||
if (!("packetLoss_in_percentage" in session.rpcs[UUID].stats[stat.trackId])){
|
||||
session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage = 0;
|
||||
}
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage = session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage*0.35 + 0.65*((stat.packetsLost-session.rpcs[UUID].stats[stat.trackId]._packetsLost)*100.0)/((stat.packetsReceived-session.rpcs[UUID].stats[stat.trackId]._packetsReceived)+(stat.packetsLost-session.rpcs[UUID].stats[stat.trackId]._packetsLost)) || 0;
|
||||
|
||||
if (session.rpcs[UUID].signalMeter && (session.rpcs[UUID].stats[stat.trackId]._type==="video")){
|
||||
if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<0.01){
|
||||
if (session.rpcs[UUID].stats[stat.trackId].Bitrate_in_kbps==0){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 0;
|
||||
} else {
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 5;
|
||||
}
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<0.3){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 4;
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<1.0){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 3;
|
||||
} else if (session.rpcs[UUID].stats[stat.trackId].packetLoss_in_percentage<3.5){
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 2;
|
||||
} else {
|
||||
session.rpcs[UUID].signalMeter.dataset.level = 1;
|
||||
}
|
||||
}
|
||||
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsReceived = stat.packetsReceived;
|
||||
session.rpcs[UUID].stats[stat.trackId]._packetsLost = stat.packetsLost;
|
||||
}
|
||||
|
||||
} else if (("_codecId" in session.rpcs[UUID].stats) && (stat.id == session.rpcs[UUID].stats._codecId)){
|
||||
|
||||
if ("mimeType" in stat){
|
||||
if (session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId]){
|
||||
session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType;
|
||||
} else {
|
||||
session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId] = {};
|
||||
session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType;
|
||||
}
|
||||
}
|
||||
if ("frameHeight" in stat){
|
||||
if ("frameWidth" in stat){
|
||||
session.rpcs[UUID].stats.Resolution = parseInt(stat.frameWidth)+" x "+parseInt(stat.frameHeight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (e){errorlog(e);}
|
||||
}
|
||||
|
||||
|
||||
function printMyStats(menu) { // see: setupStatsMenu
|
||||
var scrollLeft = getById("menuStatsBox").scrollLeft;
|
||||
@ -18932,6 +19788,18 @@ async function recordVideo(target, event, videoKbps = false) { // event.currentT
|
||||
|
||||
var UUID = target.dataset.UUID;
|
||||
var video = session.rpcs[UUID].videoElement;
|
||||
|
||||
if (video.stopWriter){
|
||||
video.stopWriter();
|
||||
updateLocalRecordButton(UUID, -1);
|
||||
return;
|
||||
} else if (video.startWriter){
|
||||
await video.startWriter();
|
||||
updateLocalRecordButton(UUID, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var audioKbps = false;
|
||||
|
||||
if (event === null) {
|
||||
|
||||
22
main.css
22
main.css
@ -1034,7 +1034,27 @@ input[type='radio'] { cursor:pointer; }
|
||||
margin: 44vh auto;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
#retryimage{
|
||||
display: block;
|
||||
margin: 25vh auto;
|
||||
max-width: 50%;
|
||||
max-height: 50%;
|
||||
animation: fadeIn 2s;
|
||||
}
|
||||
#retrymessage{
|
||||
display: block;
|
||||
margin: 80vh auto;
|
||||
animation: fadeIn 2s;
|
||||
color: white;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
float: left;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
@keyframes spin-animation {
|
||||
0% {
|
||||
|
||||
85
main.js
85
main.js
@ -717,6 +717,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (urlParams.has('preloadbitrate')) {
|
||||
session.preloadbitrate = parseInt(urlParams.get('preloadbitrate')) || 0; // 1000
|
||||
}
|
||||
|
||||
if (urlParams.has('rampuptime')) {
|
||||
session.rampUpTime = parseInt(urlParams.get('rampuptime')) || 10000;
|
||||
}
|
||||
|
||||
if (urlParams.has('scenetype') || urlParams.has('type')) {
|
||||
session.sceneType = parseInt(urlParams.get('scenetype')) || parseInt(urlParams.get('type')) || false;
|
||||
@ -1368,7 +1372,26 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers?
|
||||
session.audioMeterGuest = false;
|
||||
session.audioEffects = false;
|
||||
session.obsfix = 15; // can be manually set via URL. ; VP8=15, VP9=30. (previous was 20.)
|
||||
if (window.obsstudio.pluginVersion){
|
||||
if (navigator.userAgent.indexOf('Mac OS X') !== -1){ // if mac, no fix
|
||||
//session.obsfix = false;
|
||||
} else if (window.obsstudio.pluginVersion=="2.17.4"){ // if obs v27.2 beta, no fix
|
||||
//session.obsfix = false;
|
||||
} else {
|
||||
var ver = window.obsstudio.pluginVersion.split(".");
|
||||
if (ver.length >= 2){
|
||||
if (parseInt(ver[0])<=2){
|
||||
if (parseInt(ver[0])==2){
|
||||
if (parseInt(ver[1])<=16){
|
||||
session.obsfix = 15;
|
||||
}
|
||||
} else {
|
||||
session.obsfix = 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
log("OBS VERSION:" + window.obsstudio.pluginVersion);
|
||||
log("macOS: " + navigator.userAgent.indexOf('Mac OS X') != -1);
|
||||
@ -1493,9 +1516,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
} catch(e){errorlog(e);}
|
||||
|
||||
if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has('vd') || urlParams.has('device') || urlParams.has('d')) {
|
||||
if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has('vd') || urlParams.has('device') || urlParams.has('d') || urlParams.has('vdo')) {
|
||||
|
||||
session.videoDevice = urlParams.get("videodevice") || urlParams.get("vdevice") || urlParams.get("vd") || urlParams.get("device") || urlParams.get("d");
|
||||
session.videoDevice = urlParams.get("videodevice") || urlParams.get("vdevice") || urlParams.get("vd") || urlParams.get("device") || urlParams.get("d") || urlParams.get("vdo");
|
||||
|
||||
if (session.videoDevice === null) {
|
||||
session.videoDevice = "1";
|
||||
@ -1528,7 +1551,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
// whatever the user entered I guess, santitized.
|
||||
session.videoDevice = session.videoDevice.replace(/[\W]+/g, "_").toLowerCase();
|
||||
}
|
||||
getById("videoMenu").style.display = "none";
|
||||
|
||||
if (!urlParams.has('vdo')){
|
||||
getById("videoMenu").style.display = "none";
|
||||
}
|
||||
log("session.videoDevice:" + session.videoDevice);
|
||||
}
|
||||
|
||||
@ -1741,6 +1767,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.sensorData = parseInt(session.sensorData);
|
||||
}
|
||||
|
||||
|
||||
if (urlParams.has('ptime')) {
|
||||
session.ptime = parseInt(urlParams.get('ptime')) || 20;
|
||||
if (session.ptime<10){
|
||||
session.ptime = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('minptime')) {
|
||||
session.minptime = parseInt(urlParams.get('minptime')) || 10;
|
||||
if (session.minptime < 10) {
|
||||
@ -1761,16 +1795,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('ptime')) {
|
||||
session.ptime = parseInt(urlParams.get('ptime')) || 20;
|
||||
if (session.minptime < 10) {
|
||||
session.ptime = 10;
|
||||
}
|
||||
if (session.minptime > 300) {
|
||||
session.ptime = 300;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (urlParams.has('codec')) {
|
||||
log("CODEC CHANGED");
|
||||
@ -2954,23 +2978,38 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
getById("mainmenu").innerHTML = '';
|
||||
getById("mainmenu").classList.remove("row");
|
||||
|
||||
var timeout = 5000;
|
||||
if (urlParams.has('waittimeout')){
|
||||
timeout = parseInt(urlParams.get('waittimeout')) || 0;
|
||||
}
|
||||
setTimeout(function() {
|
||||
try {
|
||||
if ((session.view) && (!(session.cleanOutput))) {
|
||||
if ((session.view)) {
|
||||
if (document.getElementById("mainmenu")) {
|
||||
getById("mainmenu").innerHTML += '<div class="retry-spinner" id="retrySpinner"></div>';
|
||||
retrySpinner.title = miscTranslations["waiting-for-the-stream"]
|
||||
retrySpinner.onclick = function(){
|
||||
updateURL("cleanoutput");
|
||||
location.reload();
|
||||
if (urlParams.has('waitimage')){
|
||||
getById("mainmenu").innerHTML += '<img id="retryimage"/>';
|
||||
getById("retryimage").src = decodeURIComponent(urlParams.get('waitimage'));
|
||||
getById("retryimage").onerror = function(){this.style.display='none';};
|
||||
|
||||
} else if (!(session.cleanOutput)){
|
||||
getById("mainmenu").innerHTML += '<div class="retry-spinner" id="retrySpinner"></div>';
|
||||
getById("retrySpinner").onclick = function(){
|
||||
updateURL("cleanoutput");
|
||||
location.reload();
|
||||
}
|
||||
getById("retrySpinner").title = miscTranslations["waiting-for-the-stream"]
|
||||
}
|
||||
if (urlParams.has('waitmessage')){
|
||||
getById("mainmenu").innerHTML += '<div id="retrymessage"></div>';
|
||||
getById("retrymessage").innerText = urlParams.get('waitmessage');
|
||||
getById("retrySpinner").title = urlParams.get('waitmessage');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
errorlog("Error handling QR Code failure");
|
||||
errorlog(e);
|
||||
}
|
||||
}, 5000);
|
||||
}, timeout);
|
||||
|
||||
log("auto playing");
|
||||
var SafariVer = safariVersion();
|
||||
@ -3267,7 +3306,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
warnlog(e.data.getDeviceList);
|
||||
enumerateDevices().then(function(deviceInfos) {
|
||||
parent.postMessage({
|
||||
"deviceList": deviceInfos
|
||||
"deviceList": JSON.parse(JSON.stringify(deviceInfos))
|
||||
}, "*");
|
||||
});
|
||||
}
|
||||
|
||||
496
mixer.html
496
mixer.html
@ -7,7 +7,7 @@
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
background-color: #0000;
|
||||
background-color: #c5c2c2;
|
||||
}
|
||||
iframe {
|
||||
border:0;
|
||||
@ -89,452 +89,6 @@
|
||||
padding: 10px 10px 0 15px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
100% {
|
||||
transform: translate3d(0, 0, 1px) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.background {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #bfbfbf;
|
||||
overflow: hidden;
|
||||
z-index:-1;
|
||||
}
|
||||
|
||||
.background span {
|
||||
width: 50vmin;
|
||||
height: 50vmin;
|
||||
border-radius: 50vmin;
|
||||
backface-visibility: hidden;
|
||||
position: absolute;
|
||||
animation: move;
|
||||
animation-duration: 7;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
|
||||
.background span:nth-child(0) {
|
||||
color: #a9a0bb;
|
||||
top: 26%;
|
||||
left: 54%;
|
||||
animation-duration: 133s;
|
||||
animation-delay: -143s;
|
||||
transform-origin: 17vw 23vh;
|
||||
box-shadow: 100vmin 0 13.032236536031524vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(1) {
|
||||
color: #888aa0;
|
||||
top: 32%;
|
||||
left: 60%;
|
||||
animation-duration: 98s;
|
||||
animation-delay: -19s;
|
||||
transform-origin: -9vw -17vh;
|
||||
box-shadow: -100vmin 0 13.361880379427102vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(2) {
|
||||
color: #a9a0bb;
|
||||
top: 76%;
|
||||
left: 30%;
|
||||
animation-duration: 27s;
|
||||
animation-delay: -382s;
|
||||
transform-origin: -21vw -12vh;
|
||||
box-shadow: -100vmin 0 13.29654068929897vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(3) {
|
||||
color: #a9a0bb;
|
||||
top: 13%;
|
||||
left: 3%;
|
||||
animation-duration: 89s;
|
||||
animation-delay: -71s;
|
||||
transform-origin: -5vw -23vh;
|
||||
box-shadow: -100vmin 0 13.269376230706396vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(4) {
|
||||
color: #888aa0;
|
||||
top: 98%;
|
||||
left: 83%;
|
||||
animation-duration: 61s;
|
||||
animation-delay: -57s;
|
||||
transform-origin: 19vw -16vh;
|
||||
box-shadow: 100vmin 0 13.465063933009704vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(5) {
|
||||
color: #4b6477;
|
||||
top: 73%;
|
||||
left: 100%;
|
||||
animation-duration: 312s;
|
||||
animation-delay: -390s;
|
||||
transform-origin: 23vw -6vh;
|
||||
box-shadow: -100vmin 0 12.719463296930117vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(6) {
|
||||
color: #888aa0;
|
||||
top: 6%;
|
||||
left: 84%;
|
||||
animation-duration: 176s;
|
||||
animation-delay: -255s;
|
||||
transform-origin: 16vw 3vh;
|
||||
box-shadow: -100vmin 0 13.358884039462355vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(7) {
|
||||
color: #888aa0;
|
||||
top: 99%;
|
||||
left: 9%;
|
||||
animation-duration: 221s;
|
||||
animation-delay: -385s;
|
||||
transform-origin: -16vw -9vh;
|
||||
box-shadow: 100vmin 0 13.435082385169103vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(8) {
|
||||
color: #a9a0bb;
|
||||
top: 8%;
|
||||
left: 92%;
|
||||
animation-duration: 142s;
|
||||
animation-delay: -99s;
|
||||
transform-origin: -1vw -14vh;
|
||||
box-shadow: -100vmin 0 13.456440775816723vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(9) {
|
||||
color: #a9a0bb;
|
||||
top: 80%;
|
||||
left: 70%;
|
||||
animation-duration: 99s;
|
||||
animation-delay: -124s;
|
||||
transform-origin: 21vw -10vh;
|
||||
box-shadow: 100vmin 0 13.007405893839119vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(10) {
|
||||
color: #888aa0;
|
||||
top: 42%;
|
||||
left: 2%;
|
||||
animation-duration: 70s;
|
||||
animation-delay: -341s;
|
||||
transform-origin: -15vw -23vh;
|
||||
box-shadow: 100vmin 0 13.005431373357645vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(11) {
|
||||
color: #a9a0bb;
|
||||
top: 28%;
|
||||
left: 2%;
|
||||
animation-duration: 244s;
|
||||
animation-delay: -369s;
|
||||
transform-origin: -17vw -16vh;
|
||||
box-shadow: 100vmin 0 13.351086475613746vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(12) {
|
||||
color: #4b6477;
|
||||
top: 80%;
|
||||
left: 50%;
|
||||
animation-duration: 117s;
|
||||
animation-delay: -399s;
|
||||
transform-origin: 24vw 8vh;
|
||||
box-shadow: 100vmin 0 12.979558964166843vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(13) {
|
||||
color: #888aa0;
|
||||
top: 74%;
|
||||
left: 65%;
|
||||
animation-duration: 283s;
|
||||
animation-delay: -50s;
|
||||
transform-origin: 13vw 14vh;
|
||||
box-shadow: 100vmin 0 13.282742227479792vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(14) {
|
||||
color: #888aa0;
|
||||
top: 64%;
|
||||
left: 61%;
|
||||
animation-duration: 194s;
|
||||
animation-delay: -278s;
|
||||
transform-origin: -6vw 4vh;
|
||||
box-shadow: -100vmin 0 13.333280580396286vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(15) {
|
||||
color: #a9a0bb;
|
||||
top: 11%;
|
||||
left: 86%;
|
||||
animation-duration: 403s;
|
||||
animation-delay: -329s;
|
||||
transform-origin: 3vw 0vh;
|
||||
box-shadow: -100vmin 0 13.1162690433691vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(16) {
|
||||
color: #4b6477;
|
||||
top: 14%;
|
||||
left: 59%;
|
||||
animation-duration: 100s;
|
||||
animation-delay: -15s;
|
||||
transform-origin: -23vw 21vh;
|
||||
box-shadow: -100vmin 0 13.30508866110655vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(17) {
|
||||
color: #888aa0;
|
||||
top: 68%;
|
||||
left: 45%;
|
||||
animation-duration: 394s;
|
||||
animation-delay: -137s;
|
||||
transform-origin: 18vw -20vh;
|
||||
box-shadow: -100vmin 0 12.809008306332636vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(18) {
|
||||
color: #a9a0bb;
|
||||
top: 7%;
|
||||
left: 33%;
|
||||
animation-duration: 132s;
|
||||
animation-delay: -346s;
|
||||
transform-origin: -22vw -17vh;
|
||||
box-shadow: -100vmin 0 12.940258664941906vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(19) {
|
||||
color: #a9a0bb;
|
||||
top: 24%;
|
||||
left: 65%;
|
||||
animation-duration: 49s;
|
||||
animation-delay: -44s;
|
||||
transform-origin: 0vw -24vh;
|
||||
box-shadow: -100vmin 0 12.733081490401828vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(20) {
|
||||
color: #888aa0;
|
||||
top: 98%;
|
||||
left: 49%;
|
||||
animation-duration: 204s;
|
||||
animation-delay: -160s;
|
||||
transform-origin: -1vw 3vh;
|
||||
box-shadow: -100vmin 0 13.05844646387434vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(21) {
|
||||
color: #a9a0bb;
|
||||
top: 2%;
|
||||
left: 29%;
|
||||
animation-duration: 294s;
|
||||
animation-delay: -272s;
|
||||
transform-origin: 13vw -18vh;
|
||||
box-shadow: -100vmin 0 12.626917095797205vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(22) {
|
||||
color: #a9a0bb;
|
||||
top: 73%;
|
||||
left: 32%;
|
||||
animation-duration: 424s;
|
||||
animation-delay: -59s;
|
||||
transform-origin: 19vw 3vh;
|
||||
box-shadow: 100vmin 0 13.092155905631065vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(23) {
|
||||
color: #888aa0;
|
||||
top: 45%;
|
||||
left: 93%;
|
||||
animation-duration: 277s;
|
||||
animation-delay: -215s;
|
||||
transform-origin: 0vw -14vh;
|
||||
box-shadow: 100vmin 0 12.768802405913084vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(24) {
|
||||
color: #4b6477;
|
||||
top: 35%;
|
||||
left: 40%;
|
||||
animation-duration: 375s;
|
||||
animation-delay: -205s;
|
||||
transform-origin: -16vw -21vh;
|
||||
box-shadow: -100vmin 0 12.845025825087218vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(25) {
|
||||
color: #a9a0bb;
|
||||
top: 82%;
|
||||
left: 77%;
|
||||
animation-duration: 211s;
|
||||
animation-delay: -150s;
|
||||
transform-origin: 15vw -14vh;
|
||||
box-shadow: 100vmin 0 12.825514275205975vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(26) {
|
||||
color: #4b6477;
|
||||
top: 99%;
|
||||
left: 95%;
|
||||
animation-duration: 342s;
|
||||
animation-delay: -118s;
|
||||
transform-origin: -16vw -10vh;
|
||||
box-shadow: 100vmin 0 13.09952938429165vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(27) {
|
||||
color: #a9a0bb;
|
||||
top: 24%;
|
||||
left: 43%;
|
||||
animation-duration: 191s;
|
||||
animation-delay: -249s;
|
||||
transform-origin: 10vw 25vh;
|
||||
box-shadow: -100vmin 0 12.948079018854608vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(28) {
|
||||
color: #888aa0;
|
||||
top: 35%;
|
||||
left: 83%;
|
||||
animation-duration: 38s;
|
||||
animation-delay: -5s;
|
||||
transform-origin: -15vw 21vh;
|
||||
box-shadow: -100vmin 0 12.884993041770123vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(29) {
|
||||
color: #4b6477;
|
||||
top: 50%;
|
||||
left: 19%;
|
||||
animation-duration: 83s;
|
||||
animation-delay: -367s;
|
||||
transform-origin: -17vw 24vh;
|
||||
box-shadow: 100vmin 0 12.943907699048086vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(30) {
|
||||
color: #4b6477;
|
||||
top: 94%;
|
||||
left: 43%;
|
||||
animation-duration: 139s;
|
||||
animation-delay: -151s;
|
||||
transform-origin: 15vw 13vh;
|
||||
box-shadow: -100vmin 0 13.188888046545854vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(31) {
|
||||
color: #4b6477;
|
||||
top: 1%;
|
||||
left: 30%;
|
||||
animation-duration: 276s;
|
||||
animation-delay: -118s;
|
||||
transform-origin: 20vw -13vh;
|
||||
box-shadow: 100vmin 0 12.67400347628477vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(32) {
|
||||
color: #a9a0bb;
|
||||
top: 62%;
|
||||
left: 91%;
|
||||
animation-duration: 179s;
|
||||
animation-delay: -95s;
|
||||
transform-origin: 21vw 0vh;
|
||||
box-shadow: 100vmin 0 13.078495151792405vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(33) {
|
||||
color: #a9a0bb;
|
||||
top: 62%;
|
||||
left: 24%;
|
||||
animation-duration: 176s;
|
||||
animation-delay: -417s;
|
||||
transform-origin: 11vw 1vh;
|
||||
box-shadow: -100vmin 0 13.180095388225237vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(34) {
|
||||
color: #4b6477;
|
||||
top: 44%;
|
||||
left: 7%;
|
||||
animation-duration: 15s;
|
||||
animation-delay: -23s;
|
||||
transform-origin: -5vw 24vh;
|
||||
box-shadow: -100vmin 0 12.99638150831451vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(35) {
|
||||
color: #888aa0;
|
||||
top: 41%;
|
||||
left: 18%;
|
||||
animation-duration: 52s;
|
||||
animation-delay: -66s;
|
||||
transform-origin: 10vw 18vh;
|
||||
box-shadow: 100vmin 0 12.881618623995024vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(36) {
|
||||
color: #888aa0;
|
||||
top: 32%;
|
||||
left: 75%;
|
||||
animation-duration: 377s;
|
||||
animation-delay: -252s;
|
||||
transform-origin: 19vw -21vh;
|
||||
box-shadow: -100vmin 0 13.235523992130878vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(37) {
|
||||
color: #4b6477;
|
||||
top: 26%;
|
||||
left: 96%;
|
||||
animation-duration: 352s;
|
||||
animation-delay: -359s;
|
||||
transform-origin: -22vw -3vh;
|
||||
box-shadow: 100vmin 0 13.203141789834469vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(38) {
|
||||
color: #4b6477;
|
||||
top: 63%;
|
||||
left: 64%;
|
||||
animation-duration: 9s;
|
||||
animation-delay: -70s;
|
||||
transform-origin: 16vw 10vh;
|
||||
box-shadow: 100vmin 0 12.63072769188053vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(39) {
|
||||
color: #a9a0bb;
|
||||
top: 69%;
|
||||
left: 81%;
|
||||
animation-duration: 56s;
|
||||
animation-delay: -361s;
|
||||
transform-origin: -17vw 11vh;
|
||||
box-shadow: 100vmin 0 13.123746520776026vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(40) {
|
||||
color: #888aa0;
|
||||
top: 54%;
|
||||
left: 33%;
|
||||
animation-duration: 142s;
|
||||
animation-delay: -2s;
|
||||
transform-origin: 4vw 24vh;
|
||||
box-shadow: 100vmin 0 13.216474278399133vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(41) {
|
||||
color: #4b6477;
|
||||
top: 56%;
|
||||
left: 10%;
|
||||
animation-duration: 65s;
|
||||
animation-delay: -105s;
|
||||
transform-origin: 3vw -24vh;
|
||||
box-shadow: 100vmin 0 13.322200697362886vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(42) {
|
||||
color: #4b6477;
|
||||
top: 2%;
|
||||
left: 39%;
|
||||
animation-duration: 213s;
|
||||
animation-delay: -62s;
|
||||
transform-origin: 4vw 10vh;
|
||||
box-shadow: -100vmin 0 12.580279613916804vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(43) {
|
||||
color: #888aa0;
|
||||
top: 100%;
|
||||
left: 49%;
|
||||
animation-duration: 12s;
|
||||
animation-delay: -71s;
|
||||
transform-origin: 9vw -21vh;
|
||||
box-shadow: 100vmin 0 13.356960223158397vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(44) {
|
||||
color: #a9a0bb;
|
||||
top: 75%;
|
||||
left: 59%;
|
||||
animation-duration: 286s;
|
||||
animation-delay: -81s;
|
||||
transform-origin: -6vw 18vh;
|
||||
box-shadow: 100vmin 0 12.718648874626872vmin currentColor;
|
||||
}
|
||||
.background span:nth-child(45) {
|
||||
color: #888aa0;
|
||||
top: 22%;
|
||||
left: 12%;
|
||||
animation-duration: 87s;
|
||||
animation-delay: -298s;
|
||||
transform-origin: -17vw 5vh;
|
||||
box-shadow: 100vmin 0 13.066314361220197vmin currentColor;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
@ -1073,53 +627,7 @@
|
||||
</div>
|
||||
<div id="container">
|
||||
</div>
|
||||
<div class="background">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user