From d00f56d0365239b398640a98efa385f9aab2e22e Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Thu, 12 Mar 2026 23:41:32 +0300 Subject: [PATCH] When a stream is loaded, paused, or stopped, display a corresponding caption in the center of the stream player. (MonitorStream.js) Additionally, when loading or stopping a stream using Go2RTC, RTSP2Web, or Janus, display a static image generated at that moment. --- web/js/MonitorStream.js | 110 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 591c6b89f..fe3db1ffd 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,7 +637,103 @@ 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; + infoBlock.textContent = text; + if (text === null || text === "") { + infoBlock.style.zIndex = 0; + this.hideImgForInfoBlock(); + } else { + const fontSize = calcTextSizeOnInfoBlock(infoBlock); + infoBlock.style.fontSize = params.fontSize || (fontSize)+'px'; + 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'); + const stream = this.getElement(); + 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; + 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%)'; + this.getElement().parentNode.appendChild(infoBlock); + currentInfoBlock = infoBlock; + } + return currentInfoBlock; + } + this.stop = function() { + if (-1 !== this.activePlayer.indexOf('zms')) { + this.writeTextInfoBlock("Stopped", {showImg: false}); + } else { + this.writeTextInfoBlock("Stopped"); + } manageEventListener.removeEventListener(this.handlerEventListener['killStream']); manageEventListener.removeEventListener(this.handlerEventListener['playStream']); if (manageEventListener.removeEventListener(this.handlerEventListener['volumechange']) == this.handlerEventListener['volumechange']) this.handlerEventListener['volumechange'] = null; @@ -1630,6 +1731,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({