From 830929a85fda8de888b86567462e7b04db1b3ec0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 10:06:41 -0400 Subject: [PATCH 01/32] Remove extra value='s from PTZ buttons. Fix remaining on-click-this on numbered presets --- web/skins/classic/includes/control_functions.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 6f1399786..58b2853c5 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -55,8 +55,8 @@ function controlZoom($monitor, $cmds) { CanAutoZoom() ) { ?> - - + + @@ -77,8 +77,8 @@ function controlIris($monitor, $cmds) { CanAutoIris() ) { ?> - - + + @@ -100,8 +100,8 @@ function controlWhite($monitor, $cmds) { CanAutoWhite() ) { ?> - - + + @@ -162,7 +162,7 @@ function controlPresets($monitor, $cmds) { NumPresets(); $i++ ) { ?> - + @@ -223,9 +223,7 @@ function controlPower($monitor, $cmds) { function ptzControls($monitor) { $control = $monitor->Control(); - //ZM\Error("Control: " . print_r($control,true)); $cmds = $control->commands(); - //ZM\Error("Cmds: " . print_r($cmds, true)); ob_start(); ?>
From 62ddc02d52170c83c5b8ebeee8d063121fdb5a90 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 13:01:34 -0400 Subject: [PATCH 02/32] quotes to match master --- web/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.php b/web/index.php index 18da56e63..29316efc3 100644 --- a/web/index.php +++ b/web/index.php @@ -71,7 +71,7 @@ define('ZM_BASE_URL', ''); require_once('includes/functions.php'); if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) { - ZM\Logger::Debug("OPTIONS Method, only doing CORS"); + ZM\Logger::Debug('OPTIONS Method, only doing CORS'); # Add Cross domain access headers CORSHeaders(); return; From daa08e874068b01de5c88530be45a9fdca7cbdaa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:35:20 -0400 Subject: [PATCH 03/32] Always just default to scale from monitor --- web/skins/classic/views/watch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index fa540557e..bcf40d855 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -46,7 +46,7 @@ if ( isset($_REQUEST['scale']) ) { } else if ( isset($_COOKIE['zmWatchScale'.$mid]) ) { $scale = $_COOKIE['zmWatchScale'.$mid]; } else { - $scale = reScale(SCALE_BASE, $monitor->DefaultScale(), ZM_WEB_DEFAULT_SCALE); + $scale = $monitor->DefaultScale(); } $connkey = generateConnKey(); From 289045500b7ff580c18023c03d1ba785cb01c1ff Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:35:04 -0400 Subject: [PATCH 04/32] Don't use event as a variable name unless we are talking about window evnt. Change controlCmd to take the event and get the button from event.target --- web/skins/classic/views/js/watch.js | 91 ++++++++++++++++++----------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index e8977ddd6..633d507a5 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -546,42 +546,62 @@ function getEventCmdResponse( respObj, respText ) { } ); for ( var i = 0; i < dbEvents.length; i++ ) { - var event = dbEvents[i]; - var row = $('event'+event.Id); + var zm_event = dbEvents[i]; + var row = $('event'+zm_event.Id); var newEvent = (row == null ? true : false); if ( newEvent ) { - row = new Element( 'tr', {'id': 'event'+event.Id} ); - new Element( 'td', {'class': 'colId'} ).inject( row ); - new Element( 'td', {'class': 'colName'} ).inject( row ); - new Element( 'td', {'class': 'colTime'} ).inject( row ); - new Element( 'td', {'class': 'colSecs'} ).inject( row ); - new Element( 'td', {'class': 'colFrames'} ).inject( row ); - new Element( 'td', {'class': 'colScore'} ).inject( row ); - new Element( 'td', {'class': 'colDelete'} ).inject( row ); + row = new Element('tr', {'id': 'event'+zm_event.Id}); + new Element('td', {'class': 'colId'}).inject(row); + new Element('td', {'class': 'colName'}).inject(row); + new Element('td', {'class': 'colTime'}).inject(row); + new Element('td', {'class': 'colSecs'}).inject(row); + new Element('td', {'class': 'colFrames'}).inject(row); + new Element('td', {'class': 'colScore'}).inject(row); + new Element('td', {'class': 'colDelete'}).inject(row); - var link = new Element( 'a', {'href': '#', 'events': {'click': createEventPopup.pass( [event.Id, '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', event.Width, event.Height] )}}); - link.set( 'text', event.Id ); - link.inject( row.getElement( 'td.colId' ) ); + var link = new Element('a', { + 'href': '#', + 'events': { + 'click': createEventPopup.pass( [ + zm_event.Id, + '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', + zm_event.Width, + zm_event.Height + ] ) + } + }); + link.set('text', zm_event.Id); + link.inject(row.getElement('td.colId')); - link = new Element( 'a', {'href': '#', 'events': {'click': createEventPopup.pass( [event.Id, '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', event.Width, event.Height] )}}); - link.set( 'text', event.Name ); - link.inject( row.getElement( 'td.colName' ) ); + link = new Element('a', { + 'href': '#', + 'events': { + 'click': createEventPopup.pass( [ + zm_event.Id, + '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', + zm_event.Width, + zm_event.Height + ] ) + } + }); + link.set('text', zm_event.Name); + link.inject(row.getElement('td.colName')); - row.getElement( 'td.colTime' ).set( 'text', event.StartTime ); - row.getElement( 'td.colSecs' ).set( 'text', event.Length ); + row.getElement('td.colTime').set('text', zm_event.StartTime); + row.getElement('td.colSecs').set('text', zm_event.Length); - link = new Element( 'a', {'href': '#', 'events': {'click': createFramesPopup.pass( [event.Id, event.Width, event.Height] )}}); - link.set( 'text', event.Frames+'/'+event.AlarmFrames ); - link.inject( row.getElement( 'td.colFrames' ) ); + link = new Element('a', {'href': '#', 'events': {'click': createFramesPopup.pass( [zm_event.Id, zm_event.Width, zm_event.Height] )}}); + link.set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); + link.inject(row.getElement('td.colFrames')); - link = new Element( 'a', {'href': '#', 'events': {'click': createFramePopup.pass( [event.Id, '0', event.Width, event.Height] )}}); - link.set( 'text', event.AvgScore+'/'+event.MaxScore ); - link.inject( row.getElement( 'td.colScore' ) ); + link = new Element('a', {'href': '#', 'events': {'click': createFramePopup.pass( [zm_event.Id, '0', zm_event.Width, zm_event.Height] )}}); + link.set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); + link.inject(row.getElement('td.colScore')); - link = new Element( 'button', { + link = new Element('button', { 'type': 'button', 'title': deleteString, - 'data-event-id': event.Id, + 'data-event-id': zm_event.Id, 'events': { 'click': function(e) { var event_id = e.target.getAttribute('data-event-id'); @@ -608,14 +628,14 @@ function getEventCmdResponse( respObj, respText ) { } } } else { - row.getElement('td.colName a').set('text', event.Name); - row.getElement('td.colSecs').set('text', event.Length); - row.getElement('td.colFrames a').set('text', event.Frames+'/'+event.AlarmFrames); - row.getElement('td.colScore a').set('text', event.AvgScore+'/'+event.MaxScore); + row.getElement('td.colName a').set('text', zm_event.Name); + row.getElement('td.colSecs').set('text', zm_event.Length); + row.getElement('td.colFrames a').set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); + row.getElement('td.colScore a').set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); row.removeClass('recent'); } row.addClass('updated'); - } + } // end foreach event var rows = $(eventListBody).getElements('tr'); for ( var i = 0; i < rows.length; i++ ) { @@ -631,7 +651,7 @@ function getEventCmdResponse( respObj, respText ) { } } else { checkStreamForErrors('getEventCmdResponse', respObj); - } + } // end if objresult == ok var eventCmdTimeout = eventsRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { @@ -672,7 +692,8 @@ function getControlResponse(respObj, respText) { } } -function controlCmd(button) { +function controlCmd(event) { + button = event.target; control = button.getAttribute('value'); xtell = button.getAttribute('xtell'); ytell = button.getAttribute('ytell'); @@ -687,7 +708,7 @@ function controlCmd(button) { var y = event.pageY - coords.top; if ( xtell ) { - var xge = parseInt( (x*100)/coords.width ); + var xge = parseInt((x*100)/coords.width); if ( xtell == -1 ) { xge = 100 - xge; } else if ( xtell == 2 ) { @@ -696,7 +717,7 @@ function controlCmd(button) { locParms += '&xge='+xge; } if ( ytell ) { - var yge = parseInt( (y*100)/coords.height ); + var yge = parseInt((y*100)/coords.height); if ( ytell == -1 ) { yge = 100 - yge; } else if ( ytell == 2 ) { From 4c9f67eb5a4127b16f031ae5b24bc55e350a0461 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:34:27 -0400 Subject: [PATCH 05/32] Fix no redirect on login with palemoon browser --- web/skins/classic/views/js/postlogin.js.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/postlogin.js.php b/web/skins/classic/views/js/postlogin.js.php index 75c241a91..a062b4c52 100644 --- a/web/skins/classic/views/js/postlogin.js.php +++ b/web/skins/classic/views/js/postlogin.js.php @@ -21,13 +21,13 @@ } ?>'; - if ( querySuffix == '?view=login' ) { + if ( querySuffix == '?view=login' || querySuffix == '' ) { // If we didn't redirect elsewhere, then don't show login page, go to console querySuffix = '?view=console'; } var newUrl = querySuffix; - -console.log("Redirecting to" + newUrl + ' ' + thisUrl); +console.log("Current location: " + window.location); +console.log("Redirecting to (" + newUrl + ') from :' + thisUrl); window.location.replace(newUrl); } ).delay( 500 ); From 2da88be307df2fcbbc85e3b9443dd4222b84837e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:34:07 -0400 Subject: [PATCH 06/32] Use auth_relay instead of auth_hash if AUTH_RELAY==plain or none. --- web/skins/classic/views/js/montage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index b88833db0..d0e0c348c 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -13,6 +13,8 @@ function Monitor(monitorData) { this.streamCmdParms = 'view=request&request=stream&connkey='+this.connKey; if ( auth_hash ) { this.streamCmdParms += '&auth='+auth_hash; + } else if ( auth_relay ) { + this.streamCmdParms += '&'+auth_relay; } this.streamCmdTimer = null; this.type = monitorData.type; From 89245e0d46aca69ae989df1f41c920f9d4d164ec Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:33:41 -0400 Subject: [PATCH 07/32] Add auth relay information when auth_relay is something other than hashed. --- web/skins/classic/js/skin.js.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index a9f6b85af..5250e654f 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -71,6 +71,22 @@ var focusWindow = ; var imagePrefix = ""; var auth_hash; - -auth_hash = ''; - +var auth_relay; + From e29c426f04b803f1b96a5d5cf59242885714b123 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:32:55 -0400 Subject: [PATCH 08/32] Pass event object to data-on-click events --- web/skins/classic/js/skin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index dd965a39f..2012837e8 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -182,8 +182,8 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { return; } - el.onclick = function() { - window[fnName](); + el.onclick = function(ev) { + window[fnName](ev); }; }); From bd7475a450dd6dde63715bf0cedd7f7416c7b3d8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:32:26 -0400 Subject: [PATCH 09/32] use on-click instead of on-click-this. We can get the button from event.target. The palemoon browser doesn't have a global event object so this wasn't working --- .../classic/includes/control_functions.php | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 58b2853c5..ac66c6f3f 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -25,15 +25,15 @@ function controlFocus($monitor, $cmds) { ?>
- - - + + +
CanAutoFocus() ) { ?> - - + + @@ -48,15 +48,15 @@ function controlZoom($monitor, $cmds) { ?>
- - - + + +
CanAutoZoom() ) { ?> - - + + @@ -70,15 +70,15 @@ function controlIris($monitor, $cmds) { ?>
- - - + + +
CanAutoIris() ) { ?> - - + + @@ -93,15 +93,15 @@ function controlWhite($monitor, $cmds) { ?>
- - - + + +
CanAutoWhite() ) { ?> - - + + @@ -122,19 +122,19 @@ function controlPanTilt($monitor, $cmds) { $hasTilt = $control->CanTilt(); $hasDiag = $hasPan && $hasTilt && $control->CanMoveDiag(); ?> - - - - + + + + - + - - - - + + + +
NumPresets(); $i++ ) { ?> - + @@ -171,7 +171,7 @@ function controlPresets($monitor, $cmds) { HasHomePreset() ) { ?> - + CanSetPresets() ) { @@ -196,22 +196,22 @@ function controlPower($monitor, $cmds) { CanWake() ) { ?> - + CanSleep() ) { ?> - + CanReset() ) { ?> - + CanReboot() ) { ?> - + From e960e638b33197e462737dc1c23f7b7fe5a79e41 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 10:06:41 -0400 Subject: [PATCH 10/32] Remove extra value='s from PTZ buttons. Fix remaining on-click-this on numbered presets --- web/skins/classic/includes/control_functions.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index ac66c6f3f..61138612f 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -55,8 +55,8 @@ function controlZoom($monitor, $cmds) { CanAutoZoom() ) { ?> - - + + @@ -77,8 +77,8 @@ function controlIris($monitor, $cmds) { CanAutoIris() ) { ?> - - + + @@ -100,8 +100,8 @@ function controlWhite($monitor, $cmds) { CanAutoWhite() ) { ?> - - + + @@ -162,7 +162,7 @@ function controlPresets($monitor, $cmds) { NumPresets(); $i++ ) { ?> - + From ad373a5549d726f62c7b9cec59b4effe226713c0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 13:20:42 -0400 Subject: [PATCH 11/32] add Zone.php from master --- web/includes/Zone.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 web/includes/Zone.php diff --git a/web/includes/Zone.php b/web/includes/Zone.php new file mode 100644 index 000000000..7a19df9d2 --- /dev/null +++ b/web/includes/Zone.php @@ -0,0 +1,41 @@ + null, + 'Name' => '', + 'Type' => 'Active', + 'Units' => 'Pixels', + 'CheckMethod' => 'Blobs', + 'MinPixelThreshold' => null, + 'MaxPixelThreshold' => null, + 'MinAlarmPixels' => null, + 'MaxAlarmPixels' => null, + 'FilterX' => null, + 'FilterY' => null, + 'MinFilterPixels' => null, + 'MaxFilterPixels' => null, + 'MinBlobPixels' => null, + 'MaxBlobPixels' => null, + 'MinBlobs' => null, + 'MaxBlobs' => null, + 'OverloadFrames' => 0, + 'ExtendAlarmFrames' => 0, + ); + + public static function find( $parameters = array(), $options = array() ) { + return ZM_Object::_find(get_class(), $parameters, $options); + } + + public static function find_one( $parameters = array(), $options = array() ) { + return ZM_Object::_find_one(get_class(), $parameters, $options); + } + +} # end class Zone +?> From 45a445ac485199a0f1a952acf86b044d24252b72 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 13:21:02 -0400 Subject: [PATCH 12/32] sync up index.php to master, where is a global --- web/index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/index.php b/web/index.php index 29316efc3..b29a4e7e7 100644 --- a/web/index.php +++ b/web/index.php @@ -52,6 +52,9 @@ require_once('includes/Event.php'); require_once('includes/Group.php'); require_once('includes/Monitor.php'); +global $Servers; +$Servers = ZM\Server::find(); + if ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') or @@ -152,8 +155,6 @@ if ( setcookie('zmCSS', $css, time()+3600*24*30*12*10); } - - # Running is global but only do the daemonCheck if it is actually needed $running = null; @@ -174,7 +175,6 @@ $user = null; if ( isset($_REQUEST['view']) ) $view = detaintPath($_REQUEST['view']); - # Add CSP Headers $cspNonce = bin2hex(zm_random_bytes(16)); From 82188c266edd6e21dceba649096eba672da0e7be Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 29 Apr 2020 16:30:59 -0400 Subject: [PATCH 13/32] Fixup Monitor->sendControlCommand. start/stop/quit should be run through zmdc.pl now instead of talking directly to zmcontrol. --- web/includes/Monitor.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index c788b5fb8..f9341c71a 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -495,12 +495,16 @@ class Monitor extends ZM_Object { } } if ( !count($options) ) { - if ( $command == 'quit' ) { - $options['command'] = 'quit'; - } else if ( $command == 'start' ) { - $options['command'] = 'start'; - } else if ( $command == 'stop' ) { - $options['command'] = 'stop'; + + if ( $command == 'quit' or $command == 'start' or $command == 'stop' ) { + # These are special as we now run zmcontrol as a daemon through zmdc. + $status = daemonStatus('zmcontrol.pl', array('--id', $this->{'Id'})); + Logger::Debug("Current status $status"); + if ( $status or ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) ) { + daemonControl($command, 'zmcontrol.pl', '--id '.$this->{'Id'}); + return; + } + $options['command'] = $command; } else { Warning("No commands to send to zmcontrol from $command"); return false; @@ -538,12 +542,12 @@ class Monitor extends ZM_Object { $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { - $url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS ); + $url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS); } else if ( ZM_AUTH_RELAY == 'plain' ) { - $url = '?user='.$_SESSION['username']; - $url = '?pass='.$_SESSION['password']; + $url .= '?user='.$_SESSION['username']; + $url .= '?pass='.$_SESSION['password']; } else if ( ZM_AUTH_RELAY == 'none' ) { - $url = '?user='.$_SESSION['username']; + $url .= '?user='.$_SESSION['username']; } } Logger::Debug("sending command to $url"); From 6dbc91721556d68c7478305abe981294cad89aab Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 24 Apr 2020 12:23:48 -0400 Subject: [PATCH 14/32] Don't include mootools on login view. It isn't needed and apparently breaks google recaptcha --- web/skins/classic/includes/functions.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index a5e000d3d..e0d919325 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -117,9 +117,11 @@ if ( $css != 'base' ) + + From 1bd340d602c4454b8d18d8190e8540a20f635d04 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 10 Apr 2020 11:16:11 -0400 Subject: [PATCH 15/32] spacing in watch.php --- web/skins/classic/views/watch.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index bcf40d855..caeb5f95e 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -1,6 +1,6 @@ Name().' - '.translate('Feed')); ?> From ffcef8a42ac10d9daa85b8a050e7107e3ba1aaf3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 15:22:32 -0400 Subject: [PATCH 16/32] spacing and make sws_context static global so we don't have to keep initializing it when playing back from mp4 in eventstream. --- src/zm_image.cpp | 91 +++++++++++++++++++++++++++--------------------- src/zm_image.h | 2 +- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 97c2132c6..31c71037f 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -48,6 +48,8 @@ static short *g_v_table; static short *g_u_table; static short *b_u_table; +struct SwsContext *sws_convert_context = NULL; + jpeg_compress_struct *Image::writejpg_ccinfo[101] = { 0 }; jpeg_compress_struct *Image::encodejpg_ccinfo[101] = { 0 }; jpeg_decompress_struct *Image::readjpg_dcinfo = 0; @@ -160,15 +162,17 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin update_function_pointers(); } -Image::Image( const AVFrame *frame ) { +Image::Image(const AVFrame *frame) { AVFrame *dest_frame = zm_av_frame_alloc(); text[0] = '\0'; width = frame->width; height = frame->height; pixels = width*height; + colours = ZM_COLOUR_RGB32; subpixelorder = ZM_SUBPIX_ORDER_RGBA; + size = pixels*colours; buffer = 0; holdbuffer = 0; @@ -183,26 +187,28 @@ Image::Image( const AVFrame *frame ) { #endif #if HAVE_LIBSWSCALE - struct SwsContext *mConvertContext = sws_getContext( + sws_convert_context = sws_getCachedContext( + sws_convert_context, width, height, (AVPixelFormat)frame->format, width, height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); - if ( mConvertContext == NULL ) - Fatal( "Unable to create conversion context" ); + if ( sws_convert_context == NULL ) + Fatal("Unable to create conversion context"); - if ( sws_scale(mConvertContext, frame->data, frame->linesize, 0, frame->height, dest_frame->data, dest_frame->linesize) < 0 ) + if ( sws_scale(sws_convert_context, frame->data, frame->linesize, 0, frame->height, + dest_frame->data, dest_frame->linesize) < 0 ) Fatal("Unable to convert raw format %u to target format %u", frame->format, AV_PIX_FMT_RGBA); #else // HAVE_LIBSWSCALE Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); #endif // HAVE_LIBSWSCALE - av_frame_free( &dest_frame ); + av_frame_free(&dest_frame); update_function_pointers(); -} +} // end Image::Image(const AVFrame *frame) -Image::Image( const Image &p_image ) { +Image::Image(const Image &p_image) { if ( !initialised ) Initialise(); width = p_image.width; @@ -215,7 +221,7 @@ Image::Image( const Image &p_image ) { holdbuffer = 0; AllocImgBuffer(size); (*fptr_imgbufcpy)(buffer, p_image.buffer, size); - strncpy( text, p_image.text, sizeof(text) ); + strncpy(text, p_image.text, sizeof(text)); update_function_pointers(); } @@ -225,35 +231,39 @@ Image::~Image() { /* Should be called as part of program shutdown to free everything */ void Image::Deinitialise() { - if ( initialised ) { - /* - delete[] y_table; - delete[] uv_table; - delete[] r_v_table; - delete[] g_v_table; - delete[] g_u_table; - delete[] b_u_table; + if ( !initialised ) return; + /* + delete[] y_table; + delete[] uv_table; + delete[] r_v_table; + delete[] g_v_table; + delete[] g_u_table; + delete[] b_u_table; */ - initialised = false; - if ( readjpg_dcinfo ) { - jpeg_destroy_decompress( readjpg_dcinfo ); - delete readjpg_dcinfo; - readjpg_dcinfo = 0; - } - if ( decodejpg_dcinfo ) { - jpeg_destroy_decompress( decodejpg_dcinfo ); - delete decodejpg_dcinfo; - decodejpg_dcinfo = 0; - } - for ( unsigned int quality=0; quality <= 100; quality += 1 ) { - if ( writejpg_ccinfo[quality] ) { - jpeg_destroy_compress( writejpg_ccinfo[quality] ); - delete writejpg_ccinfo[quality]; - writejpg_ccinfo[quality] = NULL; - } - } // end foreach quality + initialised = false; + if ( readjpg_dcinfo ) { + jpeg_destroy_decompress( readjpg_dcinfo ); + delete readjpg_dcinfo; + readjpg_dcinfo = 0; } -} + if ( decodejpg_dcinfo ) { + jpeg_destroy_decompress( decodejpg_dcinfo ); + delete decodejpg_dcinfo; + decodejpg_dcinfo = 0; + } + for ( unsigned int quality=0; quality <= 100; quality += 1 ) { + if ( writejpg_ccinfo[quality] ) { + jpeg_destroy_compress( writejpg_ccinfo[quality] ); + delete writejpg_ccinfo[quality]; + writejpg_ccinfo[quality] = NULL; + } + } // end foreach quality + + if ( sws_convert_context ) { + sws_freeContext(sws_convert_context); + sws_convert_context = NULL; + } +} // end void Image::Deinitialise() void Image::Initialise() { /* Assign the blend pointer to function */ @@ -655,15 +665,16 @@ void Image::Assign( const Image &image ) { return; } - if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder) { + if ( !buffer || image.width != width || image.height != height + || image.colours != colours || image.subpixelorder != subpixelorder) { - if (holdbuffer && buffer) { - if (new_size > allocation) { + if ( holdbuffer && buffer ) { + if ( new_size > allocation ) { Error("Held buffer is undersized for assigned buffer"); return; } } else { - if(new_size > allocation || !buffer) { + if ( new_size > allocation || !buffer) { // DumpImgBuffer(); This is also done in AllocImgBuffer AllocImgBuffer(new_size); } diff --git a/src/zm_image.h b/src/zm_image.h index 6b2448c67..6be574dfb 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -121,7 +121,7 @@ protected: }; inline void DumpImgBuffer() { - DumpBuffer(buffer,buffertype); + DumpBuffer(buffer, buffertype); buffer = NULL; allocation = 0; } From 9dbeaa2c7bd8af3b12b81969f0689e6dfe55f967 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 15:32:26 -0400 Subject: [PATCH 17/32] Add default for Orientation. Fixes saving WebSite type monitors --- web/includes/Monitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index f9341c71a..066625c20 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -39,7 +39,7 @@ class Monitor extends ZM_Object { 'Height' => null, 'Colours' => 4, 'Palette' => '0', - 'Orientation' => null, + 'Orientation' => 'ROTATE_0', 'Deinterlacing' => 0, 'DecoderHWAccelName' => null, 'DecoderHWAccelDevice' => null, From 683bf3f9820c4424f030d943b507fde30757571f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 15:32:54 -0400 Subject: [PATCH 18/32] Handle no default set for array defaults. Fixes Warning when saving WebSite type Monitors --- web/includes/Object.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Object.php b/web/includes/Object.php index 1668a7767..09be86a9e 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -249,7 +249,7 @@ class ZM_Object { } } } else if ( array_key_exists($field, $this->defaults) ) { - if ( is_array($this->defaults[$field]) ) { + if ( is_array($this->defaults[$field]) and isset($this->defaults[$field]['default']) ) { $default = $this->defaults[$field]['default']; } else { $default = $this->defaults[$field]; From f96cc66b9d593f92186789795f3548eaf1d2cdb8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 14:14:38 -0500 Subject: [PATCH 19/32] google code style --- src/zm_box.h | 32 ++++++++++++++------------------ src/zm_coord.h | 6 ++---- src/zm_poly.cpp | 46 +++++++++++++++++++--------------------------- src/zm_poly.h | 34 +++++++++++++++++----------------- 4 files changed, 52 insertions(+), 66 deletions(-) diff --git a/src/zm_box.h b/src/zm_box.h index fa377e593..efc12cb18 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -33,37 +33,33 @@ // Class used for storing a box, which is defined as a region // defined by two coordinates // -class Box -{ +class Box { private: Coord lo, hi; Coord size; public: - inline Box() - { - } + inline Box() { } explicit inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { } inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { } inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { } inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { } - inline const Coord &Lo() const { return( lo ); } - inline int LoX() const { return( lo.X() ); } - inline int LoY() const { return( lo.Y() ); } - inline const Coord &Hi() const { return( hi ); } - inline int HiX() const { return( hi.X() ); } - inline int HiY() const { return( hi.Y() ); } - inline const Coord &Size() const { return( size ); } - inline int Width() const { return( size.X() ); } - inline int Height() const { return( size.Y() ); } - inline int Area() const { return( size.X()*size.Y() ); } + inline const Coord &Lo() const { return lo; } + inline int LoX() const { return lo.X(); } + inline int LoY() const { return lo.Y(); } + inline const Coord &Hi() const { return hi; } + inline int HiX() const { return hi.X(); } + inline int HiY() const { return hi.Y(); } + inline const Coord &Size() const { return size; } + inline int Width() const { return size.X(); } + inline int Height() const { return size.Y(); } + inline int Area() const { return size.X()*size.Y(); } - inline const Coord Centre() const - { + inline const Coord Centre() const { int mid_x = int(round(lo.X()+(size.X()/2.0))); int mid_y = int(round(lo.Y()+(size.Y()/2.0))); - return( Coord( mid_x, mid_y ) ); + return Coord( mid_x, mid_y ); } inline bool Inside( const Coord &coord ) const { diff --git a/src/zm_coord.h b/src/zm_coord.h index 9bf31144c..c6234fb1c 100644 --- a/src/zm_coord.h +++ b/src/zm_coord.h @@ -25,8 +25,7 @@ // // Class used for storing an x,y pair, i.e. a coordinate // -class Coord -{ +class Coord { private: int x, y; @@ -44,8 +43,7 @@ public: inline int &Y() { return( y ); } inline const int &Y() const { return( y ); } - inline static Coord Range( const Coord &coord1, const Coord &coord2 ) - { + inline static Coord Range( const Coord &coord1, const Coord &coord2 ) { Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 ); return( result ); } diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index 13a21e7e0..35319d6af 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -26,11 +26,9 @@ #include #endif -void Polygon::calcArea() -{ +void Polygon::calcArea() { double float_area = 0.0L; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) - { + for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L; float_area += trap_area; //printf( "%.2f (%.2f)\n", float_area, trap_area ); @@ -38,13 +36,11 @@ void Polygon::calcArea() area = (int)round(fabs(float_area)); } -void Polygon::calcCentre() -{ +void Polygon::calcCentre() { if ( !area && n_coords ) calcArea(); double float_x = 0.0L, float_y = 0.0L; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) - { + for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2))); float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2))); } @@ -54,16 +50,14 @@ void Polygon::calcCentre() centre = Coord( (int)round(float_x), (int)round(float_y) ); } -Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords ) -{ +Polygon::Polygon(int p_n_coords, const Coord *p_coords) : n_coords( p_n_coords ) { coords = new Coord[n_coords]; int min_x = -1; int max_x = -1; int min_y = -1; int max_y = -1; - for( int i = 0; i < n_coords; i++ ) - { + for ( int i = 0; i < n_coords; i++ ) { coords[i] = p_coords[i]; if ( min_x == -1 || coords[i].X() < min_x ) min_x = coords[i].X(); @@ -79,38 +73,36 @@ Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords calcCentre(); } -Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre ) +Polygon::Polygon( const Polygon &p_polygon ) : + n_coords(p_polygon.n_coords), + extent(p_polygon.extent), + area(p_polygon.area), + centre(p_polygon.centre) { coords = new Coord[n_coords]; - for( int i = 0; i < n_coords; i++ ) - { + for( int i = 0; i < n_coords; i++ ) { coords[i] = p_polygon.coords[i]; } } -Polygon &Polygon::operator=( const Polygon &p_polygon ) -{ - if ( n_coords < p_polygon.n_coords ) - { +Polygon &Polygon::operator=( const Polygon &p_polygon ) { + if ( n_coords < p_polygon.n_coords ) { delete[] coords; coords = new Coord[p_polygon.n_coords]; } n_coords = p_polygon.n_coords; - for( int i = 0; i < n_coords; i++ ) - { + for ( int i = 0; i < n_coords; i++ ) { coords[i] = p_polygon.coords[i]; } extent = p_polygon.extent; area = p_polygon.area; centre = p_polygon.centre; - return( *this ); + return *this ; } -bool Polygon::isInside( const Coord &coord ) const -{ +bool Polygon::isInside( const Coord &coord ) const { bool inside = false; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) - { + for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) || ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y()))) && (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X())) @@ -118,5 +110,5 @@ bool Polygon::isInside( const Coord &coord ) const inside = !inside; } } - return( inside ); + return inside; } diff --git a/src/zm_poly.h b/src/zm_poly.h index 0fcf34a6e..be039ef3e 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -41,13 +41,13 @@ protected: static int CompareYX( const void *p1, const void *p2 ) { const Edge *e1 = reinterpret_cast(p1), *e2 = reinterpret_cast(p2); if ( e1->min_y == e2->min_y ) - return( int(e1->min_x - e2->min_x) ); + return int(e1->min_x - e2->min_x); else - return( int(e1->min_y - e2->min_y) ); + return int(e1->min_y - e2->min_y); } static int CompareX( const void *p1, const void *p2 ) { const Edge *e1 = reinterpret_cast(p1), *e2 = reinterpret_cast(p2); - return( int(e1->min_x - e2->min_x) ); + return int(e1->min_x - e2->min_x); } }; @@ -83,32 +83,32 @@ protected: void calcCentre(); public: - inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 ), edges(0), slices(0) { + inline Polygon() : n_coords(0), coords(0), area(0), edges(0), slices(0) { } - Polygon( int p_n_coords, const Coord *p_coords ); - Polygon( const Polygon &p_polygon ); + Polygon(int p_n_coords, const Coord *p_coords); + Polygon(const Polygon &p_polygon); ~Polygon() { delete[] coords; } Polygon &operator=( const Polygon &p_polygon ); - inline int getNumCoords() const { return( n_coords ); } + inline int getNumCoords() const { return n_coords; } inline const Coord &getCoord( int index ) const { - return( coords[index] ); + return coords[index]; } - inline const Box &Extent() const { return( extent ); } - inline int LoX() const { return( extent.LoX() ); } - inline int HiX() const { return( extent.HiX() ); } - inline int LoY() const { return( extent.LoY() ); } - inline int HiY() const { return( extent.HiY() ); } - inline int Width() const { return( extent.Width() ); } - inline int Height() const { return( extent.Height() ); } + inline const Box &Extent() const { return extent; } + inline int LoX() const { return extent.LoX(); } + inline int HiX() const { return extent.HiX(); } + inline int LoY() const { return extent.LoY(); } + inline int HiY() const { return extent.HiY(); } + inline int Width() const { return extent.Width(); } + inline int Height() const { return extent.Height(); } - inline int Area() const { return( area ); } + inline int Area() const { return area; } inline const Coord &Centre() const { - return( centre ); + return centre; } bool isInside( const Coord &coord ) const; }; From 917c43a54b034b9c21d4c88f858b71cc6744f8ce Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 4 Mar 2020 14:13:44 -0500 Subject: [PATCH 20/32] If can't connect to db, bail instead of loading config --- src/zm_config.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 7d75042ba..9c99172e9 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -63,7 +63,9 @@ void zmLoadConfig() { closedir(configSubFolder); } - zmDbConnect(); + if ( !zmDbConnect() ) { + Fatal("Can't connect to db. Can't continue."); + } config.Load(); config.Assign(); From 2071b2a71e45e3eb83c07a49bf9c5b54d3662b1c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 18:14:58 -0400 Subject: [PATCH 21/32] Guess video file name for in progress events --- src/zm_eventstream.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index fbd236cc2..8db4942af 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -277,7 +277,10 @@ bool EventStream::loadEventData(uint64_t event_id) { mysql_free_result(result); - if ( event_data->video_file[0] ) { + if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) { + if ( !event_data->video_file[0] ) { + snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4"); + } std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file); //char filepath[PATH_MAX]; //snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file); From 0085e3d34f176a5dba7f202eda2ec3fc65034999 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 18:15:19 -0400 Subject: [PATCH 22/32] Don't warn about image for WebSite Monitors --- web/skins/classic/views/watch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index caeb5f95e..e1f54e29d 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -76,7 +76,7 @@ if ( canView('Control') && $monitor->Type() == 'Local' ) {
Status() != 'Connected' ) { +if ( $monitor->Status() != 'Connected' and $monitor->Type() != 'WebSite' ) { echo '
Monitor is not capturing. We will be unable to provide an image
'; } ?> From 24aaeee15545fe8f1975df66a4585ca056b829d0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 18:44:35 -0400 Subject: [PATCH 23/32] dynamically load monitor Object when loading event data. Also dynamically load Storage, but only when the Id is different. Free the various objects in the destructor. Sync up some spacing issues with master. --- src/zm_eventstream.cpp | 48 ++++++++++++++++++++-------------- src/zm_eventstream.h | 59 ++++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 8db4942af..d081f4e76 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -164,7 +164,22 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8])); mysql_free_result(result); - Storage * storage = new Storage(event_data->storage_id); + if ( !monitor ) { + monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY); + } else if ( monitor->Id() != event_data->monitor_id ) { + delete monitor; + monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY); + } + if ( !monitor ) { + Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id); + } + + if ( !storage ) { + storage = new Storage(event_data->storage_id); + } else if ( storage->Id() != event_data->storage_id ) { + delete storage; + storage = new Storage(event_data->storage_id); + } const char *storage_path = storage->Path(); if ( event_data->scheme == Storage::DEEP ) { @@ -206,7 +221,6 @@ bool EventStream::loadEventData(uint64_t event_id) { staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id); } - delete storage; storage = NULL; updateFrameRate((double)event_data->frame_count/event_data->duration); Debug(3, "fps set by frame_count(%d)/duration(%f)", @@ -246,7 +260,7 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->frames[i-1].timestamp = last_timestamp + ((i-last_id)*frame_delta); event_data->frames[i-1].offset = event_data->frames[i-1].timestamp - event_data->start_time; event_data->frames[i-1].in_db = false; - Debug(3,"Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", + Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", i, event_data->frames[i-1].timestamp, event_data->frames[i-1].offset, @@ -308,7 +322,7 @@ bool EventStream::loadEventData(uint64_t event_id) { void EventStream::processCommand(const CmdMsg *msg) { Debug(2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0]); // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) { + switch ( (MsgCommand)msg->msg_data[0] ) { case CMD_PAUSE : Debug(1, "Got PAUSE command"); @@ -660,7 +674,8 @@ bool EventStream::sendFrame(int delta_us) { Image *send_image = prepareImage(&image); if ( !vid_stream ) { - vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); + vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, + send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType()); vid_stream->OpenStream(); } @@ -703,9 +718,10 @@ Debug(1, "Loading image"); } else if ( ffmpeg_input ) { // Get the frame from the mp4 input Debug(1,"Getting frame from ffmpeg"); - AVFrame *frame; FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), frame_data->offset ); + AVFrame *frame = ffmpeg_input->get_frame( + ffmpeg_input->get_video_stream_id(), + frame_data->offset); if ( frame ) { image = new Image(frame); //av_frame_free(&frame); @@ -985,8 +1001,8 @@ void EventStream::runStream() { // you can calculate the relationship between now and the start // or calc the relationship from the last frame. I think from the start is better as it self-corrects - if ( send_frame && type != STREAM_MPEG ) { - if ( delta_us > 0) { + if ( send_frame && (type != STREAM_MPEG) ) { + if ( delta_us > 0 ) { if ( delta_us > MAX_SLEEP_USEC ) { Debug(1, "Limiting sleep to %d because calculated sleep is too long %d", MAX_SLEEP_USEC, delta_us); delta_us = MAX_SLEEP_USEC; @@ -1029,18 +1045,12 @@ void EventStream::runStream() { closeComms(); } // void EventStream::runStream() -void EventStream::setStreamStart( uint64_t init_event_id, unsigned int init_frame_id=0 ) { +void EventStream::setStreamStart( + uint64_t init_event_id, + unsigned int init_frame_id=0) { loadInitialEventData(init_event_id, init_frame_id); - if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) { - Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id); - return; - } -} +} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0) void EventStream::setStreamStart(int monitor_id, time_t event_time) { loadInitialEventData(monitor_id, event_time); - if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) { - Fatal("Unable to load monitor id %d for streaming", monitor_id); - return; - } } diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 6850a3d11..a482483b4 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -20,9 +20,6 @@ #ifndef ZM_EVENTSTREAM_H #define ZM_EVENTSTREAM_H -#include -#include - #include "zm_image.h" #include "zm_stream.h" #include "zm_video.h" @@ -83,7 +80,6 @@ class EventStream : public StreamBase { struct timeval start; // clock time when started the event EventData *event_data; - FFmpeg_Input *ffmpeg_input; protected: bool loadEventData( uint64_t event_id ); @@ -95,23 +91,40 @@ class EventStream : public StreamBase { bool sendFrame( int delta_us ); public: - EventStream() { - mode = DEFAULT_MODE; - replay_rate = DEFAULT_RATE; - - forceEventChange = false; - - curr_frame_id = 0; - curr_stream_time = 0.0; - send_frame = false; - - event_data = 0; - + EventStream() : + mode(DEFAULT_MODE), + forceEventChange(false), + curr_frame_id(0), + curr_stream_time(0.0), + send_frame(false), + event_data(0), + storage(NULL), + ffmpeg_input(NULL), // Used when loading frames from an mp4 - input_codec_context = 0; - input_codec = 0; - - ffmpeg_input = NULL; + input_codec_context(0), + input_codec(0) + {} + ~EventStream() { + if ( event_data ) { + if ( event_data->frames ) { + delete[] event_data->frames; + event_data->frames = NULL; + } + delete event_data; + event_data = NULL; + } + if ( monitor ) { + delete monitor; + monitor = NULL; + } + if ( storage ) { + delete storage; + storage = NULL; + } + if ( ffmpeg_input ) { + delete ffmpeg_input; + ffmpeg_input = NULL; + } } void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id ); void setStreamStart( int monitor_id, time_t event_time ); @@ -121,8 +134,10 @@ class EventStream : public StreamBase { void runStream(); Image *getImage(); private: - AVCodecContext *input_codec_context; - AVCodec *input_codec; + Storage *storage; + FFmpeg_Input *ffmpeg_input; + AVCodecContext *input_codec_context; + AVCodec *input_codec; }; #endif // ZM_EVENTSTREAM_H From c99db3a03f1ad708fb11c1e55ac65af7a58a6914 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 30 Mar 2020 10:09:53 -0400 Subject: [PATCH 24/32] Remove some hard coded sizes, spacing and typos. Convert inputs to number inputs as appropriate --- web/skins/classic/views/events.php | 2 +- web/skins/classic/views/monitor.php | 164 +++++++++++++++++++--------- 2 files changed, 114 insertions(+), 52 deletions(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index cfbe90383..034e908ef 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -258,7 +258,7 @@ while ( $event_row = dbFetchNext($results) ) { echo ''; $imgSrc = $event->getThumbnailSrc(array(),'&'); $streamSrc = $event->getStreamSrc(array( - 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single'), '&'); + 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&'); $imgHtml = ''. validHtmlStr('Event '.$event->Id()) .''; echo ''.$imgHtml.''; diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 303e335ee..5ac52494e 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -783,19 +783,22 @@ include('_monitor_source_nvsocket.php'); ?> Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> + + + +Protocol() || $monitor->Protocol() == 'http' ) { -?> - Method() ); ?> -Method() ); } else { -?> - Method() ); ?> -Method() ); } ?> - + + + - + Type() == 'File' ) { ?> @@ -803,23 +806,44 @@ include('_monitor_source_nvsocket.php'); Type() == 'cURL' ) { ?> - - - + + + Type() == 'WebSite' ) { ?> - - () - () - + + + + + + () + + + + () + + + + + + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { + } else if ( $monitor->Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> - -  ()Method() ); ?> + + + + + + ) + Method()) ?> + -  (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>) +  (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>) Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { + if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { ?> - Colours() ); ?> - + + + Colours()) ?> + () @@ -884,18 +910,22 @@ if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { Orientation());?> Type() == 'Local' ) { + } + if ( $monitor->Type() == 'Local' ) { ?> - - + + Deinterlacing())?> + +Type() != 'WebSite' ) { ?> - + + + Deinterlacing())?> + - Type() == 'Remote' ) { ?> Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> () RTSPDescribe() ) { ?> checked="checked"/> @@ -939,15 +969,17 @@ if ( $monitor->Type() == 'Local' ) { $videowriteropts[1] = 'X264 Encode'; - if ($monitor->Type() == 'Ffmpeg' ) + if ( $monitor->Type() == 'Ffmpeg' ) $videowriteropts[2] = 'H264 Camera Passthrough'; else $videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1); - echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() ); + echo htmlselect('newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter()); ?> - + + + Type() == 'Ffmpeg' ) { ?> RecordAudio() ) { ?> checked="checked"/> @@ -962,22 +994,52 @@ if ( $monitor->Type() == 'Local' ) { case 'timestamp' : { ?> - - - - + + + + + + + + + + + + + + + LabelSize()) ?> + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + Type() == 'Local' ) { ?> - Controllable() ) { ?> checked="checked"/> + Controllable() ) { ?> checked="checked"/> + ControlId()); @@ -1011,20 +1074,19 @@ if ( canEdit('Control') ) { TrackMotion() ) { ?> checked="checked"/> - translate('None'), - '0' => translate('Home'), - '1' => translate('Preset').' 1', - ); -?> - ReturnLocation()); ?> + translate('None'), + '0' => translate('Home'), + '1' => translate('Preset').' 1', + ); +echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnLocation()); ?> From 9beb5cee3a780b1e49ac58f7c779288ba0df6c55 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 30 Apr 2020 11:04:55 -0400 Subject: [PATCH 25/32] use delete[] instead of delete. --- src/zm_ffmpeg_input.cpp | 6 +++++- src/zm_user.cpp | 10 +++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 45128645c..a6844d6a3 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -15,7 +15,11 @@ FFmpeg_Input::FFmpeg_Input() { FFmpeg_Input::~FFmpeg_Input() { if ( streams ) { - delete streams; + for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) { + avcodec_close(streams[i].context); + streams[i].context = NULL; + } + delete[] streams; streams = NULL; } } diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 0ad9f0672..623982fe9 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -107,14 +107,18 @@ User *zmLoadUser(const char *username, const char *password) { mysql_real_escape_string(&dbconn, safer_username, username, username_length); snprintf(sql, sizeof(sql), - "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`" - " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", safer_username); + "SELECT `Id`, `Username`, `Password`, `Enabled`," + " `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0," + " `MonitorIds`" + " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", + safer_username); + delete[] safer_username; + safer_username = NULL; if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); exit(mysql_errno(&dbconn)); } - delete safer_username; MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { From 050868fad1d73335ad1c62a3073d5fa954bfd245 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 11 Mar 2020 10:54:35 -0400 Subject: [PATCH 26/32] fix build on libavtools < 12 --- src/zm_ffmpeg.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index c65bb38a0..f8fc519ed 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -40,6 +40,7 @@ extern "C" { #include #include #include "libavutil/audio_fifo.h" +#include "libavutil/pixfmt.h" /* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) @@ -321,6 +322,7 @@ void zm_dump_codecpar(const AVCodecParameters *par); frame->pts \ ); +#if LIBAVUTIL_VERSION_CHECK(54, 4, 0, 74, 100) #define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \ text, \ frame->format, \ @@ -331,6 +333,18 @@ void zm_dump_codecpar(const AVCodecParameters *par); frame->pts \ ); +#else +#define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \ + text, \ + frame->format, \ + "unsupported", \ + frame->width, \ + frame->height, \ + frame->linesize[0], frame->linesize[1], \ + frame->pts \ + ); +#endif + #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #define zm_av_packet_unref( packet ) av_packet_unref( packet ) #define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src ) From c5cf6e1a7466c189d9b3e1b13b7eff2268a17d6d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 11 Mar 2020 10:54:52 -0400 Subject: [PATCH 27/32] undo random addition --- src/zm_ffmpeg.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index f8fc519ed..73afff3e3 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -40,7 +40,6 @@ extern "C" { #include #include #include "libavutil/audio_fifo.h" -#include "libavutil/pixfmt.h" /* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) From 6098c4c96125ce81bac19ce0d8fddbd43587e971 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 19:03:18 -0400 Subject: [PATCH 28/32] properly close streams and free memory in ffmpeg_input destructor --- src/zm_ffmpeg_input.cpp | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index a6844d6a3..28ecd4ac7 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -19,10 +19,22 @@ FFmpeg_Input::~FFmpeg_Input() { avcodec_close(streams[i].context); streams[i].context = NULL; } - delete[] streams; + delete streams; streams = NULL; } -} + if ( frame ) { + av_frame_free(&frame); + frame = NULL; + } + if ( input_format_context ) { +#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) + av_close_input_file(input_format_context); +#else + avformat_close_input(&input_format_context); +#endif + input_format_context = NULL; + } +} // end ~FFmpeg_Input() int FFmpeg_Input::Open(const char *filepath) { @@ -94,6 +106,7 @@ int FFmpeg_Input::Open(const char *filepath) { avcodec_free_context(&streams[i].context); #endif avformat_close_input(&input_format_context); + input_format_context = NULL; return error; } } // end foreach stream @@ -107,7 +120,6 @@ int FFmpeg_Input::Open(const char *filepath) { } // end int FFmpeg_Input::Open( const char * filepath ) AVFrame *FFmpeg_Input::get_frame(int stream_id) { - int frameComplete = false; AVPacket packet; av_init_packet(&packet); @@ -143,8 +155,8 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { } ret = zm_send_packet_receive_frame(context, frame, packet); if ( ret < 0 ) { - Error("Unable to decode frame at frame %d: %s, continuing", - streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str()); + Error("Unable to decode frame at frame %d: %d %s, continuing", + streams[packet.stream_index].frame_count, ret, av_make_error_string(ret).c_str()); zm_av_packet_unref(&packet); av_frame_free(&frame); continue; @@ -215,15 +227,17 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { last_seek_request = seek_target; // Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want. - if ( frame->pts <= seek_target ) { + if ( frame->pts <= seek_target ) { zm_dump_frame(frame, "pts <= seek_target"); while ( frame && (frame->pts < seek_target) ) { - if ( !get_frame(stream_id) ) + if ( !get_frame(stream_id) ) { + Warning("Got no frame. returning nothing"); return frame; + } } + zm_dump_frame(frame, "frame->pts <= seek_target, got"); return frame; } return get_frame(stream_id); - -} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at) +} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at) From c9f04c91365f388ec64cbc06c2d146107310d751 Mon Sep 17 00:00:00 2001 From: Joe540 Date: Sun, 12 Apr 2020 20:42:43 -0400 Subject: [PATCH 29/32] Support event_close_mode for record mode Support event_close_mode for record mode (with linked monitor) --- src/zm_monitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 2a4c46724..b2a3b56c5 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1655,7 +1655,7 @@ bool Monitor::Analyse() { Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images", name, image_count, event->Id(), event->Frames(), event->AlarmFrames()); //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) - if ( function != MOCORD || event_close_mode == CLOSE_ALARM ) { + if ( ( function != MOCORD && function != RECORD ) || event_close_mode == CLOSE_ALARM ) { shared_data->state = state = IDLE; Info("%s: %03d - Closing event %" PRIu64 ", alarm end%s", name, image_count, event->Id(), (function==MOCORD)?", section truncated":""); From ef540aa9726db791d0bb2af59e2bfc211a1c49f8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 24 Apr 2020 18:03:00 -0400 Subject: [PATCH 30/32] sun_path is defined to be 108 bytes. No need for 4095. Quiets compiler warnings --- src/zm_stream.cpp | 2 +- src/zm_stream.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index d355ef4c2..587c01ebf 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -349,7 +349,7 @@ void StreamBase::openComms() { } snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey); - strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1); + strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)); rem_addr.sun_family = AF_UNIX; gettimeofday(&last_comm_update, NULL); diff --git a/src/zm_stream.h b/src/zm_stream.h index eb99b74c1..15ba13423 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -77,11 +77,11 @@ protected: protected: int connkey; int sd; - char loc_sock_path[PATH_MAX]; + char loc_sock_path[108]; struct sockaddr_un loc_addr; - char rem_sock_path[PATH_MAX]; + char rem_sock_path[108]; struct sockaddr_un rem_addr; - char sock_path_lock[PATH_MAX]; + char sock_path_lock[108]; int lock_fd; protected: From eca56c7123066f9cbee3790200d06f17b0c48200 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 9 Mar 2020 13:14:09 -0400 Subject: [PATCH 31/32] Provide a more useful error message when the monitor's function is set to NONE. Fixes #2877 --- src/zm_stream.cpp | 89 +++++++------- src/zm_user.cpp | 300 +++++++++++++++++++++++----------------------- src/zms.cpp | 24 ++-- 3 files changed, 206 insertions(+), 207 deletions(-) diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 587c01ebf..258e04d52 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -114,7 +114,7 @@ bool StreamBase::checkCommandQueue() { return false; } -Image *StreamBase::prepareImage( Image *image ) { +Image *StreamBase::prepareImage(Image *image) { // Do not bother to scale zoomed in images, just crop them and let the browser scale // Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream. @@ -124,50 +124,51 @@ Image *StreamBase::prepareImage( Image *image ) { int mag = (scale * zoom) / ZM_SCALE_BASE; int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag; - Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag ); int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE; int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag; - Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag ); - int base_image_width = image->Width(), base_image_height = image->Height(); - Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height ); - int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE; - Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height ); - int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE; - Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height ); - int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height ); - int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height ); - int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE; - Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height ); - int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE; - Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height ); - int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag; - Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height ); - int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag; - Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); - if ( mag != ZM_SCALE_BASE ) { - if ( act_mag != ZM_SCALE_BASE ) { - Debug(3, "Magnifying by %d", mag); - if ( !image_copied ) { - static Image copy_image; - copy_image.Assign(*image); - image = ©_image; - image_copied = true; - } - image->Scale(mag); - } + Debug(3, + "Scaling by %d, zooming by %d = magnifying by %d(%d)\n" + "Last scaling by %d, zooming by %d = magnifying by %d(%d)\n" + "Base image width = %d, height = %d\n" + "Virtual image width = %d, height = %d\n" + "Last virtual image width = %d, height = %d\n" + "Actual image width = %d, height = %d\n" + "Last actual image width = %d, height = %d\n" + "Display image width = %d, height = %d\n" + "Last display image width = %d, height = %d\n" + "Send image width = %d, height = %d\n" + "Last send image width = %d, height = %d\n", + scale, zoom, mag, act_mag, + last_scale, last_zoom, last_mag, last_act_mag, + base_image_width, base_image_height, + virt_image_width, virt_image_height, + last_virt_image_width, last_virt_image_height, + act_image_width, act_image_height, + last_act_image_width, last_act_image_height, + disp_image_width, disp_image_height, + last_disp_image_width, last_disp_image_height, + send_image_width, send_image_height, + last_send_image_width, last_send_image_height + ); + + if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) { + Debug(3, "Magnifying by %d", mag); + static Image copy_image; + copy_image.Assign(*image); + image = ©_image; + image_copied = true; + image->Scale(mag); } Debug(3, "Real image width = %d, height = %d", image->Width(), image->Height()); @@ -176,26 +177,22 @@ Image *StreamBase::prepareImage( Image *image ) { static Box last_crop; if ( mag != last_mag || x != last_x || y != last_y ) { - Debug( 3, "Got click at %d,%d x %d", x, y, mag ); - - //if ( !last_mag ) - //last_mag = mag; + Debug(3, "Got click at %d,%d x %d", x, y, mag); if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) ) last_crop = Box(); - Debug( 3, "Recalculating crop" ); // Recalculate crop parameters, as %ges int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image click_x += ( x * 100 ) / last_virt_image_width; int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image click_y += ( y * 100 ) / last_virt_image_height; - Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); + Debug(3, "Got adjusted click at %d%%,%d%%", click_x, click_y); // Convert the click locations to the current image pixels click_x = ( click_x * act_image_width ) / 100; click_y = ( click_y * act_image_height ) / 100; - Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); + Debug(3, "Got readjusted click at %d,%d", click_x, click_y); int lo_x = click_x - (send_image_width/2); if ( lo_x < 0 ) @@ -214,23 +211,25 @@ Image *StreamBase::prepareImage( Image *image ) { lo_y = hi_y - (send_image_height - 1); } last_crop = Box( lo_x, lo_y, hi_x, hi_y ); - } - Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); + } // end if ( mag != last_mag || x != last_x || y != last_y ) + + Debug(3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY()); if ( !image_copied ) { static Image copy_image; - copy_image.Assign( *image ); + copy_image.Assign(*image); image = ©_image; image_copied = true; } - image->Crop( last_crop ); - } + image->Crop(last_crop); + } // end if difference in image vs displayed dimensions + last_scale = scale; last_zoom = zoom; last_x = x; last_y = y; return image; -} +} // end Image *StreamBase::prepareImage(Image *image) bool StreamBase::sendTextFrame(const char *frame_text) { Debug(2, "Sending %dx%d * %d text frame '%s'", diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 623982fe9..8457fd5b7 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -1,21 +1,21 @@ /* * ZoneMinder regular expression class implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes - * + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ +*/ #include "zm.h" #include "zm_db.h" @@ -26,6 +26,7 @@ #include #include #include +#include #if HAVE_GNUTLS_GNUTLS_H #include @@ -35,7 +36,7 @@ #include #elif HAVE_LIBCRYPTO #include -#endif // HAVE_L || HAVE_LIBCRYPTO +#endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO #include "zm_utils.h" #include "zm_crypt.h" @@ -52,7 +53,7 @@ User::User(const MYSQL_ROW &dbrow) { id = atoi(dbrow[index++]); strncpy(username, dbrow[index++], sizeof(username)-1); strncpy(password, dbrow[index++], sizeof(password)-1); - enabled = (bool)atoi(dbrow[index++]); + enabled = static_cast(atoi(dbrow[index++])); stream = (Permission)atoi(dbrow[index++]); events = (Permission)atoi(dbrow[index++]); control = (Permission)atoi(dbrow[index++]); @@ -61,7 +62,7 @@ User::User(const MYSQL_ROW &dbrow) { char *monitor_ids_str = dbrow[index++]; if ( monitor_ids_str && *monitor_ids_str ) { StringVector ids = split(monitor_ids_str, ","); - for( StringVector::iterator i = ids.begin(); i < ids.end(); ++i ) { + for ( StringVector::iterator i = ids.begin(); i < ids.end(); ++i ) { monitor_ids.push_back(atoi((*i).c_str())); } } @@ -72,9 +73,9 @@ User::~User() { } void User::Copy(const User &u) { - id=u.id; - strncpy(username, u.username, sizeof(username)-1); - strncpy(password, u.password, sizeof(password)-1); + id = u.id; + strncpy(username, u.username, sizeof(username)); + strncpy(password, u.password, sizeof(password)); enabled = u.enabled; stream = u.stream; events = u.events; @@ -87,8 +88,9 @@ void User::Copy(const User &u) { bool User::canAccess(int monitor_id) { if ( monitor_ids.empty() ) return true; - - for ( std::vector::iterator i = monitor_ids.begin(); i != monitor_ids.end(); ++i ) { + + for ( std::vector::iterator i = monitor_ids.begin(); + i != monitor_ids.end(); ++i ) { if ( *i == monitor_id ) { return true; } @@ -103,7 +105,8 @@ User *zmLoadUser(const char *username, const char *password) { int username_length = strlen(username); char *safer_username = new char[(username_length * 2) + 1]; - // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator. + // According to docs, size of safer_whatever must be 2*length+1 + // due to unicode conversions + null terminator. mysql_real_escape_string(&dbconn, safer_username, username, username_length); snprintf(sql, sizeof(sql), @@ -117,7 +120,7 @@ User *zmLoadUser(const char *username, const char *password) { if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); - exit(mysql_errno(&dbconn)); + exit(mysql_errno(&dbconn)); } MYSQL_RES *result = mysql_store_result(&dbconn); @@ -126,162 +129,157 @@ User *zmLoadUser(const char *username, const char *password) { exit(mysql_errno(&dbconn)); } - if ( mysql_num_rows(result) != 1 ) { + if ( mysql_num_rows(result) == 1 ) { + MYSQL_ROW dbrow = mysql_fetch_row(result); + User *user = new User(dbrow); mysql_free_result(result); - Warning("Unable to authenticate user %s", username); - return NULL; - } - MYSQL_ROW dbrow = mysql_fetch_row(result); - User *user = new User(dbrow); + if ( + (! password ) // relay type must be none + || + verifyPassword(username, password, user->getPassword()) ) { + Info("Authenticated user '%s'", user->getUsername()); + return user; + } + } // end if 1 result from db mysql_free_result(result); - if ( !password ) { - // relay type must be none - return user; - } - - if ( verifyPassword(username, password, user->getPassword()) ) { - Info("Authenticated user '%s'", user->getUsername()); - return user; - } - Warning("Unable to authenticate user %s", username); return NULL; -} +} // end User *zmLoadUser(const char *username, const char *password) -User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) { +User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr) { std::string key = config.auth_hash_secret; std::string remote_addr = ""; - - if (use_remote_addr) { - remote_addr = std::string(getenv( "REMOTE_ADDR" )); + + if ( use_remote_addr ) { + remote_addr = std::string(getenv("REMOTE_ADDR")); if ( remote_addr == "" ) { - Warning( "Can't determine remote address, using null" ); + Warning("Can't determine remote address, using null"); remote_addr = ""; } key += remote_addr; } - Debug (1,"Inside zmLoadTokenUser, formed key=%s", key.c_str()); + Debug(1, "Inside zmLoadTokenUser, formed key=%s", key.c_str()); std::pair ans = verifyToken(jwt_token_str, key); std::string username = ans.first; unsigned int iat = ans.second; - Debug (1,"retrieved user '%s' from token", username.c_str()); + Debug(1, "retrieved user '%s' from token", username.c_str()); - if (username != "") { - char sql[ZM_SQL_MED_BUFSIZ] = ""; - snprintf(sql, sizeof(sql), - "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`" - " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str() ); - - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - exit(mysql_errno(&dbconn)); - } - - MYSQL_RES *result = mysql_store_result(&dbconn); - if ( !result ) { - Error("Can't use query result: %s", mysql_error(&dbconn)); - exit(mysql_errno(&dbconn)); - } - int n_users = mysql_num_rows(result); - - if ( n_users != 1 ) { - mysql_free_result(result); - Error("Unable to authenticate user '%s'", username.c_str()); - return NULL; - } - - MYSQL_ROW dbrow = mysql_fetch_row(result); - User *user = new User(dbrow); - unsigned int stored_iat = strtoul(dbrow[10], NULL,0 ); - - if (stored_iat > iat ) { // admin revoked tokens - mysql_free_result(result); - Error("Token was revoked for '%s'", username.c_str()); - return NULL; - } - - Debug (1,"Got last token revoke time of: %u",stored_iat); - Debug (1,"Authenticated user '%s' via token", username.c_str()); - mysql_free_result(result); - return user; - - } - else { + if ( username == "" ) { return NULL; } -} + char sql[ZM_SQL_MED_BUFSIZ] = ""; + snprintf(sql, sizeof(sql), + "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0," + " `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`" + " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str()); + + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + return NULL; + } + + MYSQL_RES *result = mysql_store_result(&dbconn); + if ( !result ) { + Error("Can't use query result: %s", mysql_error(&dbconn)); + return NULL; + } + + int n_users = mysql_num_rows(result); + if ( n_users != 1 ) { + mysql_free_result(result); + Error("Unable to authenticate user '%s'", username.c_str()); + return NULL; + } + + MYSQL_ROW dbrow = mysql_fetch_row(result); + User *user = new User(dbrow); + unsigned int stored_iat = strtoul(dbrow[10], NULL, 0); + + if ( stored_iat > iat ) { // admin revoked tokens + mysql_free_result(result); + Error("Token was revoked for '%s'", username.c_str()); + return NULL; + } + + Debug(1, "Authenticated user '%s' via token with last revoke time: %u", + username.c_str(), stored_iat); + mysql_free_result(result); + return user; +} // User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr) // Function to validate an authentication string -User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { +User *zmLoadAuthUser(const char *auth, bool use_remote_addr) { #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT #ifdef HAVE_GCRYPT_H // Special initialisation for libgcrypt - if ( !gcry_check_version( GCRYPT_VERSION ) ) { - Fatal( "Unable to initialise libgcrypt" ); + if ( !gcry_check_version(GCRYPT_VERSION) ) { + Fatal("Unable to initialise libgcrypt"); } - gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); - gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); -#endif // HAVE_GCRYPT_H + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif // HAVE_GCRYPT_H const char *remote_addr = ""; if ( use_remote_addr ) { - remote_addr = getenv( "REMOTE_ADDR" ); + remote_addr = getenv("REMOTE_ADDR"); if ( !remote_addr ) { - Warning( "Can't determine remote address, using null" ); + Warning("Can't determine remote address, using null"); remote_addr = ""; } } - Debug( 1, "Attempting to authenticate user from auth string '%s'", auth ); + Debug(1, "Attempting to authenticate user from auth string '%s'", auth); char sql[ZM_SQL_SML_BUFSIZ] = ""; - snprintf( sql, sizeof(sql), "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds` FROM `Users` WHERE `Enabled` = 1" ); + snprintf(sql, sizeof(sql), + "SELECT `Id`, `Username`, `Password`, `Enabled`," + " `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0," + " `MonitorIds` FROM `Users` WHERE `Enabled` = 1"); - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } - MYSQL_RES *result = mysql_store_result( &dbconn ); + MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Error("Can't use query result: %s", mysql_error(&dbconn)); + return NULL; } - int n_users = mysql_num_rows( result ); - + int n_users = mysql_num_rows(result); if ( n_users < 1 ) { - mysql_free_result( result ); - Warning( "Unable to authenticate user" ); - return( 0 ); + mysql_free_result(result); + Warning("Unable to authenticate user"); + return NULL; } - while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) { + time_t now = time(0); + unsigned int hours = config.auth_hash_ttl; + if ( ! hours ) { + Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2."); + hours = 2; + } else { + Debug(1, "AUTH_HASH_TTL is %d", hours); + } + char auth_key[512] = ""; + char auth_md5[32+1] = ""; + size_t md5len = 16; + unsigned char md5sum[md5len]; + + const char * hex = "0123456789abcdef"; + while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) { const char *user = dbrow[1]; const char *pass = dbrow[2]; - char auth_key[512] = ""; - char auth_md5[32+1] = ""; - size_t md5len = 16; - unsigned char md5sum[md5len]; + time_t our_now = now; + for ( unsigned int i = 0; i < hours; i++, our_now -= 3600 ) { + struct tm *now_tm = localtime(&our_now); - time_t now = time( 0 ); - unsigned int hours = config.auth_hash_ttl; - - if ( ! hours ) { - Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2."); - hours = 2; - } else { - Debug( 1, "AUTH_HASH_TTL is %d", hours ); - } - - for ( unsigned int i = 0; i < hours; i++, now -= 3600 ) { - struct tm *now_tm = localtime( &now ); - - snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", + snprintf(auth_key, sizeof(auth_key)-1, "%s%s%s%s%d%d%d%d", config.auth_hash_secret, user, pass, @@ -289,51 +287,57 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { now_tm->tm_hour, now_tm->tm_mday, now_tm->tm_mon, - now_tm->tm_year - ); + now_tm->tm_year); #if HAVE_DECL_MD5 - MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum ); + MD5((unsigned char *)auth_key, strlen(auth_key), md5sum); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len ); + gnutls_datum_t md5data = { (unsigned char *)auth_key, (unsigned int)strlen(auth_key) }; + gnutls_fingerprint(GNUTLS_DIG_MD5, &md5data, md5sum, &md5len); #endif - auth_md5[0] = '\0'; + unsigned char *md5sum_ptr = md5sum; + char *auth_md5_ptr = auth_md5; + for ( unsigned int j = 0; j < md5len; j++ ) { - sprintf( &auth_md5[2*j], "%02x", md5sum[j] ); + *auth_md5_ptr++ = hex[(*md5sum_ptr>>4)&0xf]; + *auth_md5_ptr++ = hex[(*md5sum_ptr++)&0xf]; } - Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", auth_key, auth_md5, auth ); + *auth_md5_ptr = 0; - if ( !strcmp( auth, auth_md5 ) ) { + Debug(1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", + auth_key, auth_md5, auth); + + if ( !strcmp(auth, auth_md5) ) { // We have a match - User *user = new User( dbrow ); - Debug(1, "Authenticated user '%s'", user->getUsername() ); - mysql_free_result( result ); - return( user ); + User *user = new User(dbrow); + Debug(1, "Authenticated user '%s'", user->getUsername()); + mysql_free_result(result); + return user; } else { - Debug(1, "No match for %s", auth ); + Debug(1, "No match for %s", auth); } - } - } - mysql_free_result( result ); -#else // HAVE_DECL_MD5 - Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); -#endif // HAVE_DECL_MD5 - Debug(1, "No user found for auth_key %s", auth ); - return 0; -} + } // end foreach hour + } // end foreach user + mysql_free_result(result); +#else // HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT + Error("You need to build with gnutls or openssl to use hash based auth"); +#endif // HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT + Debug(1, "No user found for auth_key %s", auth); + return NULL; +} // end User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) -//Function to check Username length -bool checkUser ( const char *username) { - if ( ! username ) +// Function to check Username length +bool checkUser(const char *username) { + if ( !username ) return false; if ( strlen(username) > 32 ) return false; return true; } -//Function to check password length -bool checkPass (const char *password) { + +// Function to check password length +bool checkPass(const char *password) { if ( !password ) return false; if ( strlen(password) > 64 ) diff --git a/src/zms.cpp b/src/zms.cpp index 6511afdc8..ad7076e7a 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -184,7 +184,7 @@ int main(int argc, const char *argv[]) { logInit(log_id_string); if ( config.opt_use_auth ) { - User *user = 0; + User *user = NULL; if ( jwt_token_str != "" ) { // user = zmLoadTokenUser(jwt_token_str, config.auth_hash_ips); @@ -195,19 +195,11 @@ int main(int argc, const char *argv[]) { } else { Error("Bad username"); } - } else { - // if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( *auth ) { - user = zmLoadAuthUser(auth, config.auth_hash_ips); - } - } - // else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( username.length() && password.length() ) { - user = zmLoadUser(username.c_str(), password.c_str()); - } + if ( *auth ) { + user = zmLoadAuthUser(auth, config.auth_hash_ips); + } else if ( username.length() && password.length() ) { + user = zmLoadUser(username.c_str(), password.c_str()); } } if ( !user ) { @@ -218,11 +210,15 @@ int main(int argc, const char *argv[]) { return 0; } if ( !ValidateAccess(user, monitor_id) ) { + delete user; + user = NULL; fputs("HTTP/1.0 403 Forbidden\r\n\r\n", stdout); logTerm(); zmDbClose(); return 0; } + delete user; + user = NULL; } // end if config.opt_use_auth hwcaps_detect(); @@ -336,5 +332,5 @@ int main(int argc, const char *argv[]) { logTerm(); zmDbClose(); - return(0); + return 0; } From 3f680e11fa60014ecbd6f16fa19bae54b73cc354 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 1 May 2020 19:23:37 -0400 Subject: [PATCH 32/32] Bump version for release --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index f7eeeb016..2a08edc70 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.10 +Version: 1.34.11 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 0ceb7f6f3..6fdc61072 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.10 +1.34.11