Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <script type="text/javascript">
- var alwaysShowControls = false;
- var statusText = document.getElementById('statusText');
- var hideStatus = document.getElementById('hideStatus');
- var statusBox = document.getElementById('statusBox');
- var modelStatus = document.getElementById('model_status');
- var modelDetail = document.getElementById('model_detail');
- var detailText = document.getElementById('detailText');
- var thumbPreviewText = document.getElementById('thumbPreviewText');
- var snapImg = document.getElementById('snapImg');
- var liveIcon = document.getElementById('liveIcon');
- var overlayFrame = document.getElementById('overlayFrame');
- var previewFrame = document.getElementById('previewFrame');
- var nextFeed = document.getElementById('nextFeed');
- var prevFeed = document.getElementById('prevFeed');
- var attachedInterval = 0;
- var unattachedInterval = 0;
- var g_hSelf = { uid: 0, sid: 0 };
- var g_nState = FCS.FCVIDEO_RX_IDLE;
- var obsFeeds = { };
- var attachedFeed = new NgxStreamId({"room":0,"serv":0,"phase":"z"});
- var selectedKeyId = new NgxStreamId({"room":0,"serv":0,"phase":"z"});
- var liveIconTimer = 0;
- var clubGroupId = 0;
- function onClubShowStart(sArg)
- {
- var o = ParseJSON(sArg);
- console.log('ClubShowStart event called: ' + o);
- }
- function stateBroadcasting(nState)
- {
- return (nState === FCS.FCVIDEO_TX_IDLE || nState === FCS.FCVIDEO_TX_CLUB || nState === FCS.FCVIDEO_TX_GRP || nState === FCS.FCVIDEO_TX_PVT);
- }
- function onSessionState(sData)
- {
- var hData = null;
- if (typeof(sData) === 'string')
- {
- hData = ParseJSON(sData);
- if (hData && hData.hasOwnProperty("vs"))
- {
- var newState = parseInt(hData.vs);
- var wasBroadcasting = stateBroadcasting( g_nState );
- var isBroadcasting = stateBroadcasting( newState );
- if (wasBroadcasting && !isBroadcasting)
- {
- if (!attachedFeed.isNull())
- {
- // if we have no feeds, clear attachement (error), and if we have more
- // than 1 feed, clear attachment so model will know to choose the right
- // feed before returning to a broadcast state
- var numFeeds = Object.size(obsFeeds);
- if (numFeeds < 1 || numFeeds > 1)
- {
- unattachObsFeed(attachedFeed, "onSessionState: broadcast ending, unattaching 1 of " + numFeeds + " feeds");
- attachedFeed.clear();
- mfc.onChangeAttachment();
- }
- else Log('AMDBG: onSessionState(): broadcast ending, but attachedFeed remains since numFeeds is 1');
- }
- else Log('AMDBG: onSessionState(): BUG: broadcast ending, but no attached feed (?)');
- }
- else if (newState != g_nState)
- Log('AMDBG: onSessionState(): state change not broadcast ending for ' + g_nState + ' => ' + newState);
- renderSessionState(newState);
- }
- }
- else Log("onSessionState() unable to decode or parse json data: " + JSON.stringify(sData));
- }
- function onSetClubGroup(sArg)
- {
- var o = ParseJSON(sArg);
- if (o && o.hasOwnProperty("gid"))
- {
- clubGroupId = parseInt(o.gid);
- }
- else clubGroupId = 0;
- renderSessionState(g_nState);
- }
- function parseNgxStream(sid)
- {
- var hObj = null;
- if (typeof sid === 'string')
- {
- try { hObj = JSON.parse(sid); }
- catch (e) { Log('error parsing string for NgxStreamId: ' + sid); }
- }
- else if (isObject(sid))
- {
- hObj = sid;
- }
- if (hObj && hObj.hasOwnProperty('room'))
- {
- sid = new NgxStreamId( hObj );
- }
- else Log('error parsing object for NgxStreamId: ' + JSON.stringify(sid));
- return sid;
- }
- hideStatus.onclick = function()
- {
- // Short click action: toggle control overlay (forced on or auto-on/off)
- alwaysShowControls = !alwaysShowControls;
- redrawControls();
- };
- liveIcon.onmousedown = function()
- {
- if (liveIconTimer > 0)
- clearTimeout(liveIconTimer);
- liveIconTimer = setTimeout(function()
- {
- liveIconTimer = 0;
- if (!attachedFeed.isNull())
- {
- if (selectedKeyId.isEqual(attachedFeed))
- {
- if (obsFeeds.hasOwnProperty( attachedFeed.toString() ) &&
- obsFeeds[ attachedFeed.toString() ].mact == g_hSelf.sid)
- {
- manually_unattachObsFeed(attachedFeed);
- }
- else Log('UNATTACH ERR cant unattach[' + attachedFeed.toString() + '], obsFeeds[].mact not us or obsFeeds missing attachedFeed');
- }
- else Log('UNATTACH ERR cant unattach[' + selectedKeyId.toString() + '], its not your attached[' + attachedFeed.toString() + ']: unattach that one first');
- }
- else if (!selectedKeyId.isNull())
- {
- if (obsFeeds.hasOwnProperty( selectedKeyId.toString() ))
- {
- if (obsFeeds[ selectedKeyId.toString() ].mact == 0)
- {
- attachObsFeed(selectedKeyId);
- }
- else Log('ATTACH ERR cant attach this, mact has it owned by sid[' + obsFeeds[ selectedKeyId.toString() ].mact + '], self[' + g_hSelf.sid + ']');
- }
- else Log('selectedKeyId of ' + selectedKeyId.toString() + ' not found in obsFeeds[] !!');
- }
- }, 1500);
- };
- liveIcon.onmouseup = function()
- {
- // if short click, liveIconTimer will be there still, so cancel and
- // execute short click action, otherwise longclick arleady happened
- // and we dont need to do anything
- //
- if (liveIconTimer > 0)
- {
- clearTimeout(liveIconTimer);
- liveIconTimer = 0;
- // Short click action: toggle control overlay (forced on or auto-on/off)
- alwaysShowControls = !alwaysShowControls;
- redrawControls();
- }
- };
- function SetName(sData)
- {
- var hSelf = ParseJSON(sData);
- if (hSelf)
- {
- g_hSelf = hSelf;
- g_hSelf.channel = UserChannel(g_hSelf.uid);
- }
- else Log('Failed to g_hSelf for sData:' + sData);
- }
- function previewThumb(sid)
- {
- sid = parseNgxStream(sid);
- if (sid instanceof NgxStreamId)
- {
- sKey = sid.toString();
- if (obsFeeds.hasOwnProperty(sKey))
- {
- previewFrame.style.backgroundImage = "url('" + obsFeeds[sKey]['t_url'] + "')";
- previewFrame.innerHTML = "<span id=previewText class=previewText>Click to Close</span>";
- previewFrame.onclick = function() { hideObj(previewFrame); };
- showObj(previewFrame);
- clearTimeout(fadePreviewTimer);
- fadePreviewTimer = setTimeout( function() { fadeOut(document.getElementById('previewText')); }, 500);
- }
- else Log('previewThumb() cannot find ' + sKey + ' in obsFeeds[]');
- }
- else Log('previewThumb() expected NgxStreamId obj, dropping: ' + JSON.stringify(sid));
- }
- function attachObsFeed(sid)
- {
- sid = parseNgxStream(sid);
- if (sid instanceof NgxStreamId)
- {
- var sKey = sid.toString();
- if (obsFeeds.hasOwnProperty(sKey))
- {
- // if no obs feeds at all are attached to model, then this method
- // will take the obs feed keyId and attach it to the model. This will
- // make the model's camserv become the camserv of this obs feed, and set
- // her state to tx_idle or tx_away (tx_idle by default, tx_away only if away
- // mode is currently set)
- //
- obsFeeds[sKey].mact = g_hSelf.sid;
- attachedFeed.load(sid);
- mfc.AttachObsFeed( JSON.stringify({"sid":sid.toJson(), "updateServer": true }) );
- mfc.onChangeAttachment();
- selectFeed(sid);
- }
- else Log('Cannot attach to feed ' + sKey + ', not found in obsFeeds[]');
- }
- else Log('Cannot attach, expected NgxStreamId for sid, not: ' + JSON.stringify(sid));
- }
- function manually_unattachObsFeed(sid)
- {
- obsOpts.disableAutoAttach = true;
- unattachObsFeed(sid, "manually_unattachObsFeed: clicked unattach link");
- }
- function unattachObsFeed(sid, sLog)
- {
- sid = parseNgxStream(sid);
- if (sid instanceof NgxStreamId)
- {
- var sKey = sid.toString();
- // if obs feed keyId is attached to this model, this method will disconnect
- // that OBS feed from being attached to model and causes model's camserv to
- // become 0, and state to go to rx_idle.
- //
- var logCtx = {
- "stack": getStackTrace(),
- "sid": sid.toJson(),
- "attachedFeed": attachedFeed.toJson(),
- "log": sLog
- };
- Log( JSON.stringify(logCtx) );
- mfc.UnattachObsFeed( JSON.stringify({"sid":sid.toJson(), "obslog":logCtx, "updateServer": true }) );
- if (obsFeeds.hasOwnProperty(sKey))
- {
- if (attachedFeed.isEqual (obsFeeds[sKey].sid ))
- {
- obsFeeds[sKey].mact = 0;
- attachedFeed.clear();
- selectFeed(sid);
- mfc.onChangeAttachment();
- }
- }
- else Log('Cannot unattach to feed ' + sKey + ', not found in obsFeeds[]');
- }
- else Log('Cannot unattach, expected NgxStreamId for sid, not: ' + JSON.stringify(sid));
- }
- // fade out
- function fadeOut(el){
- el.style.opacity = 1;
- (function fade() {
- if ((el.style.opacity -= .01) < 0) {
- el.style.display = "none";
- } else {
- queuedFrames.push( requestAnimationFrame(fade) );
- }
- })();
- }
- // fade in
- // semi-fade out
- function semiFadeOut(el){
- el.style.opacity = 1;
- (function impl_semiFadeOut() {
- el.style.opacity -= 0.05;
- if (el.style.opacity > 0.06)
- semiFadeOut.queuedFrames.push( requestAnimationFrame(impl_semiFadeOut) );
- })();
- }
- semiFadeOut.queuedFrames = Array();
- semiFadeOut.clearQueuedFrames = function()
- {
- if (semiFadeOut.queuedFrames.length > 0)
- {
- for (var n = 0; n < semiFadeOut.queuedFrames.length; n++)
- window.cancelAnimationFrame(semiFadeOut.queuedFrames[n]);
- semiFadeOut.queuedFrames = Array();
- }
- };
- // semi-fade in
- function semiFadeIn(el, display){
- el.style.opacity = .15;
- //el.style.display = display || "block";
- (function impl_semiFadeIn() {
- var val = parseFloat(el.style.opacity);
- if (!((val += .05) >= 1)) {
- el.style.opacity = val;
- semiFadeIn.queuedFrames.push( requestAnimationFrame(impl_semiFadeIn) );
- }
- })();
- }
- semiFadeIn.queuedFrames = Array();
- semiFadeIn.clearQueuedFrames = function()
- {
- if (semiFadeIn.queuedFrames.length > 0)
- {
- for (var n = 0; n < semiFadeIn.queuedFrames.length; n++)
- window.cancelAnimationFrame(semiFadeIn.queuedFrames[n]);
- semiFadeIn.queuedFrames = Array();
- }
- };
- var obsOpts = {
- uid: 0,
- obs_publish_key: '',
- obs_publish_phase: 'z',
- obs_autoattach: 1,
- obs_live_preview: 450,
- ngx_autohide_preview: 1,
- // manually unattaching a stream flips this to true, and
- // auto-attaches stop until obs html is reloaded.
- //
- //disableAutoAttach: false,
- disableAutoAttach: true, // HACKHACK
- };
- var obsCtx = {
- //
- // context property tracking the fade of a live snap from 1.0 to 0.0
- // opacity when live broadcast starts, if ngx_autohide_preview is 1
- // if ngx_autohide_preview is 1
- //
- previewFadeOut: 1.0,
- };
- function setObsVars(sData)
- {
- var hObj = ParseJSON( sData );
- if (hObj)
- {
- // Setup object containing just default key/value pairs
- //var _defaults = { disableAutoAttach: false };
- var _defaults = { disableAutoAttach: true }; // HACKHACK
- // for any of the default pairs that are missing from thew new object provided, copy in
- // either the existing obsOpts valuye for it (if that exists), or our default value
- for (sK in _defaults)
- if (!hObj.hasOwnProperty(sK))
- hObj[sK] = (obsOpts.hasOwnProperty(sK) ? obsOpts[sK] : _defaults[sK]);
- // now override obsOpts with the hObj we were given
- obsOpts = hObj;
- // manually copy out attachedFeed id
- if (hObj.hasOwnProperty("_attached_sid"))
- {
- attachedFeed.load( hObj._attached_sid );
- mfc.onChangeAttachment();
- }
- }
- }
- function getStreamType(sid)
- {
- var sStreamType = "Unknown";
- if (sid.room >= 200000000 && sid.room < 300000000)
- {
- if (sid.phase == 'c') sStreamType = "Club";
- else if (sid.phase == 'p') sStreamType = "Private";
- else if (sid.phase == 'g') sStreamType = "Group";
- else sStreamType = "InvalidPhase";
- }
- else if (sid.room >= 100000000 && sid.room < 200000000)
- {
- sStreamType = "Public";
- }
- else sStreamType = "InvalidRoom";
- return sStreamType;
- }
- function renderSessionState(newState)
- {
- var nFeedSz = Object.size(obsFeeds);
- var oldState = g_nState;
- g_nState = newState;
- var smallFonts = false;
- if (g_nState == FCS.FCVIDEO_TX_IDLE) sState = "Live";
- else if (g_nState == FCS.FCVIDEO_TX_AWAY) sState = "Away";
- else if (g_nState == FCS.FCVIDEO_TX_PVT) sState = "PrivateChat"
- else if (g_nState == FCS.FCVIDEO_TX_GRP) sState = "GroupChat";
- else if (g_nState == FCS.FCVIDEO_TX_CLUB) sState = "Clubshow";
- else if (g_nState == FCS.FCVIDEO_OFFLINE) sState = "Offline";
- else if (g_nState == FCS.FCVIDEO_RX_IDLE)
- {
- if (nFeedSz == 0) sState = "Waiting for OBS to start streaming...";
- else if (attachedFeed.isNull()) sState = "Waiting for stream to be attached...";
- else if ( attachedFeed.room >= 200000000
- && attachedFeed.room < 300000000
- && attachedFeed.phase == 'c'
- && clubGroupId == 0)
- {
- sState = "Waiting for clubs to be chosen...";
- }
- else sState = "Ready to start " + getStreamType(attachedFeed) + " show...";
- smallFonts = true;
- }
- else sState = "Unknown: " + g_nState;
- if (modelStatus)
- {
- dropStyle( modelStatus, smallFonts ? "largeStatus" : "smallStatus" );
- addStyle( modelStatus, smallFonts ? "smallStatus" : "largeStatus" );
- modelStatus.innerHTML = sState;
- if (!alwaysShowControls)
- {
- //console.log('showObj() modelStatus from renderSessionState()');
- showObj(modelStatus);
- }
- }
- // Reset intervals on major state changes
- if (oldState != g_nState)
- {
- unattachedInterval = 0;
- attachedInterval = 0;
- obsCtx.previewFadeOut = 1.0;
- // changed to idle/club and attached to stream? start countdown for previews to disappear
- if ((g_nState == FCS.FCVIDEO_TX_IDLE || g_nState == FCS.FCVIDEO_TX_CLUB) && !attachedFeed.isNull())
- {
- // start drawing text banner/download of thumbs queued
- setStatusDetail();
- }
- else if ((oldState == FCS.FCVIDEO_TX_IDLE || oldState == FCS.FCVIDEO_TX_CLUB) && obsOpts.ngx_autohide_preview == 1)
- {
- // cancel/queue resume of preview snaps if we are leaving a broadcast state
- // when autohide was active. this forces us to resume preview callback loop.
- activeThumb_cancel();
- }
- }
- }
- function setStatusDetail()
- {
- if (g_nState == FCS.FCVIDEO_TX_IDLE || g_nState == FCS.FCVIDEO_TX_CLUB)
- {
- var nSec = Math.round( (obsCtx.previewFadeOut * 3) + .5 );
- if (nSec > 3) nSec = 3;
- if (obsOpts.ngx_autohide_preview == 1)
- {
- modelDetail.innerHTML = "Hiding preview in <b>" + nSec + "</b>";
- showObj(modelDetail);
- obsCtx.previewFadeOut -= .080;
- // Fade below 100% opacity, but if 0.0, then replace with empty div
- if (obsCtx.previewFadeOut > 0)
- {
- if (activeThumbImg)
- activeThumbImg.style.opacity = obsCtx.previewFadeOut;
- if (lastThumbImg)
- lastThumbImg.style.opacity = obsCtx.previewFadeOut;
- setStatusDetail.hUpdate = setTimeout(function() { setStatusDetail(); }, 230);
- }
- else
- {
- renderPreviewDiv('activeBroadcastSnap');
- obsCtx.previewFadeOut = 0;
- hideObj(modelDetail);
- }
- }
- return;
- }
- else
- {
- clearTimeout( setStatusDetail.hUpdate );
- setStatusDetail.hUpdate = 0;
- obsCtx.previewFadeOut = 1.0;
- hideObj(modelDetail);
- }
- }
- setStatusDetail.hUpdate = 0;
- function loadNextFeed()
- {
- if (obsFeeds.hasOwnProperty(selectedKeyId.toString()))
- {
- if (obsFeeds[selectedKeyId.toString()].nextId != null)
- {
- selectFeed(obsFeeds[selectedKeyId.toString()].nextId);
- }
- else Log('loadNextFeed(): prevId NULL for ' + JSON.stringify( obsFeeds[selectedKeyId.toString()] ) );
- }
- else Log('loadNextFeed(): selectedKeyId not in obsFeeds: ' + selectedKeyId.toString());
- }
- function loadPrevFeed()
- {
- if (obsFeeds.hasOwnProperty(selectedKeyId.toString()))
- {
- if (obsFeeds[selectedKeyId.toString()].prevId != null)
- {
- selectFeed(obsFeeds[selectedKeyId.toString()].prevId);
- }
- else Log('loadPrevFeed(): prevId NULL for ' + JSON.stringify( obsFeeds[selectedKeyId.toString()] ) );
- }
- else Log('loadPrevFeed(): selectedKeyId not in obsFeeds: ' + selectedKeyId.toString());
- }
- function removeFeed(hFeed)
- {
- var oldAttachedFeed = null;
- if ( ! attachedFeed.isNull() )
- {
- oldAttachedFeed = new NgxStreamId({ "room": attachedFeed.room, "serv": attachedFeed.serv, "phase": attachedFeed.phase });
- }
- else oldAttachedFeed = new NgxStreamId({"room":0,"serv":0,"phase":"z"});
- hideObj( previewFrame );
- if (hFeed.hasOwnProperty('t_url') &&
- hFeed.hasOwnProperty('camserv') &&
- hFeed.hasOwnProperty('sid') &&
- hFeed.hasOwnProperty('vidserv') &&
- hFeed.hasOwnProperty('state') )
- {
- // clear any queued actions before resetting snap frame
- clearTimeout(fadePreviewTimer);
- // prepare hFeed for adding or updating existing entry to obsFeeds[]
- var sKeyId = hFeed.sid.toString();
- if (obsFeeds.hasOwnProperty(sKeyId))
- {
- if (hFeed.sid.isEqual( selectedKeyId ))
- selectedKeyId.clear();
- if (hFeed.sid.isEqual( attachedFeed ))
- {
- Log('auto-unattached a feed that was removed')
- unattachObsFeed(hFeed.sid, "removeFeed: hFeed matching attachedFeed");
- }
- delete obsFeeds[sKeyId];
- if (Object.size(obsFeeds) > 0)
- {
- if (selectedKeyId.isNull())
- {
- for (sK in obsFeeds)
- {
- selectedKeyId.load( JSON.parse(sK) );
- break;
- }
- }
- setFeedLinks();
- selectFeed(selectedKeyId);
- }
- else resetEmptyFeeds();
- }
- }
- return ( ! attachedFeed.isEqual( oldAttachedFeed ) );
- }
- function addFeed(hFeed, hContainer)
- {
- hideObj(previewFrame); // if preview is open, close it
- var oldAttachedFeed = null;
- if ( ! attachedFeed.isNull() )
- {
- oldAttachedFeed = new NgxStreamId({ "room": attachedFeed.room, "serv": attachedFeed.serv, "phase": attachedFeed.phase });
- }
- else oldAttachedFeed = new NgxStreamId({"room":0,"serv":0,"phase":"z"});
- if (!hFeed.hasOwnProperty('sid') && hFeed.hasOwnProperty('room') && hFeed.hasOwnProperty('camserv') && hFeed.hasOwnProperty('phase'))
- {
- hFeed.sid = new NgxStreamId({ "room": parseInt(hFeed.room), "serv":parseInt(hFeed.camserv), "phase":hFeed.phase });
- }
- if (hFeed.hasOwnProperty('t_url') &&
- hFeed.hasOwnProperty('camserv') &&
- hFeed.hasOwnProperty('sid') &&
- hFeed.hasOwnProperty('vidserv') &&
- hFeed.hasOwnProperty('state') )
- {
- // clear any queued actions before resetting snap frame
- clearTimeout(fadePreviewTimer);
- // prepare hFeed for adding or updating existing entry to hContainer[]
- var sKeyId = hFeed.sid.toString();
- // add text, add to hContainer[], recalculate number of feeds
- hContainer[sKeyId] = hFeed;
- if (!hFeed.hasOwnProperty('tag'))
- hFeed.tag = 'OBS Feed ' + sKeyId;
- if (!hFeed.hasOwnProperty('mact'))
- hFeed['mact'] = 0;
- if (attachedFeed.isEqual( hFeed.sid ))
- {
- if (hFeed.mact == 0)
- {
- attachedFeed.clear();
- mfc.UnattachObsFeed( JSON.stringify({"sid": hFeed.sid.toJson(), "updateServer": false }) );
- Log('AMDBG: attachedFeed matches newly added feed, but new feed has modelactive set to 0, unattaching from it!');
- mfc.onChangeAttachment();
- }
- }
- else if (attachedFeed.isNull() && hFeed.mact == g_hSelf.sid && g_hSelf.sid > 0)
- {
- Log('AMDBG: attachedFeed is null, but newly added feed has modelactive set to our sessionId, attaching to it!');
- attachedFeed.load( hFeed.sid );
- mfc.onChangeAttachment();
- }
- }
- else Log('hFeed missing one of: t_url, camserv, vidserv, state');
- return ( ! attachedFeed.isEqual( oldAttachedFeed ) );
- }
- var fadePreviewTimer = 0;
- var thumbCancelTimer = 0;
- var thumbRefreshTimer = 0;
- function setFeedLinks()
- {
- var feedCount = Object.size(obsFeeds);
- var prevId = 0, nextId = 0, firstId = 0;
- var nIdx = 0;
- for (sK in obsFeeds)
- {
- obsFeeds[sK].prevId = null;
- obsFeeds[sK].nextId = null;
- obsFeeds[sK].idx = ++nIdx;
- if (feedCount > 1)
- {
- if (prevId != 0)
- {
- obsFeeds[prevId].nextId = sK;
- obsFeeds[sK].prevId = prevId;
- }
- else firstId = sK;
- prevId = sK;
- }
- }
- if (feedCount > 1 && prevId != 0 && firstId != 0)
- {
- obsFeeds[prevId].nextId = firstId;
- obsFeeds[firstId].prevId = prevId;
- }
- }
- var activeThumbImg, lastThumbImg;
- function activeThumb_cancel()
- {
- var oldTimer = thumbRefreshTimer;
- clearTimeout(thumbRefreshTimer);
- clearTimeout(thumbCancelTimer);
- thumbRefreshTimer = 0;
- thumbCancelTimer = 0;
- thumbRefreshTimer = setTimeout(activeThumb_download, 2000);
- }
- function activeThumb_download()
- {
- if (thumbRefreshTimer != 0)
- {
- clearTimeout(thumbRefreshTimer);
- thumbRefreshTimer = 0;
- }
- if (thumbCancelTimer != 0)
- {
- clearTimeout(thumbCancelTimer);
- thumbCancelTimer = 0;
- }
- if (obsFeeds.hasOwnProperty(selectedKeyId))
- {
- if (obsOpts.obs_live_preview > 0)
- {
- if (obsOpts.ngx_autohide_preview == 1 &&
- (g_nState == FCS.FCVIDEO_TX_IDLE ||
- g_nState == FCS.FCVIDEO_TX_CLUB ) &&
- obsCtx.previewFadeOut < 0.05 )
- {
- renderPreviewDiv('activeBroadcastSnap');
- }
- else
- {
- activeThumbImg = new Image();
- activeThumbImg.onload = activeThumb_display;
- activeThumbImg.src = obsFeeds[selectedKeyId]['t_url'] + '?nc=' + Math.random();
- activeThumbImg.style.position = "absolute";
- activeThumbImg.style.top = 0;
- activeThumbImg.style.left = 0;
- activeThumbImg.style.zIndex = -100;
- thumbCancelTimer = setTimeout(activeThumb_cancel, 3000);
- }
- }
- }
- else
- {
- thumbRefreshTimer = setTimeout(activeThumb_download, obsOpts.obs_live_preview);
- }
- }
- function activeThumb_display()
- {
- clearTimeout(thumbRefreshTimer);
- clearTimeout(thumbCancelTimer); thumbRefreshTimer = 0;
- thumbCancelTimer = 0;
- if (obsOpts.obs_live_preview > 0 || !(activeThumbImg instanceof Image))
- {
- if ( (g_nState == FCS.FCVIDEO_TX_IDLE ||
- g_nState == FCS.FCVIDEO_TX_CLUB ) &&
- obsCtx.previewFadeOut != 1.0 &&
- obsCtx.previewFadeOut > 0 &&
- obsOpts.ngx_autohide_preview == 1 )
- {
- activeThumbImg.style.opacity = obsCtx.previewFadeOut;
- }
- if (lastThumbImg != undefined)
- {
- overlayFrame.replaceChild(activeThumbImg, lastThumbImg);
- delete lastThumbImg;
- }
- else overlayFrame.appendChild(activeThumbImg);
- lastThumbImg = activeThumbImg;
- if ( (g_nState == FCS.FCVIDEO_TX_IDLE ||
- g_nState == FCS.FCVIDEO_TX_CLUB ) &&
- obsCtx.previewFadeOut >= 0 &&
- obsOpts.ngx_autohide_preview == 1 )
- {
- // do not refresh thumb, they are live and have autohide preview on
- }
- else
- {
- thumbRefreshTimer = setTimeout(activeThumb_download, obsOpts.obs_live_preview);
- }
- }
- }
- var selectedKeyId = 0;
- function selectFeed(sid)
- {
- var startLink = document.getElementById('startLink');
- var startButton = document.getElementById('startButton');
- var feedCount = Object.size(obsFeeds);
- var sKey = ((feedCount > 0 && !selectedKeyId.isNull()) ? selectedKeyId.toString() : false);
- var isSelected = (sKey !== false && obsFeeds.hasOwnProperty(sKey));
- var isAttached = (isSelected && obsFeeds[sKey].mact == g_hSelf.sid );
- sid = parseNgxStream(sid);
- if (sid instanceof NgxStreamId)
- {
- var sKeyId = sid.toString();
- if (obsFeeds.hasOwnProperty(sKeyId))
- {
- selectedKeyId = sid;
- var sStreamType = "Unknown";
- var sStartText = "";
- var sStopText = "";
- if (sid.room >= 200000000 && sid.room < 300000000)
- {
- if (sid.phase == 'c') sStreamType = "Club";
- else if (sid.phase == 'p') sStreamType = "Private";
- else if (sid.phase == 'g') sStreamType = "Group";
- else sStreamType = "InvalidSess";
- sStartText = "(Start " + sStreamType + "show)";
- sStopText = "(End " + sStreamType + "show)";
- }
- else if (sid.room >= 100000000 && sid.room < 200000000)
- {
- sStreamType = "Public";
- sStartText = "(Go Live)";
- sStopText = "(Go Away)";
- }
- else sStreamType = "InvalidRoom";
- statusText.innerHTML = sStreamType + ' Stream (' + obsFeeds[sKeyId].idx + ' of ' + feedCount + ')';
- var isAttached = (obsFeeds[sKeyId].mact == g_hSelf.sid ? true : false);
- var sHtml = "video" + obsFeeds[sKeyId].camserv;
- if (obsFeeds[sKeyId].hasOwnProperty('age'))
- sHtml += formatSeconds( parseInt(obsFeeds[sKeyId].age) );
- detailText.innerHTML = sHtml;
- sHtml = "<span class=startButton id=startButton><br><center><a class='startLink' id=startLink href='#' ";
- var sRet = (window.parent && window.parent.UI ? "" : "return false;");
- if (!isAttached)
- sHtml += "onclick='attachObsFeed(" + sKeyId + ");" + sRet + "'>Attach to " + sStreamType + " " + sStartText;
- else
- sHtml += "onclick='manually_unattachObsFeed(" + sKeyId + ");" + sRet + "'>Unattach from " + sStreamType + " " + sStopText;
- sHtml += "</a></center></span>";
- snapImg.innerHTML = sHtml;
- addStyle(overlayFrame, 'frameImgFeed');
- // queue download of thumbnail background for this feed now that it's the selected feed
- if (thumbRefreshTimer != 0)
- clearTimeout(thumbRefreshTimer);
- thumbRefreshTimer = setTimeout(activeThumb_download, 100);
- // remove hidden class, if its active
- liveIcon.updateImgStatus();
- redrawControls();
- }
- else Log('missing keyId[' + sKeyId + '] from obsFeeds: ' + JSON.stringify( obsFeeds ));
- }
- else Log('sid argument isnt NgxStreamId, call dropped');
- }
- function setLivePreview()
- {
- var livePreview = document.getElementById('obs_live_preview');
- obsOpts.obs_live_preview = parseInt( livePreview.options[ livePreview.selectedIndex ].value );
- activeThumb_download();
- mfc.SaveUEOpts( JSON.stringify( { "obs_live_preview": obsOpts.obs_live_preview } ) );
- }
- function onPublishErr(sData)
- {
- var objEv = null;
- try
- {
- if ((objEv = JSON.parse(sData)))
- {
- if (objEv.hasOwnProperty("op"))
- {
- if (objEv.op == "attach")
- {
- obsOpts.disableAutoAttach = true;
- attachedFeed.clear();
- mfc.onChangeAttachment();
- }
- else if (objEv.op == "unattach")
- {
- Log('unattach operation failed');
- }
- }
- }
- }
- catch(e) { Log('onPublishErr error with: ' + JSON.stringify(e)); }
- }
- function onFeedStateEvents(sData)
- {
- var trimKeys = [ 'prevId', 'nextId', 'idx', 'age', 'exp' ];
- var attachmentChanged = false;
- var objEv = null;
- try
- {
- if ((objEv = JSON.parse(sData)))
- {
- if (isArray(objEv))
- {
- newObsFeeds = { };
- var sKeyId = '';
- for (var n = 0; n < objEv.length; n++)
- {
- if (isObject(objEv[n]) && objEv[n].hasOwnProperty('state') && objEv[n].hasOwnProperty('op') && objEv[n].hasOwnProperty('room'))
- {
- if (!(objEv[n].op & FCS.FCCHAN_PART))
- {
- objEv[n].sid = new NgxStreamId({ "room": parseInt(objEv[n].room), "serv": parseInt(objEv[n].camserv), "phase": objEv[n].phase });
- if (sKeyId == '')
- sKeyId = objEv[n].sid.toString();
- if (addFeed(objEv[n], newObsFeeds))
- attachmentChanged = true;
- }
- }
- }
- var newSz = Object.size(newObsFeeds);
- var oldSz = Object.size(obsFeeds);
- var anyChanges = (oldSz != newSz);
- if (oldSz === newSz)
- {
- var hOrig = JSON.parse( JSON.stringify( obsFeeds ) );
- var hNew = JSON.parse( JSON.stringify( newObsFeeds ) );
- for (n in hOrig)
- for (x in trimKeys)
- delete hOrig[n][ trimKeys[x] ];
- for (n in hNew)
- for (x in trimKeys)
- delete hNew[n][ trimKeys[x] ];
- var sOrig = JSON.stringify( hOrig );
- var sNew = JSON.stringify( hNew );
- anyChanges = (sOrig !== sNew);
- }
- // redraw/rebuild only if some relevant changes made to obsFeeds
- if (anyChanges || attachmentChanged)
- {
- // change in obsFeeds, reset obsFeeds to newObsFeeds and then reprocess attachments
- obsFeeds = newObsFeeds;
- // if the number of feeds changed and we are in either Away or RX idle, then
- // we auto-un attach any currently attached feeds.
- if (g_nState === FCS.FCVIDEO_TX_AWAY || g_nState === FCS.FCVIDEO_RX_IDLE)
- {
- if (oldSz !== newSz)
- {
- if (!attachedFeed.isNull())
- {
- unattachObsFeed(attachedFeed, "onFeedStateEvents: change in feeds while idle/away");
- attachmentChanged = true;
- attachedFeed.clear();
- }
- }
- }
- // if our attached feed is no longer found in obsFeeds, also unattach it
- if (!attachedFeed.isNull() && !obsFeeds.hasOwnProperty(attachedFeed.toString()))
- {
- unattachObsFeed(attachedFeed, "onFeedStateEvents: obsFeeds missing attachedFeed");
- attachmentChanged = true;
- attachedFeed.clear();
- }
- // if our selectedFeed is no longer in obsFeeds, clear the currently selected index
- if ( ! selectedKeyId.isNull() )
- if ( ! obsFeeds.hasOwnProperty( selectedKeyId.toString() ) )
- selectedKeyId.clear();
- // if we have an attachment but no selected feed, set selected feed to that
- // attachment, otherwise we point sKeyId to the currently selected feed. We
- // reselect that feed (sKeyId) next, provided obsFeeds isnt empty.
- if ( selectedKeyId.isNull() )
- {
- if ( ! attachedFeed.isNull() )
- if ( obsFeeds.hasOwnProperty( attachedFeed.toString() ) )
- sKeyId = attachedFeed.toString();
- }
- else sKeyId = selectedKeyId.toString();
- // if we have at least 1 feed, then re-select current feed in case attachment
- // status changed or selected key id moved/reset. Otherwise reset status to
- // empty feed mode when no feeds present.
- if (Object.size(obsFeeds) > 0)
- {
- setFeedLinks();
- selectFeed(sKeyId);
- }
- else resetEmptyFeeds();
- }
- else
- {
- //Log('onFeedStates: no relevant changes in obsFeeds or attachedFeed status');
- obsFeeds = newObsFeeds;
- setFeedLinks();
- }
- }
- else if (isObject(objEv))
- {
- Log('BUG: Received [deprecated] single feedstate object');
- if ( objEv.hasOwnProperty('state')
- && objEv.hasOwnProperty('sid')
- && objEv.hasOwnProperty('op') )
- {
- if (objEv.op & FCS.FCCHAN_PART)
- {
- if ( removeFeed(objEv, obsFeeds) )
- attachmentChanged = true;
- }
- else if (objEv.op & FCS.FCCHAN_JOIN)
- {
- if ( addFeed(objEv, obsFeeds) )
- attachmentChanged = true;
- }
- }
- }
- }
- }
- catch(e) { Log('onFeedStateEvents error with: ' + JSON.stringify(e)); }
- if (attachmentChanged)
- mfc.onChangeAttachment();
- if (Object.size(obsFeeds) > 0)
- {
- var sHtml = '';
- var hOpts = [ { "val": 0, "label": "Disable preview images." },
- { "val": 10, "label": "Quickly as possible." },
- { "val": 250, "label": "4 frames per sec." },
- { "val": 500, "label": "2 frames per sec." },
- { "val": 1000, "label": "1 frame per sec." },
- { "val": 2000, "label": "1 frame per 2 sec." },
- { "val": 5000, "label": "1 frame per 5 sec." } ];
- sHtml = "<span class=obsHidePreviewLabel>Preview image display:</span> <br> ";
- sHtml += "<select id=obs_live_preview onchange='setLivePreview();' class=obsLiveSelect>";
- for (var n = 0; n < hOpts.length; n++)
- sHtml += "<option class=obsLiveOption value="
- + hOpts[n].val + " "
- + (obsOpts.obs_live_preview == hOpts[n].val ? "selected" : "")
- + ">" + hOpts[n].label;
- sHtml += "</select><br>";
- sHtml += "<input type=checkbox class=obsHidePreview id=obs_hide_preview " + (obsOpts.ngx_autohide_preview == 0 ? "" : "checked") + ">"
- + "<span class=obsHidePreviewLabel><a href='#' onclick='hidePreviewToggle();'>"
- + "Hide preview when Live</a></span>";
- thumbPreviewText.innerHTML = sHtml;
- statusBox.style.height = "142px";
- showObj(hideStatus);
- }
- else
- {
- thumbPreviewText.innerHTML = "";
- renderPreviewDiv('emptySnap');
- showObj(modelStatus);
- hideObj(hideStatus);
- hideObj(statusBox);
- }
- }
- function hidePreviewToggle()
- {
- hideCheckbox = document.getElementById('obs_hide_preview');
- hideCheckbox.checked = !hideCheckbox.checked;
- obsOpts.ngx_autohide_preview = (hideCheckbox.checked ? 1 : 0);
- mfc.SaveUEOpts( JSON.stringify( { "ngx_autohide_preview": obsOpts.ngx_autohide_preview } ) );
- }
- function renderPreviewDiv(sClass)
- {
- activeThumbImg = buildPreviewDiv(sClass);
- activeThumb_display();
- }
- function buildPreviewDiv(sClass)
- {
- var div = document.createElement('div');
- div.className = sClass;
- div.style.position = "absolute";
- div.style.top = 0;
- div.style.left = 0;
- div.style.zIndex = -100;
- return div;
- }
- function resetEmptyFeeds()
- {
- obsFeeds = { };
- selectedKeyId = new NgxStreamId({"room":0,"serv":0,"phase":"z"});
- detailText.innerHTML = "Start streaming in OBS Studio.";
- statusText.innerHTML = "Waiting for OBS Feeds.";
- thumbPreviewText.innerHTML = "";
- snapImg.innerHTML = "";
- overlayFrame.style.backgroundImage = "none;";
- overlayFrame.style.backgroundColor = "transparent";
- dropStyle(overlayFrame, 'frameImgFeed');
- dropStyle(nextFeed, 'activeNextFeed');
- addStyle(nextFeed, 'inactiveNextFeed');
- hideObj(nextFeed);
- dropStyle(prevFeed, 'activePrevFeed');
- addStyle(prevFeed, 'inactivePrevFeed');
- hideObj(prevFeed);
- alwaysShowControls = false;
- //showObj(modelStatus);
- // make sure we can see the 'waiting for obs feeds...' banner
- //showObj(statusBox);
- liveIcon.updateImgStatus();
- redrawControls();
- }
- function redrawControls()
- {
- // update if the links for previous, next, and attach/unattach
- // should be hidden or visible based on attach state and number
- // of obs feeds available
- //
- var feedCount = Object.size(obsFeeds);
- var startLink = document.getElementById('startLink');
- var startButton = document.getElementById('startButton');
- var sKey = ((feedCount > 0 && !selectedKeyId.isNull()) ? selectedKeyId.toString() : false);
- var isSelected = (sKey !== false && obsFeeds.hasOwnProperty(sKey));
- var isAttached = (isSelected && obsFeeds[sKey].mact == g_hSelf.sid );
- var hasNext = (isSelected && obsFeeds[sKey].nextId);
- var hasPrev = (isSelected && obsFeeds[sKey].prevId);
- dropStyle( nextFeed, (hasNext ? 'inactiveNextFeed' : 'activeNextFeed' ) );
- addStyle( nextFeed, (hasNext ? 'activeNextFeed' : 'inactiveNextFeed' ) );
- dropStyle( prevFeed, (hasPrev ? 'inactivePrevFeed' : 'activePrevFeed' ) );
- addStyle( prevFeed, (hasPrev ? 'activePrevFeed' : 'inactivePrevFeed' ) );
- // hide next/previous links when attached, or when we only
- // have 1 feed to choose from. If there are more feeds, model
- // must unattach before getting these navigation links to browse
- // snapshots. Name of function assigned to sFunc, call that
- // function on both prevFeed and nextFeed as arguments.
- //
- var sFunc = (feedCount == 1 || isAttached) ? "hideObj" : "showObj";
- execFuncByName(sFunc, "", prevFeed);
- execFuncByName(sFunc, "", nextFeed);
- // hide the attach/unattach links when we're attached and
- // dont have the alwaysShowControls flag set, but show them
- // if we are not attached or, but
- // show them if we're not attached or the
- // can always force toggle them back on by clicking the liveicon.
- // if model clicks live icon to view these, alwaysShowControls
- // is set and they are then pinned open/visible, otherwise only
- // visible when no feed is attached.
- //
- sFunc = (alwaysShowControls || !isAttached) ? "showObj" : "hideObj";
- execFuncByName(sFunc, "", startLink);
- if (startButton)
- {
- execFuncByName(sFunc, "", startButton);
- startButton.style.background = "rgba(66, 66, 66, " + ((alwaysShowControls || !isAttached) ? "0.30" : "0.00") + ")";
- }
- // status box/modelStatus is directly tied to show controls flag or
- // when feed count is 0. modelStatus is always opposite from statusBox.
- //
- hideObj( (alwaysShowControls || feedCount < 1) ? modelStatus : statusBox );
- showObj( (alwaysShowControls || feedCount < 1) ? statusBox : modelStatus );
- renderSessionState(g_nState);
- statusBox.style.opacity = (isAttached ? .88 : 1);
- liveIcon.updateImgStatus();
- }
- liveIcon.onmouseenter = function()
- {
- liveIcon.updateImgStatus();
- if (liveIcon.fadeTimer != 0)
- {
- clearTimeout(liveIcon.fadeTimer);
- liveIcon.fadeTimer = 0;
- }
- semiFadeIn.clearQueuedFrames();
- semiFadeOut.clearQueuedFrames();
- liveIcon.fadeTimer = setTimeout( function() { semiFadeIn(liveIcon); }, 10);
- };
- liveIcon.onmouseleave = function()
- {
- liveIcon.updateImgStatus();
- if (liveIcon.fadeTimer != 0)
- {
- clearTimeout(liveIcon.fadeTimer);
- liveIcon.fadeTimer = 0;
- }
- semiFadeIn.clearQueuedFrames();
- semiFadeOut.clearQueuedFrames();
- liveIcon.fadeTimer = setTimeout( function() { semiFadeOut(liveIcon); }, 100);
- };
- liveIcon.updateImgStatus = function()
- {
- var feedCount = Object.size(obsFeeds);
- var sKey = ((feedCount > 0 && !selectedKeyId.isNull()) ? selectedKeyId.toString() : false);
- var isSelected = (sKey !== false && obsFeeds.hasOwnProperty(sKey));
- var isAttached = (isSelected && obsFeeds[sKey].mact == g_hSelf.sid );
- // make sure to add the right activated/attached styles to liveIcon,
- // remove any of the wrong inactive/unattached styles from liveIcon
- //
- addStyle( liveIcon, 'live_' + (isAttached ? 'active' : 'inactive') + '_icon' );
- dropStyle( liveIcon, 'live_' + (isAttached ? 'inactive' : 'active') + '_icon' );
- // show icon if feed count non-zero, otherwise hide it
- execFuncByName( (feedCount > 0 ? "showObj" : "hideObj"), "", liveIcon );
- }
- // initialize controls, draw controls
- resetEmptyFeeds();
- // change state from defaulted tx_idle to offline until we get our actual state data from parent
- onSessionState( JSON.stringify( {"vs":FCS.FCVIDEO_RX_IDLE} ) );
- // each second evaluate if we need to auto-attach to a feed based on our auto-attach settings
- setInterval(function()
- {
- if (attachedFeed.isNull())
- {
- unattachedInterval++;
- attachedInterval = 0;
- // auto-attach? first check if its enabled and there is at least one feed
- var nFeedSz = Object.size(obsFeeds);
- if (obsOpts.obs_autoattach & 1 && nFeedSz > 0)
- {
- if (obsOpts.disableAutoAttach == false)
- {
- // if only one feed, attach to it. If more than one feed, attach to either the oldest
- // (if auto attach oldest bit is set), or the newest (if newest bit is set), or if neither
- // are set, then don't auto-attach.
- //
- if (nFeedSz == 1) autoAttachFirst();
- else if (obsOpts.obs_autoattach & 4) autoAttachOldest();
- else if (obsOpts.obs_autoattach & 8) autoAttachNewest();
- else Log('No auto-attach handler called, feedSz: ' + nFeedSz);
- }
- }
- }
- else
- {
- unattachedInterval = 0;
- attachedInterval++;
- }
- }, 1000);
- function autoAttachFirst()
- {
- // auto-attach to first feed in obsFeeds
- var sKey = false;
- for (sK in obsFeeds)
- if (obsFeeds[sK].hasOwnProperty('age') && sKey === false)
- sKey = sK;
- if (sKey !== false)
- {
- Log('[autoAttach first] attached to ' + sKey + ', age: ' + parseInt(obsFeeds[sKey].age) + ' sec');
- attachObsFeed(sKey);
- }
- }
- function autoAttachOldest()
- {
- // auto-attach to oldest
- var sKey = (selectedKeyId.isNull() ? false : selectedKeyId.toString());
- var nVal = 0;
- for (sK in obsFeeds)
- {
- if (obsFeeds[sK].hasOwnProperty('age'))
- {
- if (nVal <= parseInt(obsFeeds[sK].age))
- {
- nVal = parseInt(obsFeeds[sK].age);
- sKey = sK;
- }
- }
- }
- if (sKey !== false)
- {
- Log('[autoAttach oldest] attached to ' + sKey + ', age: ' + parseInt(obsFeeds[sKey].age) + ' sec');
- attachObsFeed(sKey);
- }
- }
- function autoAttachNewest()
- {
- // auto-attach to newest
- var sKey = (selectedKeyId.isNull() ? false : selectedKeyId.toString());
- var nVal = 999999999;
- for (sK in obsFeeds)
- {
- if (obsFeeds[sK].hasOwnProperty('age'))
- {
- if (nVal > parseInt(obsFeeds[sK].age))
- {
- nVal = parseInt(obsFeeds[sK].age);
- sKey = sK;
- }
- }
- }
- if (sKey !== false)
- {
- Log('[autoAttach newest] attached to ' + sKey + ', age: ' + parseInt(obsFeeds[sKey].age) + ' sec');
- attachObsFeed(sKey);
- }
- }
- function addStyle(obj, someClass)
- {
- if (obj != undefined)
- if (!obj.classList.contains(someClass))
- obj.classList.add(someClass);
- }
- function dropStyle(obj, someClass)
- {
- if (obj != undefined)
- if (obj.classList.contains(someClass))
- obj.classList.remove(someClass);
- }
- function showObj(obj) { dropStyle(obj, "hidden"); }
- function hideObj(obj) { addStyle(obj, "hidden"); }
- function execFuncByName(functionName, context /*, ... */ )
- {
- var args = Array.prototype.slice.call(arguments, 2);
- return window[functionName].apply(context, args);
- }
- function formatSeconds(nSec)
- {
- var nMin = 0, nHour = 0, nDay = 0;
- var sHtml = "<span class=feedTime>";
- var nBaseLen = sHtml.length;
- while (nSec >= 86400)
- {
- nSec -= 86400;
- nDay++;
- }
- while (nSec >= 3600)
- {
- nSec -= 3600;
- nHour++;
- }
- while (nSec >= 60)
- {
- nSec -= 60;
- nMin++;
- }
- if (nDay > 0)
- sHtml += nDay + " day" + (nDay > 1 ? "s" : "");
- if (nHour > 0)
- sHtml += (sHtml.length > nBaseLen ? ", " : "") + nHour + " hour" + (nHour > 1 ? "s" : "");
- if (nMin > 0)
- sHtml += (sHtml.length > nBaseLen ? ", " : "") + nMin + " min";
- // only render seconds if the feed is < 1 min old
- if (nDay == 0 && nHour == 0 && nMin == 0 && nSec > 0)
- sHtml += (sHtml.length > nBaseLen ? ", " : "") + nSec + " sec";
- sHtml += "</span>";
- return sHtml;
- }
- function getStackTrace ()
- {
- var stack;
- try { throw new Error(''); }
- catch (error) { stack = error.stack || ''; }
- stack = stack.split('\n').map(function (line) { return line.trim(); });
- return stack.splice(stack[0] == 'Error' ? 2 : 1);
- }
- </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement