From ec49506e1b1108330c979853f92c7df01762d05d Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Sun, 28 Jun 2026 23:32:40 +0300 Subject: [PATCH 01/11] When broadcasting a stream in an unsupported RTSP2Web format (for example, H265), select the next player (MonitorStream.js) --- web/js/MonitorStream.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 865a53bc45..7b53dcf660 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -2336,7 +2336,8 @@ function mseListenerSourceopen(context, videoEl, url) { const msg = `WebSocket MSE for a video object ID=${context.id} codec '${mimeCodec}' not supported. Monitor '${context.name}' ID=${context.id} not starting.`; console.log(msg); context.getElement().before(document.createTextNode(msg)); - context.stop(); + context.streamErrorRegistration(); + context.selectNextPlayer(context.player); context.RTSP2WebType = null; // Avoid repeated restarts return; } From 99c90fd3f2ffd21a9b2c8f846b2f2f416d4de99b Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Mon, 29 Jun 2026 00:01:42 +0300 Subject: [PATCH 02/11] Select next player only for "Auto" mode (MonitorStream.js) --- web/js/MonitorStream.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 7b53dcf660..2f0cd3d1b0 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -2336,9 +2336,14 @@ function mseListenerSourceopen(context, videoEl, url) { const msg = `WebSocket MSE for a video object ID=${context.id} codec '${mimeCodec}' not supported. Monitor '${context.name}' ID=${context.id} not starting.`; console.log(msg); context.getElement().before(document.createTextNode(msg)); - context.streamErrorRegistration(); - context.selectNextPlayer(context.player); context.RTSP2WebType = null; // Avoid repeated restarts + if (this.selectedPlayer) { + context.stop(); + } else { + // Select next player only for "Auto" mode + context.streamErrorRegistration(); + context.selectNextPlayer(context.player); + } return; } From 1dd51495085773dd429da2d7c38f9b8b429f6713 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 7 Jul 2026 19:05:58 +0300 Subject: [PATCH 03/11] - More accurate player switching for mode=Auto and mode=go2rtc and more comprehensive error handling. - Added getCountStreamErrors() and resetCountStreamErrors() methods. - On the Montage page, run hls.js without parsing RTSP2WebType == "HLS" because we use automatic player switching, and even if we don't use the HLS type by default, the player may switch to HLS when using RTSP2Web. - Added waitingWebrtcPlayback to video-stream.js to detect the start of RTC playback. If we receive a 'webrtc/candidate' candidate, the next step is to start playback. We wait 3 seconds, and if the video hasn't loaded, we log the error and switch to another player. - Error logging in video-stream.js occurs via errorHandling() and allows us to display a text message in the video display block. - Before logging errors, we change monitorStream.player to a specific go2rtc_webrtc or go2rtc_mse player, since we don't have just "go2rtc" in the list of players to switch to. - We'll simplify getting monitorStream: instead of looking for the parent liveStream and getting its ID, we'll directly request getMonitorStream(stringToNumber(this.id)) - Go2RTC errors now generate the onErrorGo2RTC event. - Added the ability to display text information (typically used to inform about errors) in the block where the stream is displayed. The class for the block is ".info-text" - We no longer need the changes made in https://github.com/ZoneMinder/zoneminder/commit/8b9546df3315c171d0fd8f0f6886a68119e7d951 - this.started = false; In this.stop() in MonitorStream.js we will move it higher in the code so that when stopping, the correct state of the stream is already set. --- web/js/MonitorStream.js | 195 ++++++++++++++++++---------- web/js/video-stream.js | 94 +++++++++----- web/skins/classic/css/base/skin.css | 8 ++ web/skins/classic/js/skin.js | 36 ++++- web/skins/classic/views/montage.php | 2 +- 5 files changed, 233 insertions(+), 102 deletions(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 1f4a3b6ba5..ef54980c54 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -59,6 +59,7 @@ function MonitorStream(monitorData) { request: 'stream', connkey: this.connKey }; + this.limitCountErrors = 2; this.playerPriority = { 1: { // This setting should always be priority #1. name: 'default', @@ -75,11 +76,11 @@ function MonitorStream(monitorData) { countErrors: 0, durationErrors: 0 }, - //4: { - // name: 'go2rtc_hls', // Doesn't work for live viewing. - // countErrors: 0, - // durationErrors: 0 - //}, + 100: { + name: 'go2rtc_hls', // Doesn't work for live viewing. But it's required for error counting. Let's set the priority after ZMS, i.e., in theory, with Mode=Auto, it will never be selected. + countErrors: 0, + durationErrors: 0 + }, 5: { name: 'rtsp2web_webrtc', countErrors: 0, @@ -218,7 +219,8 @@ function MonitorStream(monitorData) { } - this.selectedPlayer = p; + //this.selectedPlayer = p; + this.selectedPlayer = $j('#player').val(); // Selected player in the browser // Let's clear out the errors for (const key in this.playerPriority) { this.playerPriority[key]['countErrors'] = 0; @@ -536,6 +538,8 @@ function MonitorStream(monitorData) { ); this.handlerEventListener['errorStream'] = manageEventListener.addEventListener(stream, 'error', (e) => { + clearTimeout(this.mseWaitingErrorReset); + console.warn(`Stream playback error for monitor ID=${this.id}.`, `ERROR: ${e.srcElement.error.message}`, e); this.writeTextInfoBlock("Error"); this.streamErrorRegistration(); this.restart(this.currentChannelStream); @@ -543,40 +547,9 @@ function MonitorStream(monitorData) { ); }; - // When a go2rtc player connects but the source video codec cannot be decoded by - // this browser (e.g. an H.265 camera viewed in Chrome, which supports HEVC over - // neither WebRTC nor MSE), go2rtc negotiates the video track as inactive and sends - // only audio. The