diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 591c6b89f..b9136cf6d 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -83,12 +83,14 @@ function MonitorStream(monitorData) { this.img_onerror = function() { console.log('Image stream has been stopped! stopping streamCmd'); this.streamCmdTimer = clearInterval(this.streamCmdTimer); + this.writeTextInfoBlock("Error", {showImg: false}); }; this.img_onload = function() { if (!this.streamCmdTimer) { console.log('Image stream has loaded! starting streamCmd for monitor ID='+this.id+' connKey='+this.connKey+' in '+statusRefreshTimeout + 'ms'); this.streamCmdQuery(); // This is to get an instant status update this.streamCmdTimer = setInterval(this.streamCmdQuery.bind(this), statusRefreshTimeout); + this.writeTextInfoBlock(""); } }; @@ -404,18 +406,21 @@ function MonitorStream(monitorData) { } this.handlerEventListener['playStream'] = manageEventListener.addEventListener(stream, 'play', (e) => { + this.writeTextInfoBlock(""); this.createVolumeSlider(); - getTracksFromStream(this); //Go2rtc + getTracksFromStream(this); } ); this.handlerEventListener['pauseStream'] = manageEventListener.addEventListener(stream, 'pause', (e) => { + this.writeTextInfoBlock("Paused", {showImg: false}); manageEventListener.removeEventListener(this.handlerEventListener['volumechange']); } ); }; this.start = function(streamChannel = 'default') { + this.writeTextInfoBlock("Loading..."); if (streamChannel === null || streamChannel === '' || currentView == 'montage') streamChannel = 'default'; // Normalize channel name for internal tracking if (streamChannel == 'default') { @@ -632,6 +637,98 @@ function MonitorStream(monitorData) { this.updateStreamInfo('ZMS MJPEG'); }; // this.start + this.setSrcInfoBlock = function() { + const imgInfoBlock = document.getElementById('img-stream-info-block' + this.id); + if (!imgInfoBlock) return null; + + let src = this.url_to_zms.replace(/mode=jpeg/i, 'mode=single'); + if (-1 == src.search('auth')) { + src += '&'+auth_relay; + } else { + src = src.replace(/auth=\w+/i, 'auth='+auth_hash); + } + if (-1 == src.search('scale=')) { + src += '&scale='+this.scale; + } + if (-1 == src.search('mode=')) { + src += '&mode=single'; + } + imgInfoBlock.src = ''; + imgInfoBlock.src = src; + return imgInfoBlock; + }; + + this.writeTextInfoBlock = function(text, params = {}) { + const infoBlock = document.getElementById('stream-info-block' + this.id) || this.createInfoBlock(); + if (infoBlock) { + if (params.color) infoBlock.style.color = params.color; + const normalizedText = (text == null) ? '' : text; + infoBlock.textContent = normalizedText; + if (normalizedText === "") { + infoBlock.style.zIndex = 0; + this.hideImgForInfoBlock(); + } else { + setTextSizeOnInfoBlock(infoBlock); + infoBlock.style.zIndex = 10001; + if (params.showImg === false) { + this.hideImgForInfoBlock(); + } else { + this.createImgForInfoBlock(); + this.showImgForInfoBlock(); + } + } + } + }; + + this.hideImgForInfoBlock = function() { + const imgInfoBlock = document.getElementById('img-stream-info-block' + this.id); + if (imgInfoBlock) imgInfoBlock.classList.add('hidden-shift'); + }; + + this.showImgForInfoBlock = function() { + const imgInfoBlock = document.getElementById('img-stream-info-block' + this.id); + if (imgInfoBlock) imgInfoBlock.classList.remove('hidden-shift'); + }; + + this.createImgForInfoBlock = function() { + let currentImg = document.getElementById('img-stream-info-block' + this.id); + if (!currentImg) { + const imgInfoBlock = document.createElement('img'); + imgInfoBlock.classList.add('img-stream-info-block'); + imgInfoBlock.id = 'img-stream-info-block' + this.id; + imgInfoBlock.style.position = 'absolute'; + imgInfoBlock.style.top = 0; + imgInfoBlock.style.left = 0; + imgInfoBlock.style.width = '100%'; + imgInfoBlock.style.height = '100%'; + imgInfoBlock.style.zIndex = 10000; + imgInfoBlock.style.pointerEvents = 'none'; + this.getElement().parentNode.appendChild(imgInfoBlock); + currentImg = imgInfoBlock; + } + this.setSrcInfoBlock(); + return currentImg; + }; + + this.createInfoBlock = function() { + let currentInfoBlock = document.getElementById('stream-info-block' + this.id); + if (!currentInfoBlock) { + const infoBlock = document.createElement('div'); + infoBlock.classList.add('stream-info-block'); + infoBlock.id = 'stream-info-block' + this.id; + infoBlock.style.position = 'absolute'; + infoBlock.style.width = '100%'; + infoBlock.style.height = 'auto'; + infoBlock.style.top = '50%'; + infoBlock.style.left = '50%'; + infoBlock.style.transform = 'translate(-50%, -50%)'; + infoBlock.style.pointerEvents = 'none'; + this.getElement().parentNode.appendChild(infoBlock); + currentInfoBlock = infoBlock; + } + return currentInfoBlock; + }; + this.stop = function() { manageEventListener.removeEventListener(this.handlerEventListener['killStream']); manageEventListener.removeEventListener(this.handlerEventListener['playStream']); @@ -647,6 +744,11 @@ function MonitorStream(monitorData) { console.warn(`! ${dateTimeToISOLocal(new Date())} Stream for ID=${this.id} has already stopped.`); return; } + if (-1 !== this.activePlayer.indexOf('zms')) { + this.writeTextInfoBlock("Stopped", {showImg: false}); + } else { + this.writeTextInfoBlock("Stopped"); + } console.debug(`! ${dateTimeToISOLocal(new Date())} Stream for ID=${this.id} STOPPING`); this.statusCmdTimer = clearInterval(this.statusCmdTimer); this.streamCmdTimer = clearInterval(this.streamCmdTimer); @@ -1630,6 +1732,13 @@ function MonitorStream(monitorData) { $j.ajaxSetup({timeout: AJAX_TIMEOUT}); this.streamCmdReq = function(streamCmdParms) { + if (-1 !== this.activePlayer.indexOf('zms')) { + if (streamCmdParms.command == CMD_PAUSE) { + this.writeTextInfoBlock("Paused", {showImg: false}); + } else if (streamCmdParms.command == CMD_PLAY) { + this.writeTextInfoBlock(""); + } + } if (!(streamCmdParms.command == CMD_STOP && ((-1 !== this.activePlayer.indexOf('go2rtc')) || (-1 !== this.activePlayer.indexOf('rtsp2web'))))) { //Otherwise, there will be errors in the console "Socket ... does not exist" when quickly switching stop->start and we also do not need to replace SRC in getStreamCmdResponse this.ajaxQueue = jQuery.ajaxQueue({ diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index 615dbeea7..144ab9b79 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -1621,6 +1621,28 @@ video-stream[id^='liveStream'] video{ border-radius: 4px; } +.text-3d { + color: transparent; + text-shadow: -4px 4px hsla(0, 0%, 70%, .4), + -3px 3px hsla(0, 0%, 65%, .2), + -2px 2px hsla(0, 0%, 60%, .2), + -1px 1px hsla(0, 0%, 55%, .2), + 0px 0px hsla(0, 0%, 50%, .5), + 1px -1px hsla(0, 0%, 40%, .6), + 2px -2px hsla(0, 0%, 38%, .7), + 3px -3px hsla(0, 0%, 37%, .8), + 4px -4px hsla(0, 0%, 36%, .9), + 5px -5px hsla(0, 0%, 35%, 1.0); +} +.text-3d-mini { + color: transparent; + text-shadow: -2px 2px hsla(0, 0%, 65%, .2), + -1px 1px hsla(0, 0%, 55%, .2), + 0px 0px hsla(0, 0%, 50%, .5), + 1px -1px hsla(0, 0%, 40%, .6), + 2px -2px hsla(0, 0%, 30%, .7); +} + /* +++ This block should always be located at the end! */ .hidden { display: none; diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index b71e12c59..af9a4613a 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -1854,6 +1854,30 @@ function setButtonSizeOnStream() { }); } +function calcTextSizeOnInfoBlock(el) { + const w = el.offsetWidth; + const textLength = el.innerText.length; + if (textLength === 0) return false; + const d = (w/400 > 1) ? 1 : w/400/0.8; // If the block width is less than 400px, the text will take up more than 40% of the width, otherwise it will be difficult to read. + return parseInt((w/textLength) * 0.6 / d); // ~40% of the block width +} + +function setTextSizeOnInfoBlocks() { + const block = document.querySelectorAll('[id ^= "stream-info-block"]'); + Array.prototype.forEach.call(block, (el) => { + setTextSizeOnInfoBlock(el); + }); +} + +function setTextSizeOnInfoBlock(el) { + if (el.innerText.length == 0) return; + const fontSize = calcTextSizeOnInfoBlock(el); + el.style.fontSize = fontSize + "px"; + el.classList.remove("text-3d-mini", "text-3d"); + const blockClass = (fontSize !== fontSize || fontSize < 50) ? 'text-3d-mini' : 'text-3d'; + el.classList.add(blockClass); +} + /* * date - object type Date() * shift.offset - number (can be negative) @@ -2768,6 +2792,7 @@ function monitorsSetScale(id=null) { } } // End function _setScale setButtonSizeOnStream(); + setTextSizeOnInfoBlocks(); } // End function monitorsSetScale /*IMPORTANT DO NOT CALL WITHOUT CONSCIOUS NEED!!!*/