Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0831913
The "RTSP2WebStream" setting is now visible for both RTSP2Web and Go2…
IgorA100 Jul 24, 2025
e474bf9
Added support for streamChannelSelected from session cookies and brow…
IgorA100 Jul 24, 2025
b2f7911
The start of the stream now always occurs with the indication of the …
IgorA100 Jul 24, 2025
e00f8dc
The MonitorStream.start() method can now be passed both a numerical c…
IgorA100 Jul 24, 2025
bded137
On Montage page StreamChannel is now always used from the monitor set…
IgorA100 Jul 24, 2025
97bd5d9
Renamed manageRTSP2WebChannelStream() function to manageChannelStream…
IgorA100 Jul 24, 2025
6f21ac6
Renamed manageRTSP2WebChannelStream() function to manageChannelStream…
IgorA100 Jul 24, 2025
4784bf0
Fix: When switching monitors, cookies may store a channel from the pr…
IgorA100 Jul 24, 2025
864c705
Missing semicolon (watch.js)
IgorA100 Jul 24, 2025
289a8b9
We will now manage player availability from JS (watch.php)
IgorA100 Jul 24, 2025
69a7270
Merge branch 'ZoneMinder:master' into patch-784062
IgorA100 Jul 24, 2025
8da9cb3
We will now manage player availability here (MonitorStream.js)
IgorA100 Jul 24, 2025
989b4a8
Fix: Missing semicolons (MonitorStream.js)
IgorA100 Jul 24, 2025
3356c06
Correct switching of players and stream channels (watch.js)
IgorA100 Jul 25, 2025
a01a06b
Added the variable "activePlayer". It is necessary to understand whic…
IgorA100 Jul 25, 2025
42607c8
Missing semicolon (MonitorStream.js)
IgorA100 Jul 25, 2025
a88ac36
Fix: eslint (watch.js)
IgorA100 Jul 25, 2025
042496e
The tooltip about player unavailability in <select> on Watch page now…
IgorA100 Jul 25, 2025
0194097
When changing the player after stopping the stream, a delay is requir…
IgorA100 Jul 25, 2025
b39bf32
Run applyChosen() (watch.js)
IgorA100 Jul 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion web/js/MonitorStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ function MonitorStream(monitorData) {
};

this.player = '';
this.activePlayer = ''; // Variants: go2rtc, janus, rtsp2web_hls, rtsp2web_mse, rtsp2web_webrtc, zms. Relevant for this.player = ''/Auto
this.setPlayer = function(p) {
if (-1 != p.indexOf('go2rtc')) {

Expand All @@ -100,6 +101,46 @@ function MonitorStream(monitorData) {
return this.player = p;
};

this.manageAvailablePlayersOptions = function(action, opt) {
if (action == 'disable') {
opt.setAttribute('disabled', '');
opt.setAttribute('title', playerDisabledInMonitorSettings);
} else if (action == 'enable') {
opt.removeAttribute('disabled');
opt.removeAttribute('title');
}
};

this.manageAvailablePlayers = function() {
const selectPlayers = document.querySelector('[id="player"][name="codec"]');
const opts = selectPlayers.options;

for (var opt, j = 0; opt = opts[j]; j++) {
if (-1 !== opt.value.indexOf('go2rtc')) {
if (this.Go2RTCEnabled) {
this.manageAvailablePlayersOptions('enable', opt);
} else {
this.manageAvailablePlayersOptions('disable', opt);
}
} else if (-1 !== opt.value.indexOf('rtsp2web')) {
if (this.RTSP2WebEnabled) {
this.manageAvailablePlayersOptions('enable', opt);
} else {
this.manageAvailablePlayersOptions('disable', opt);
}
}
}
if (selectPlayers.options[selectPlayers.selectedIndex].value == '') {
// Perhaps "Auto" is left from the previous monitor, we will change it according to the cookies.
selectPlayers.value = getCookie('zmWatchPlayer');
}
if (selectPlayers.options[selectPlayers.selectedIndex].disabled) {
// Selected player is not available for the current monitor
selectPlayers.value = ''; // Auto
}
this.player = selectPlayers.value;
};

this.element = null;
this.getElement = function() {
if (this.element) return this.element;
Expand Down Expand Up @@ -263,7 +304,14 @@ function MonitorStream(monitorData) {
}
}; // setStreamScale

/*
* streamChannel = 0 || Primary; 1 || Secondary.
*/
this.start = function(streamChannel = 'default') {
if (streamChannel === null || streamChannel === '' || currentView == 'montage') streamChannel = 'default';
if (!['default', 0, 1].includes(streamChannel)) {
streamChannel = (streamChannel.toLowerCase() == 'primary') ? 0 : 1;
}
this.streamListenerBind = streamListener.bind(null, this);

console.log('start', this.Go2RTCEnabled, (!this.player), (-1 != this.player.indexOf('go2rtc')), ((!this.player) || (-1 != this.player.indexOf('go2rtc'))));
Expand All @@ -281,7 +329,7 @@ function MonitorStream(monitorData) {
stream.background = true; // We do not use the document hiding/showing analysis from "video-rtc.js", because we have our own analysis
const Go2RTCModUrl = url;
const webrtcUrl = Go2RTCModUrl;
this.currentChannelStream = (streamChannel == 'default') ? 0 : streamChannel;
this.currentChannelStream = (streamChannel == 'default') ? ((this.RTSP2WebStream == 'Secondary') ? 1 : 0) : streamChannel;
webrtcUrl.protocol = (url.protocol=='https:') ? 'wss:' : 'ws';
webrtcUrl.pathname += "/ws";
//webrtcUrl.search = 'src='+this.id;
Expand All @@ -304,6 +352,7 @@ function MonitorStream(monitorData) {

$j('#volumeControls').show();
if (typeof observerMontage !== 'undefined') observerMontage.observe(stream);
this.activePlayer = 'go2rtc';
return;
} else {
alert("ZM_GO2RTC_PATH is empty. Go to Options->System and set ZM_GO2RTC_PATH accordingly.");
Expand Down Expand Up @@ -332,6 +381,7 @@ function MonitorStream(monitorData) {
this.statusCmdTimer = setInterval(this.statusCmdQuery.bind(this), statusRefreshTimeout);
this.started = true;
this.streamListenerBind();
this.activePlayer = 'janus';
return;
}

Expand Down Expand Up @@ -376,16 +426,19 @@ function MonitorStream(monitorData) {
} else if (stream.canPlayType('application/vnd.apple.mpegurl')) {
stream.src = hlsUrl.href;
}
this.activePlayer = 'rtsp2web_hls';
} else if (this.RTSP2WebType == 'MSE') {
const mseUrl = rtsp2webModUrl;
mseUrl.protocol = useSSL ? 'wss' : 'ws';
mseUrl.pathname = "/stream/" + this.id + "/channel/" + this.currentChannelStream + "/mse";
mseUrl.search = "uuid=" + this.id + "&channel=" + this.currentChannelStream + "";
startMsePlay(this, stream, mseUrl.href);
this.activePlayer = 'rtsp2web_mse';
} else if (this.RTSP2WebType == 'WebRTC') {
const webrtcUrl = rtsp2webModUrl;
webrtcUrl.pathname = "/stream/" + this.id + "/channel/" + this.currentChannelStream + "/webrtc";
startRTSP2WebPlay(stream, webrtcUrl.href, this);
this.activePlayer = 'rtsp2web_webrtc';
}
clearInterval(this.statusCmdTimer); // Fix for issues in Chromium when quickly hiding/showing a page. Doesn't clear statusCmdTimer when minimizing a page https://stackoverflow.com/questions/9501813/clearinterval-not-working
this.statusCmdTimer = setInterval(this.statusCmdQuery.bind(this), statusRefreshTimeout);
Expand Down Expand Up @@ -435,6 +488,7 @@ function MonitorStream(monitorData) {
stream.onload = this.img_onload.bind(this);
this.started = true;
this.streamListenerBind();
this.activePlayer = 'zms';
}; // this.start

this.stop = function() {
Expand Down
1 change: 1 addition & 0 deletions web/lang/en_gb.php
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@
'Pixels' => 'pixels',
'PlayAll' => 'Play All',
'PlayCycle' => 'Play Cycle',
'PlayerDisabledInMonitorSettings' => 'The player is disabled in the monitor settings.',
'PleaseWait' => 'Please Wait',
'PostEventImageBuffer' => 'Post Event Image Count',
'PreEventImageBuffer' => 'Pre Event Image Count',
Expand Down
3 changes: 2 additions & 1 deletion web/lang/ru_ru.php
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@
'ZoomIn' => 'Приблизить',
'ZoomOut' => 'Отдалить',
// *********07-2022************************************
'Storage' => 'Хранилище',
'Storage' => 'Хранилище',
'Back' => 'Вернуться',
'ParentGroup' => 'Родительская группа',
'FilterUnarchiveEvents' => 'Разархивировать все совпадения',
Expand All @@ -847,6 +847,7 @@
'PreviousMonitor' => 'Предыдущий монитор',
'PauseCycle' => 'Приостановить цикл',
'PlayCycle' => 'Запуск цикла',
'PlayerDisabledInMonitorSettings' => 'Этот плеер отключен в настройках монитора.',
'NextMonitor' => 'Следующий монитор',
'Server' => 'Сервер',
'Servers' => 'Сервера',
Expand Down
4 changes: 2 additions & 2 deletions web/skins/classic/js/skin.js
Original file line number Diff line number Diff line change
Expand Up @@ -1081,7 +1081,7 @@ function manageShutdownBtns(element) {
}

/* Controls the availability of options for selection*/
function manageRTSP2WebChannelStream() {
function manageChannelStream() {
let select = null;
let secondPath_ = null;
if (currentView == 'watch') {
Expand Down Expand Up @@ -1721,7 +1721,7 @@ function findPos(obj, foundScrollLeft, foundScrollTop) {
/* Handling <input> change */
function handleChangeInputTag(evt) {
// Managing availability of channel stream selection
manageRTSP2WebChannelStream();
manageChannelStream();
}

/* Handling a mouse click */
Expand Down
1 change: 1 addition & 0 deletions web/skins/classic/js/skin.js.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
const exportFailedString = '<?php echo translate('ExportFailed') ?>';
const exportSucceededString = '<?php echo translate('ExportSucceeded') ?>';
const cancelString = '<?php echo translate('Cancel') ?>';
const playerDisabledInMonitorSettings = '<?php echo translate('PlayerDisabledInMonitorSettings') ?>';
<?php
/* We can't trust PHP_SELF on a path like /index.php/"%3E%3Cimg src=x onerror=prompt('1');%3E which
will still load index.php but will include the arbitrary payload after `.php/`. To mitigate this,
Expand Down
29 changes: 23 additions & 6 deletions web/skins/classic/views/js/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,22 +376,39 @@ function initPage() {

//Manage the RTSP2Web settings div
const RTSP2WebEnabled = form.elements['newMonitor[RTSP2WebEnabled]'];
if (RTSP2WebEnabled) {
const Go2RTCEnabled = form.elements['newMonitor[Go2RTCEnabled]'];
if (RTSP2WebEnabled || Go2RTCEnabled) {
if (Go2RTCEnabled.checked || RTSP2WebEnabled.checked) {
document.getElementById("RTSP2WebStream").hidden = false;
} else {
document.getElementById("RTSP2WebStream").hidden = true;
}

if (RTSP2WebEnabled.checked) {
document.getElementById("RTSP2WebType").hidden = false;
document.getElementById("RTSP2WebStream").hidden = false;
} else {
document.getElementById("RTSP2WebType").hidden = true;
document.getElementById("RTSP2WebStream").hidden = true;
}

Go2RTCEnabled.addEventListener('change', function() {
if (this.checked || RTSP2WebEnabled.checked) {
document.getElementById("RTSP2WebStream").hidden = false;
} else {
document.getElementById("RTSP2WebStream").hidden = true;
}
});

RTSP2WebEnabled.addEventListener('change', function() {
if (this.checked || Go2RTCEnabled.checked) {
document.getElementById("RTSP2WebStream").hidden = false;
} else {
document.getElementById("RTSP2WebStream").hidden = true;
}

if (this.checked) {
document.getElementById("RTSP2WebType").hidden = false;
document.getElementById("RTSP2WebStream").hidden = false;
} else {
document.getElementById("RTSP2WebType").hidden = true;
document.getElementById("RTSP2WebStream").hidden = true;
}
});
}
Expand Down Expand Up @@ -480,7 +497,7 @@ function initPage() {
// Setup the thumbnail video animation
if (!isMobile()) initThumbAnimation();

manageRTSP2WebChannelStream();
manageChannelStream();
} // end function initPage()

function saveMonitorData(href = '') {
Expand Down
78 changes: 52 additions & 26 deletions web/skins/classic/views/js/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -834,12 +834,14 @@ function streamStart(monitor = null) {
monitorStream.setup_volume(document.getElementById('volume'));
monitorStream.setup_mute(document.getElementById('mute'));

monitorStream.setPlayer(player);
monitorStream.setPlayer($j('#player').val());
monitorStream.setBottomElement(document.getElementById('dvrControls'));
monitorStream.manageAvailablePlayers();
setChannelStream();
// Start the fps and status updates. give a random delay so that we don't assault the server
//monitorStream.setScale($j('#scale').val(), $j('#width').val(), $j('#height').val());
//monitorsSetScale(monitorId);
monitorStream.start();
monitorStream.start(document.getElementById('streamChannel').value);
if (streamMode == 'single') {
monitorStream.setup_onclick(fetchImage);
} else {
Expand All @@ -861,21 +863,7 @@ function streamStart(monitor = null) {
forceAlmBtn.prop('title', forceAlmBtn.prop('title') + ': disabled because cannot edit Monitors');
enableAlmBtn.prop('title', enableAlmBtn.prop('title') + ': disabled because cannot edit Monitors');
}

// Managing the visibility of elements
const streamChannel = document.getElementById('streamChannel');
const streamQuality = document.getElementById('streamQuality');
const rateControl = document.getElementById('rateControl');
if (currentMonitor.RTSP2WebEnabled) {
streamChannel.classList.remove("hidden-shift");
streamQuality.classList.add("hidden-shift");
streamChannel.value = currentMonitor.RTSP2WebStream;
rateControl.classList.add("hidden-shift");
} else {
streamQuality.classList.remove("hidden-shift");
streamChannel.classList.add("hidden-shift");
rateControl.classList.remove("hidden-shift");
}
manageStreamQualityVisibility(); // In order for the Auto mode AFTER the start to register the player value .activePlayer and based on this we select the correct "Stream quality", i.e. either the quality for ZMS, or the channel for go2rtc/RTSP2Web
}

function streamReStart(oldId, newId) {
Expand Down Expand Up @@ -917,13 +905,47 @@ function streamReStart(oldId, newId) {

table.bootstrapTable('destroy');
applyMonitorControllable();
//manageChannelStream();
streamPrepareStart(currentMonitor);
zmPanZoom.init();
zmPanZoom.init({objString: '.imageFeed', disablePan: true, contain: 'inside', additional: true});
manageRTSP2WebChannelStream();
//document.getElementById('monitor').classList.remove('hidden-shift');
}

function setChannelStream() {
const streamChannel = document.getElementById('streamChannel');
manageChannelStream();

if (-1 === monitorStream.activePlayer.indexOf('zms')) {
let streamChannelValue = (getCookie('zmStreamChannel') || currentMonitor.RTSP2WebStream && currentMonitor.SecondPath);
// When switching monitors, cookies may store a channel from the previous monitor that the current monitor does not have.
streamChannel.value = streamChannelValue; // This will be easier than checking for a disabled option by going through the options. That is, we set the required option and if it is disabled, then we select the 'Primary' channel

if (streamChannel.options[streamChannel.selectedIndex].disabled) {
streamChannelValue = 'Primary';
}
monitorStream.currentChannelStream = (streamChannelValue == 'Secondary') ? 1 : 0;
streamChannel.value = streamChannelValue;
}
}

function manageStreamQualityVisibility() {
const streamQuality = document.getElementById('streamQuality');
const streamChannel = document.getElementById('streamChannel');
const rateControl = document.getElementById('rateControl');

if (-1 === monitorStream.activePlayer.indexOf('zms')) {
streamChannel.classList.remove("hidden-shift");
streamQuality.classList.add("hidden-shift");
rateControl.classList.add("hidden-shift");
} else {
streamQuality.classList.remove("hidden-shift");
streamChannel.classList.add("hidden-shift");
rateControl.classList.remove("hidden-shift");
}
applyChosen();
}

function applyMonitorControllable() {
const ptzToggle = document.getElementById('ptzToggle');
if (!ptzToggle) {
Expand Down Expand Up @@ -1088,9 +1110,6 @@ function initPage() {
} else {
alert("No monitor found for id "+monitorId);
}


manageRTSP2WebChannelStream();
} // initPage

function watchFullscreen() {
Expand Down Expand Up @@ -1291,10 +1310,13 @@ function panZoomEventPanzoomchange(event) {
}

function monitorChangeStreamChannel() {
if (currentMonitor.RTSP2WebEnabled) {
//if (currentMonitor.RTSP2WebEnabled) {
if ((monitorStream.player) && (-1 !== monitorStream.player.indexOf('go2rtc') || -1 !== monitorStream.player.indexOf('rtsp2web'))) {
streamCmdStop(true);
const streamChannel = $j('#streamChannel').val();
setCookie('zmStreamChannel', streamChannel);
setTimeout(function() {
monitorStream.start(($j('#streamChannel').val() == "Primary") ? 0 : 1);
monitorStream.start(streamChannel);
onPlay();
}, 300);
}
Expand All @@ -1307,13 +1329,17 @@ function changePlayer() {
streamCmdStop(true); // takes care of button state and calls stream.kill()
console.log('setting to ', $j('#player').val());
monitorStream.setPlayer($j('#player').val());
streamCmdPlay(true);
return;
setChannelStream();
setTimeout(function() {
streamCmdPlay(true);
manageStreamQualityVisibility();
}, 300);
/*return;

setTimeout(function() {
monitorStream.start();
onPlay();
}, 300);
}, 300);*/
}

function monitorsSetScale(id=null) {
Expand Down
Loading