diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 92d133e0f..2ff2de208 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -608,9 +608,6 @@ CREATE TABLE `Monitors` ( `WebColour` varchar(32) NOT NULL default 'red', `Exif` tinyint(1) unsigned NOT NULL default '0', `Sequence` smallint(5) unsigned default NULL, - `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', - `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, - `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, `TotalEvents` int(10) unsigned, `TotalEventDiskSpace` bigint unsigned, `HourEvents` int(10) unsigned, @@ -629,6 +626,14 @@ CREATE TABLE `Monitors` ( CREATE INDEX `Monitors_ServerId_idx` ON `Monitors` (`ServerId`); +DROP TABLE IF EXISTS `Monitor_Status`; +CREATE TABLE `Monitor_Status` ( + `Id` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`Id`) +) ENGINE=MEMORY; -- -- Table structure for table `States` -- PP - Added IsActive to track custom run states diff --git a/db/zm_update-1.31.0.sql b/db/zm_update-1.31.0.sql index 7daadd974..1e20953d6 100644 --- a/db/zm_update-1.31.0.sql +++ b/db/zm_update-1.31.0.sql @@ -1,7 +1,7 @@ -- -- This updates a 1.29.0 database to 1.30.0 -- - +SELECT 'Checking for SaveJPEGs in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -16,6 +16,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for VideoWriter in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -30,6 +31,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for EncoderParameters in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -44,6 +46,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for DefaultVideo in Events'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -58,6 +61,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Checking for RecordAudio in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS diff --git a/db/zm_update-1.31.1.sql b/db/zm_update-1.31.1.sql index 18d825091..399a100bb 100644 --- a/db/zm_update-1.31.1.sql +++ b/db/zm_update-1.31.1.sql @@ -3,6 +3,7 @@ -- -- Add StateId Column to Events. -- +SELECT 'Checkfor StateId IN Events'; SET @s = (SELECT IF( (SELECT COUNT(*) diff --git a/db/zm_update-1.31.2.sql b/db/zm_update-1.31.2.sql index d918db17b..3e40885a4 100644 --- a/db/zm_update-1.31.2.sql +++ b/db/zm_update-1.31.2.sql @@ -2,6 +2,7 @@ -- Update Filters table to have a Concurrent Column -- +SELECT 'Checking for Concurrent in Filters'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS diff --git a/db/zm_update-1.31.23.sql b/db/zm_update-1.31.23.sql index 1e4b6795c..9482561df 100644 --- a/db/zm_update-1.31.23.sql +++ b/db/zm_update-1.31.23.sql @@ -2,7 +2,11 @@ SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Servers' AND column_name = 'zmstats.pl' - ) > 0, + ) > 0 AND ( SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmstats' + ) = 0 + , "ALTER TABLE Servers CHANGE COLUMN `zmstats.pl` `zmstats` BOOLEAN NOT NULL DEFAULT FALSE", "SELECT 'zmstats.pl has already been changed to zmstats'" )); @@ -13,8 +17,27 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Servers' - AND column_name = 'zmaudit.pl' + AND column_name = 'zmstats.pl' ) > 0, + "ALTER TABLE Servers DROP COLUMN `zmstats.pl`", + "SELECT 'zmstats.pl has already been removed'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmaudit.pl' + ) > 0 + AND + ( SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmaudit' + ) = 0 + , "ALTER TABLE Servers CHANGE COLUMN `zmaudit.pl` `zmaudit` BOOLEAN NOT NULL DEFAULT FALSE", "SELECT 'zmaudit.pl has already been changed to zmaudit'" )); @@ -25,11 +48,41 @@ EXECUTE stmt; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() AND table_name = 'Servers' - AND column_name = 'zmtrigger.pl' + AND column_name = 'zmaudit.pl' ) > 0, + "ALTER TABLE Servers DROP COLUMN `zmaudit.pl`", + "SELECT 'zmaudit.pl has already been removed'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmtrigger.pl' + ) > 0 + AND + ( SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmtrigger' + ) = 0 + , "ALTER TABLE Servers CHANGE COLUMN `zmtrigger.pl` `zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE", "SELECT 'zmtrigger.pl has already been changed to zmtrigger'" )); PREPARE stmt FROM @s; EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'zmtrigger.pl' + ) > 0, + "ALTER TABLE Servers DROP COLUMN `zmtrigger.pl`", + "SELECT 'zmtrigger.pl has already been removed'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.31.27.sql b/db/zm_update-1.31.27.sql new file mode 100644 index 000000000..6500c1efd --- /dev/null +++ b/db/zm_update-1.31.27.sql @@ -0,0 +1,100 @@ +delimiter // +DROP TRIGGER IF EXISTS event_update_trigger; + +CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events +FOR EACH ROW +BEGIN + declare diff BIGINT default 0; + + set diff = NEW.DiskSpace - OLD.DiskSpace; + IF ( NEW.StorageId = OLD.StorageID ) THEN + IF ( diff ) THEN + call update_storage_stats(OLD.StorageId, diff); + END IF; + ELSE + IF ( NEW.DiskSpace ) THEN + call update_storage_stats(NEW.StorageId, NEW.DiskSpace); + END IF; + IF ( OLD.DiskSpace ) THEN + call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); + END IF; + END IF; + + UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + IF ( NEW.Archived != OLD.Archived ) THEN + IF ( NEW.Archived ) THEN + INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; + ELSEIF ( OLD.Archived ) THEN + DELETE FROM Events_Archived WHERE EventId=OLD.Id; + UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId; + ELSE + IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + UPDATE Monitors SET + ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) + WHERE Id=OLD.MonitorId; + END IF; + END IF; + END IF; + + IF ( + ( (OLD.DiskSpace IS NOT NULL) AND (NEW.DiskSpace IS NULL) ) + OR + ( (OLD.DiskSpace IS NULL) AND (NEW.DiskSpace IS NOT NULL) ) + OR ( OLD.DiskSpace != NEW.DiskSpace ) + ) THEN + UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId; + END IF; + +END; + +// + +DROP TRIGGER IF EXISTS event_insert_trigger; + +create trigger event_insert_trigger after insert on Events +for each row + begin + + INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); + UPDATE Monitors SET TotalEvents = COALESCE(TotalEvents,0)+1 WHERE Id=NEW.MonitorId; +end; +// + +UPDATE Monitors INNER JOIN ( + SELECT MonitorId, + COUNT(Id) AS TotalEvents, + SUM(DiskSpace) AS TotalEventDiskSpace, + SUM(IF(Archived,1,0)) AS ArchivedEvents, + SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents, + SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace + FROM Events GROUP BY MonitorId + ) AS E ON E.MonitorId=Monitors.Id SET + Monitors.TotalEvents = E.TotalEvents, + Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace, + Monitors.ArchivedEvents = E.ArchivedEvents, + Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace, + Monitors.HourEvents = E.HourEvents, + Monitors.HourEventDiskSpace = E.HourEventDiskSpace, + Monitors.DayEvents = E.DayEvents, + Monitors.DayEventDiskSpace = E.DayEventDiskSpace, + Monitors.WeekEvents = E.WeekEvents, + Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace, + Monitors.MonthEvents = E.MonthEvents, + Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace// + + diff --git a/db/zm_update-1.31.28.sql b/db/zm_update-1.31.28.sql new file mode 100644 index 000000000..096b020c9 --- /dev/null +++ b/db/zm_update-1.31.28.sql @@ -0,0 +1,57 @@ +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitor_Status' + ) > 0 + , + "SELECT 'Monitor_Status Already exists'", + " +CREATE TABLE `Monitor_Status` ( + `Id` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`Id`) +) ENGINE=MEMORY" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Status' + ) > 0 + , + "ALTER TABLE Monitors DROP COLUMN Status", + "SELECT 'Monitor Status already removed.'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'CaptureFPS' + ) > 0 + , + "ALTER TABLE Monitors DROP COLUMN CaptureFPS", + "SELECT 'Monitor CaptureFPS already removed.'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'AnalysisFPS' + ) > 0 + , + "ALTER TABLE Monitors DROP COLUMN AnalysisFPS", + "SELECT 'Monitor AnalysisFPS already removed.'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.31.3.sql b/db/zm_update-1.31.3.sql index 0f1e6850c..733efcedd 100644 --- a/db/zm_update-1.31.3.sql +++ b/db/zm_update-1.31.3.sql @@ -2,6 +2,7 @@ -- This adds StorageAreas -- +SELECT 'Checking For Storage Table'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES @@ -24,6 +25,7 @@ EXECUTE stmt; -- Add StorageId column to Monitors -- +SELECT 'Checking For StorageId in Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -42,6 +44,7 @@ EXECUTE stmt; -- Add StorageId column to Eventss -- +SELECT 'Checking For StorageId in Events'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS @@ -56,6 +59,7 @@ SET @s = (SELECT IF( PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'Updating Monitors SETTING StorageId to default'; UPDATE Monitors SET StorageId = 0 WHERE StorageId IS NULL; ALTER TABLE Monitors MODIFY `StorageId` smallint(5) unsigned NOT NULL default 0; UPDATE Events SET StorageId = 0 WHERE StorageId IS NULL; @@ -78,6 +82,7 @@ EXECUTE stmt; -- -- Update Monitors table to have an Index on ServerId -- +SELECT 'Create Index For ServerId on Monitors'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS @@ -96,6 +101,7 @@ EXECUTE stmt; -- -- Update Server table to have an Index on Name -- +SELECT 'Create Index FOR Name on Servers'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS @@ -111,15 +117,19 @@ PREPARE stmt FROM @s; EXECUTE stmt; +SELECT 'ALTER TABLE Logs MODIFY Message TEXT NOT NULL'; -- ALTER TABLE Logs ALTER Message DROP DEFAULT; ALTER TABLE Logs MODIFY Message TEXT NOT NULL; +SELECT 'ALTER TABLE Config MODIFY DefaultValue TEXT'; ALTER TABLE Config MODIFY DefaultValue TEXT; -- -- Add an Id column and make it the primary key of the Filters table -- + +SELECT 'Check for Id column in Filter'; SET @s = (SELECT IF( (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS diff --git a/db/zm_update-1.31.7.sql b/db/zm_update-1.31.7.sql index 6634caf5f..1221d9adb 100644 --- a/db/zm_update-1.31.7.sql +++ b/db/zm_update-1.31.7.sql @@ -1 +1,12 @@ -ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL; +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Groups' + AND column_name = 'MonitorIds' + ) > 0, + "ALTER TABLE Groups MODIFY `MonitorIds` text NOT NULL", + "SELECT 'Groups no longer has MonitorIds'" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 957488d9e..766436a84 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -90,8 +90,9 @@ sub Time { $_[0]{Time} = $_[1]; } if ( ! defined $_[0]{Time} ) { - - $_[0]{Time} = Date::Parse::str2time( $_[0]{StartTime} ); + if ( $_[0]{StartTime} ) { + $_[0]{Time} = Date::Parse::str2time( $_[0]{StartTime} ); + } } return $_[0]{Time}; } @@ -115,6 +116,10 @@ sub find { push @sql_filters , ' Name = ? '; push @sql_values, $sql_filters{Name}; } + if ( exists $sql_filters{Id} ) { + push @sql_filters , ' Id = ? '; + push @sql_values, $sql_filters{Id}; + } $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; $sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; @@ -230,8 +235,15 @@ sub LinkPath { ), '.'.$$event{Id} ); + } elsif ( $$event{Path} ) { + if ( ( $$event{Path} =~ /^(\d+\/\d{4}\/\d{2}\/\d{2})/ ) ) { + $$event{LinkPath} = $1.'/.'.$$event{Id}; + } else { + Error("Unable to get LinkPath from Path for $$event{Id} $$event{Path}"); + $$event{LinkPath} = ''; + } } else { - Error("Event $$event{Id} has no value for Time(), unable to determine link path"); + Error("Event $$event{Id} $$event{Path} has no value for Time(), unable to determine link path"); $$event{LinkPath} = ''; } } # end if Scheme @@ -243,7 +255,7 @@ sub LinkPath { sub GenerateVideo { my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_; - my $event_path = $self->getPath( ); + my $event_path = $self->Path( ); chdir( $event_path ); ( my $video_name = $self->{Name} ) =~ s/\s/_/g; @@ -337,13 +349,9 @@ sub delete { } Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" ); $ZoneMinder::Database::dbh->ping(); -# Do it individually to avoid locking up the table for new events - my $sql = 'DELETE FROM Events WHERE Id=?'; - my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) - or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) - or Error( "Can't execute '$sql': ".$sth->errstr() ); - $sth->finish(); + + $ZoneMinder::Database::dbh->begin_work(); + $event->lock_and_load(); if ( ! $Config{ZM_OPT_FAST_DELETE} ) { my $sql = 'DELETE FROM Frames WHERE EventId=?'; @@ -352,6 +360,10 @@ sub delete { my $res = $sth->execute( $event->{Id} ) or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); + if ( $ZoneMinder::Database::dbh->errstr() ) { + $ZoneMinder::Database::dbh->commit(); + return; + } $sql = 'DELETE FROM Stats WHERE EventId=?'; $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) @@ -359,11 +371,23 @@ sub delete { $res = $sth->execute( $event->{Id} ) or Error( "Can't execute '$sql': ".$sth->errstr() ); $sth->finish(); + if ( $ZoneMinder::Database::dbh->errstr() ) { + $ZoneMinder::Database::dbh->commit(); + return; + } $event->delete_files( ); } else { Debug('Not deleting frames, stats and files for speed.'); } +# Do it individually to avoid locking up the table for new events + my $sql = 'DELETE FROM Events WHERE Id=?'; + my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) + or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Error( "Can't execute '$sql': ".$sth->errstr() ); + $sth->finish(); + $ZoneMinder::Database::dbh->commit(); } # end sub delete sub delete_files { @@ -502,7 +526,10 @@ sub MoveTo { } } } - return $error if $error; + if ( $error ) { + $ZoneMinder::Database::dbh->commit(); + return $error; + } my @files = glob("$OldPath/*"); for my $file (@files) { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index ed5779b2b..cd8b76a62 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -140,7 +140,7 @@ sub Sql { M.DefaultScale FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId - INNER JOIN Storage as S on S.Id = E.StorageId + LEFT JOIN Storage as S on S.Id = E.StorageId '; $self->{Sql} = ''; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index df7118f0f..baa77059a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -42,17 +42,26 @@ use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); -use vars qw/ $AUTOLOAD $log $dbh/; +use vars qw/ $AUTOLOAD $log $dbh %cache $no_cache/; *log = \$ZoneMinder::Logger::logger; *dbh = \$ZoneMinder::Database::dbh; my $debug = 0; -use constant DEBUG_ALL=>1; +$no_cache = 0; +use constant DEBUG_ALL=>0; + +sub init_cache { + $no_cache = 0; + %cache = (); +} # end sub init_cache sub new { my ( $parent, $id, $data ) = @_; + $cache{$parent} = {} if ! $cache{$parent}; + my $sub_cache = $cache{$parent}; + my $self = {}; bless $self, $parent; no strict 'refs'; @@ -61,10 +70,23 @@ sub new { Error( 'NO primary_key for type ' . $parent ); return; } # end if - if ( ( $$self{$primary_key} = $id ) or $data ) { -#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; - $self->load( $data ); + + if ( $id and (!$no_cache) and $$sub_cache{$id} ) { + if ( $data ) { + # The reason to use load is if we have overriden it in the object, + $$sub_cache{$id}->load( $data ); + } + return $$sub_cache{$id}; } + + if ( ( $$self{$primary_key} = $id ) or $data ) { + #$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + if ( !$no_cache ) { + $$sub_cache{$id} = $self; + } # end if + } # end if + return $self; } # end sub new @@ -170,8 +192,6 @@ sub save { foreach my $k ( keys %$data ) { $log->debug("Object::save after set $k => $$data{$k} $$self{$k}"); } - } else { - $log->debug("No data after set"); } } #$debug = 0; @@ -430,6 +450,13 @@ sub transform { return $value; } # end sub transform + +sub to_string { + my $type = ref($_[0]); + my $fields = eval '\%'.$type.'::fields'; + return $type . ': '. join(' ' , map { $_[0]{$_} ? "$_ => $_[0]{$_}" : () } keys %$fields ); +} + 1; __END__ diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm index 34158b9e4..8fbe5ed80 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm @@ -30,6 +30,7 @@ use warnings; require ZoneMinder::Base; require ZoneMinder::Object; +require ZoneMinder::Server; use parent qw(Exporter ZoneMinder::Object); @@ -59,6 +60,10 @@ sub find { my @sql_filters; my @sql_values; + if ( exists $sql_filters{Id} ) { + push @sql_filters , ' Id=? '; + push @sql_values, $sql_filters{Id}; + } if ( exists $sql_filters{Name} ) { push @sql_filters , ' Name = ? '; push @sql_values, $sql_filters{Name}; @@ -108,6 +113,14 @@ sub Name { return $_[0]{Name}; } # end sub Path +sub Server { + my $self = shift; + if ( ! $$self{Server} ) { + $$self{Server} = new ZoneMinder::Server( $$self{ServerId} ); + } + return $$self{Server}; +} + 1; __END__ # Below is stub documentation for your module. You'd better edit it! diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 87cea1af1..519e80851 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -55,7 +55,6 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) ; use constant ZM_AUDIT_PID => '@ZM_RUNDIR@/zmaudit.pid'; -$| = 1; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; @@ -67,16 +66,18 @@ my $continuous = 0; my $monitor_id = 0; my $version; my $force = 0; +my $storage_id = undef; logInit(); GetOptions( - continuous =>\$continuous, - force =>\$force, - interactive =>\$interactive, - monitor_id =>\$monitor_id, - report =>\$report, - version =>\$version + continuous =>\$continuous, + force =>\$force, + interactive =>\$interactive, + 'monitor_id=i' =>\$monitor_id, + report =>\$report, + 'storage_id=i' =>\$storage_id, + version =>\$version ) or pod2usage(-exitstatus => -1); if ( $version ) { @@ -119,6 +120,9 @@ sub HupHandler { } sub TermHandler { Info("Received TERM, exiting"); + Term(); +} +sub Term { unlink ZM_AUDIT_PID; exit( 0 ); } @@ -128,6 +132,8 @@ $SIG{INT} = \&TermHandler; my $dbh = zmDbConnect(); +$| = 1; + require ZoneMinder::Monitor; require ZoneMinder::Storage; require ZoneMinder::Event; @@ -165,6 +171,21 @@ MAIN: while( $loop ) { } # end if } # end while can't connect to the db + my @Storage_Areas; + if ( defined $storage_id ) { + @Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id ); + if ( !@Storage_Areas ) { + Fatal("No Storage Area found with Id $storage_id"); + } + Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}"); + } elsif ( $Config{ZM_SERVER_ID} ) { + @Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); + Info("Auditing All Storage Areas on Server " . $Storage_Areas[0]->Server()->Name()); + } else { + @Storage_Areas = ZoneMinder::Storage->find(); + Info("Auditing All Storage Areas"); + } + my %Monitors; my $db_monitors; my $monitorSelectSql = $monitor_id ? 'SELECT * FROM Monitors WHERE Id=?' : 'SELECT * FROM Monitors ORDER BY Id'; @@ -182,21 +203,19 @@ MAIN: while( $loop ) { while( my $monitor = $monitorSelectSth->fetchrow_hashref() ) { $Monitors{$$monitor{Id}} = $monitor; - Debug( "Found database monitor '$monitor->{Id}'" ); my $db_events = $db_monitors->{$monitor->{Id}} = {}; my $res = $eventSelectSth->execute( $monitor->{Id} ) or Fatal( "Can't execute: ".$eventSelectSth->errstr() ); while ( my $event = $eventSelectSth->fetchrow_hashref() ) { $db_events->{$event->{Id}} = $event->{Age}; } - Debug( 'Got '.int(keys(%$db_events))." events\n" ); + Debug( 'Got '.int(keys(%$db_events))." events for monitor $monitor->{Id}" ); } my $fs_monitors; - foreach my $Storage ( - ZoneMinder::Storage->find( ($Config{ZM_SERVER_ID} ? ( ServerId => $Config{ZM_SERVER_ID} ) : () ) ), - ) { + + foreach my $Storage ( @Storage_Areas ) { Debug('Checking events in ' . $Storage->Path() ); if ( ! chdir( $Storage->Path() ) ) { Error( 'Unable to change dir to ' . $Storage->Path() ); @@ -214,8 +233,8 @@ MAIN: while( $loop ) { # De-taint ( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); - if ( $$Storage{Scheme} eq 'Deep' ) { - foreach my $day_dir ( glob("$monitor_dir/*/*/*") ) { + #if ( $$Storage{Scheme} eq 'Deep' ) { + foreach my $day_dir ( glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") ) { Debug( "Checking day dir $day_dir" ); ( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint if ( ! chdir( $day_dir ) ) { @@ -252,6 +271,7 @@ MAIN: while( $loop ) { $$Event{Id} = $event; $$Event{Path} = join('/', $Storage->Path(), $day_dir,$event_path); $$Event{RelativePath} = join('/', $day_dir,$event_path); + $$Event{Scheme} = 'Deep'; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); $Event->DiskSpace( undef ); @@ -259,8 +279,8 @@ MAIN: while( $loop ) { } # end foreach event_link chdir( $Storage->Path() ); } # end foreach day dir - } elsif ( $$Storage{Scheme} eq 'Medium' ) { - foreach my $event_dir ( glob("$monitor_dir/*/*") ) { + + foreach my $event_dir ( glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") ) { next if ! -d $event_dir; my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/; if ( ! $event_id ) { @@ -270,11 +290,13 @@ MAIN: while( $loop ) { my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); $$Event{Id} = $event_id; $$Event{Path} = join('/', $Storage->Path(), $event_dir ); + $$Event{Scheme} = 'Medium'; $$Event{RelativePath} = $event_dir; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); } # end foreach event - } else { + + if ( ! $$Storage{Scheme} ) { if ( ! chdir( $monitor_dir ) ) { Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" ); next; @@ -310,7 +332,6 @@ MAIN: while( $loop ) { my $Event = $fs_events->{$fs_event_id}; - if ( ! defined( $db_events->{$fs_event_id} ) ) { my $age = $Event->age(); @@ -373,12 +394,12 @@ MAIN: while( $loop ) { while ( my ( $db_event, $age ) = each( %$db_events ) ) { if ( ! defined( $fs_events->{$db_event} ) ) { - Debug("Event $db_event is not in fs."); my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); if ( ! $Event ) { Debug("Event $db_event is no longer in db. Filter probably deleted it while we were auditing."); next; } + Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); if ( ! $Event->StartTime() ) { Info("Event $$Event{Id} has no start time. deleting it."); if ( confirm() ) { @@ -401,15 +422,13 @@ MAIN: while( $loop ) { Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() ); } else { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { - aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem' ); + aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting' ); if ( confirm() ) { $Event->delete(); $cleaned = 1; } } else { - my $Storage = $Event->Storage(); - - aud_print( "Database event '".$Event->getPath()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); + aud_print( "Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); } } # end if exists in filesystem } # end if ! in fs_events @@ -651,7 +670,7 @@ $eventcounts_sth->finish(); sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous; }; -term_handler(); +Term(); sub aud_print { my $string = shift; @@ -766,6 +785,7 @@ yet. -f, --force - Run even if pid file exists -i, --interactive - Ask before applying any changes -r, --report - Just report don't actually do anything + -s, --storage_id - Specify a storage area to audit instead of all -v, --version - Print the installed version of ZoneMinder =cut diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index e7198fdd7..623769d81 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -185,7 +185,7 @@ use Sys::CpuLoad; my $attempts = 0; while( !connect( CLIENT, $saddr ) ) { $attempts++; - Error("Waiting for zmdc.pl server process at " . SOCK_FILE.", attempt $attempts" ); + Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts" ); Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); usleep(200000); } # end while @@ -566,13 +566,19 @@ sub restart { my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if @args; + dPrint ( ZoneMinder::Logger::WARNING, "Restarting $command\n"); my $process = $cmd_hash{$command}; if ( $process ) { + dPrint ( ZoneMinder::Logger::WARNING, "Have process" ); if ( $process->{pid} ) { + dPrint ( ZoneMinder::Logger::WARNING, "Have process pid " .$process->{pid} ); my $cpid = $process->{pid}; if ( defined($pid_hash{$cpid}) ) { + dPrint ( ZoneMinder::Logger::WARNING, "Have process pid hash " .$process->{pid} ); _stop( 0, $process ); return; + } else { + dPrint ( ZoneMinder::Logger::WARNING, "Not sending stop" ); } } } diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 13e643c7a..3ea850ef5 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -153,7 +153,10 @@ my $eventcounts_week_sth = $dbh->prepare_cached( $eventcounts_week_sql ); my $eventcounts_month_sth = $dbh->prepare_cached( $eventcounts_month_sql ); while( 1 ) { - $dbh->ping(); + while ( ! ( $dbh and $dbh->ping() ) ) { + Info("Reconnecting to db"); + $dbh = zmDbConnect(); + } $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)'); $dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)'); diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 5e7b72411..f428ea1f9 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -159,7 +159,7 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { my $ua = LWP::UserAgent->new; $ua->agent( "ZoneMinder Update Agent/".ZM_VERSION ); if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { - $ua->proxy( "http", $Config{ZM_UPDATE_CHECK_PROXY} ); + $ua->proxy( 'http', $Config{ZM_UPDATE_CHECK_PROXY} ); } my $req = HTTP::Request->new( GET=>'https://update.zoneminder.com/version.txt' ); my $res = $ua->request($req); @@ -224,7 +224,7 @@ if ( $zoneFix ) { } $sth->finish(); - $sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?"; + $sql = 'update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?'; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $zone ( @zones ) { my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; @@ -378,7 +378,7 @@ if ( $version ) { if ( $response =~ /^[yY]$/ ) { my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); - my $command = "mysqldump"; + my $command = 'mysqldump'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { $command .= " -S".$portOrSocket; @@ -503,7 +503,7 @@ if ( $version ) { my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { - my $sql = "update Events set Width = ?, Height = ? where MonitorId = ?"; + my $sql = 'update Events set Width = ?, Height = ? where MonitorId = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $monitor->{Width}, $monitor->{Height}, $monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -518,7 +518,7 @@ if ( $version ) { my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); my $sequence = 1; while( my $monitor = $sth->fetchrow_hashref() ) { - my $sql = "update Monitors set Sequence = ? where Id = ?"; + my $sql = 'update Monitors set Sequence = ? where Id = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $sequence++, $monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -536,7 +536,7 @@ if ( $version ) { push( @filters, $filter ); } $sth->finish(); - $sql = "update Filters set Query = ? where Name = ?"; + $sql = 'update Filters set Query = ? where Name = ?'; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $filter ( @filters ) { if ( $filter->{Query} =~ /op\d=&/ ) { @@ -569,7 +569,7 @@ if ( $version ) { no strict 'refs'; foreach my $zone ( @zones ) { # Create the coordinate strings - if ( $zone->{Units} eq "Pixels" ) { + if ( $zone->{Units} eq 'Pixels' ) { my $sql = "update Zones set NumCoords = 4, Coords = concat( LoX,',',LoY,' ',HiX,',',LoY,' ',HiX,',',HiY,' ',LoX,',',HiY ), Area = round( ((HiX-LoX)+1)*((HiY-LoY)+1) ) where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $zone->{Id} ) or die( "Can't execute: ".$sth->errstr() ); @@ -604,7 +604,7 @@ if ( $version ) { foreach my $defn ( split( /,/, $state->{Definition} ) ) { push( @new_defns, $defn.":1" ); } - my $sql = "update States set Definition = ? where Name = ?"; + my $sql = 'update States set Definition = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( join( ',', @new_defns ), $state->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -684,7 +684,7 @@ if ( $version ) { $db_monitor->{LabelFormat} =~ s/\%\%s/%N/; $db_monitor->{LabelFormat} =~ s/\%\%s/%Q/; - my $sql = "update Monitors set LabelFormat = ? where Id = ?"; + my $sql = 'update Monitors set LabelFormat = ? where Id = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $db_monitor->{LabelFormat}, $db_monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -743,7 +743,7 @@ if ( $version ) { $i++; } $newQuery .= '}'; - foreach my $field ( "sort_field", "sort_asc", "limit" ) { + foreach my $field ( 'sort_field', 'sort_asc', 'limit' ) { if ( defined($filter->{$field}) ) { $newQuery .= 's:'.length($field).':"'.$field.'";'; $newQuery .= 's:'.length($filter->{$field}).':"'.$filter->{$field}.'";'; @@ -751,7 +751,7 @@ if ( $version ) { } $newQuery .= '}'; - my $sql = "update Filters set Query = ? where Name = ?"; + my $sql = 'update Filters set Query = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $newQuery, $dbFilter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } @@ -829,7 +829,7 @@ if ( $version ) { my $phpQuery = $dbFilter->{Query}; my $query = PHP::Serialization::unserialize( $phpQuery ); my $jsonQuery = jsonEncode( $query ); - my $sql = "update Filters set Query = ? where Name = ?"; + my $sql = 'update Filters set Query = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $jsonQuery, $dbFilter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); }; @@ -851,7 +851,7 @@ if ( $version ) { # Copy the FTP specific values to the new general config my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'"; my $fetchSth = $dbh->prepare_cached( $fetchSql ) or die( "Can't prepare '$fetchSql': ".$dbh->errstr() ); - my $updateSql = "update Config set Value = ? where Name = ?"; + my $updateSql = 'update Config set Value = ? where Name = ?'; my $updateSth = $dbh->prepare_cached( $updateSql ) or die( "Can't prepare '$updateSql': ".$dbh->errstr() ); my $fetchRes = $fetchSth->execute() or die( "Can't execute: ".$fetchSth->errstr() ); while( my $config = $fetchSth->fetchrow_hashref() ) { @@ -881,7 +881,7 @@ if ( $version ) { if ( $version ge '1.26.0' ) { my @files; - $updateDir = $Config{ZM_PATH_DATA}."/db" if ! $updateDir; + $updateDir = $Config{ZM_PATH_DATA}.'/db' if ! $updateDir; opendir( my $dh, $updateDir ) || die "Can't open updateDir $!"; #@files = sort grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); #PP - use perl version sort @@ -895,29 +895,28 @@ if ( $version ) { die "Should have found upgrade scripts at $updateDir\n"; } # end if - $dbh->{'AutoCommit'} = 0; foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; #PP make sure we use version compare if ( version->parse('v' . $v) > version->parse('v' . $version) ) { print( "Upgrading DB to $v from $version\n" ); patchDB( $dbh, $v ); - if ( $dbh->errstr() ) { - $dbh->rollback(); - die "Error: " . $dbh->errstr(). ". Rolled back.\n"; - } # end if error - } # end if + my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + #patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch ); + } # end if newer version } # end foreach patchfile - $dbh->{'AutoCommit'} = 1; $cascade = !undef; } # end if if ( $cascade ) { my $installed_version = ZM_VERSION; - my $sql = "update Config set Value = ? where Name = ?"; + my $sql = 'update Config set Value = ? where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$installed_version", "ZM_DYN_DB_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); - $res = $sth->execute( "$installed_version", "ZM_DYN_CURR_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); + my $res = $sth->execute( "$installed_version", 'ZM_DYN_DB_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); + $res = $sth->execute( "$installed_version", 'ZM_DYN_CURR_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); $sth->finish(); } else { zmDbDisconnect(); @@ -928,10 +927,36 @@ if ( $version ) { zmDbDisconnect(); exit( 0 ); +sub patchDB_using_do { + my ( $dbh, $version, $file ) = @_; + + open( my $fh, '<', $file ) or die "Unable to open $file $!"; + $/ = undef; + my $sql = <$fh>; + close $fh; + if ( $sql ) { + $dbh->{'AutoCommit'} = 0; + $dbh->do($sql); + if ( $dbh->errstr() ) { + $dbh->rollback(); + die "Error: " . $dbh->errstr(). ". Rolled back.\n"; + } # end if error + + my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + + $dbh->{'AutoCommit'} = 1; + } else { + Warning("Empty db update file at $file"); + } +} sub patchDB { my $dbh = shift; my $version = shift; + my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = 'mysql'; if ( defined($portOrSocket) ) { @@ -966,10 +991,7 @@ sub patchDB { die( "Command '$command' exited with status: $status\n" ); } print( "\nDatabase successfully upgraded to version $version.\n" ); - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + } sub migratePaths { diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 43e9696f7..5ddd8ee82 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -26,9 +26,13 @@ void FFMPEGInit() { static bool bInit = false; - if(!bInit) { + if ( !bInit ) { + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); av_register_all(); - av_log_set_level(AV_LOG_DEBUG); + avformat_network_init(); bInit = true; } } @@ -106,26 +110,25 @@ static int parse_key_value_pair(AVDictionary **pm, const char **buf, return ret; } int av_dict_parse_string(AVDictionary **pm, const char *str, - const char *key_val_sep, const char *pairs_sep, - int flags) - { - if (!str) - return 0; - - /* ignore STRDUP flags */ - flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); - - while (*str) { - int ret; - if ( (ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0) - return ret; - - if (*str) - str++; - } - - return 0; + const char *key_val_sep, const char *pairs_sep, + int flags) { + if (!str) + return 0; + + /* ignore STRDUP flags */ + flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); + + while (*str) { + int ret; + if ( (ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0) + return ret; + + if (*str) + str++; } + + return 0; +} #endif #endif // HAVE_LIBAVUTIL @@ -188,30 +191,29 @@ int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat if (ret) { avformat_free_context(s); return ret; - } else { - s->oformat = oformat; -#if 1 - // This is some very wrong code, and I don't think it is neccessary - if (s->oformat->priv_data_size > 0) { - s->priv_data = av_mallocz(s->oformat->priv_data_size); - if (s->priv_data) { - if (s->oformat->priv_class) { - *(const AVClass**)s->priv_data= s->oformat->priv_class; - av_opt_set_defaults(s->priv_data); - } + } + + s->oformat = oformat; +#if 0 + if (s->oformat->priv_data_size > 0) { + if (s->oformat->priv_class) { + // This looks wrong, we just allocated priv_data and now we are losing the pointer to it.FIXME + *(const AVClass**)s->priv_data = s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); } else { - av_log(s, AV_LOG_ERROR, "Out of memory\n"); - ret = AVERROR(ENOMEM); - return ret; - } - s->priv_data = NULL; + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if ( ! s->priv_data) { + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); + return ret; } + s->priv_data = NULL; + } #endif - if (filename) strncpy(s->filename, filename, sizeof(s->filename)-1); - *avctx = s; - return 0; - } + if (filename) strncpy(s->filename, filename, sizeof(s->filename)-1); + *avctx = s; + return 0; } static void zm_log_fps(double d, const char *postfix) { @@ -228,22 +230,32 @@ static void zm_log_fps(double d, const char *postfix) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) void zm_dump_codecpar ( const AVCodecParameters *par ) { - Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) codec_tag(%d) width(%d) height(%d)", + Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)", par->codec_type, par->codec_id, par->codec_tag, par->width, - par->height + par->height, + par->bit_rate, + par->format, + ((AVPixelFormat)par->format == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format)) ); } #endif void zm_dump_codec ( const AVCodecContext *codec ) { - Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) width(%d) height(%d)", + Debug(1, "Dumping codec_context codec_type(%d) codec_id(%d) width(%d) height(%d) timebase(%d/%d) format(%s)", codec->codec_type, codec->codec_id, codec->width, - codec->height + codec->height, + codec->time_base.num, + codec->time_base.den, +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + (codec->pix_fmt == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name(codec->pix_fmt)) +#else + "unsupported on avconv" +#endif ); } @@ -255,7 +267,6 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) AVStream *st = ic->streams[i]; AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); - avcodec_string(buf, sizeof(buf), st->codec, is_output); Debug(1, " Stream #%d:%d", index, i); /* the pid is an important information, so we display it */ @@ -264,15 +275,21 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) Debug(1, "[0x%x]", st->id); if (lang) Debug(1, "(%s)", lang->value); - Debug(1, ", %d, %d/%d", st->codec_info_nb_frames, st->time_base.num, st->time_base.den); + Debug(1, ", frames:%d, timebase: %d/%d", st->codec_info_nb_frames, st->time_base.num, st->time_base.den); + avcodec_string(buf, sizeof(buf), st->codec, is_output); Debug(1, ": %s", buf); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + AVCodecParameters *codec = st->codecpar; +#else + AVCodecContext *codec = st->codec; +#endif if (st->sample_aspect_ratio.num && // default - av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) { + av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio)) { AVRational display_aspect_ratio; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, - st->codec->width * (int64_t)st->sample_aspect_ratio.num, - st->codec->height * (int64_t)st->sample_aspect_ratio.den, + codec->width * (int64_t)st->sample_aspect_ratio.num, + codec->height * (int64_t)st->sample_aspect_ratio.den, 1024 * 1024); Debug(1, ", SAR %d:%d DAR %d:%d", st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, @@ -337,8 +354,10 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) { #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #else unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) { - dst->data = reinterpret_cast(new uint64_t[(src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1]); + dst->size = (src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1; + dst->data = reinterpret_cast(new uint64_t[dst->size]); memcpy(dst->data, src->data, src->size ); + dst->flags = src->flags; return 0; } #endif @@ -373,3 +392,71 @@ bool is_audio_stream( AVStream * stream ) { } return false; } + +int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) { + int ret; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + if ( (ret = avcodec_send_packet(context, &packet)) < 0 ) { + Error( "Unable to send packet %s, continuing", + av_make_error_string(ret).c_str() ); + return 0; + } + +#if HAVE_AVUTIL_HWCONTEXT_H + if ( hwaccel ) { + if ( (ret = avcodec_receive_frame(context, hwFrame)) < 0 ) { + Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + if ( (ret = av_hwframe_transfer_data(frame, hwFrame, 0)) < 0 ) { + Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, + av_make_error_string(ret).c_str() ); + return 0; + } + } else { +#endif + if ( (ret = avcodec_receive_frame(context, frame)) < 0 ) { + Error( "Unable to send packet %s, continuing", av_make_error_string(ret).c_str() ); + return 0; + } +#if HAVE_AVUTIL_HWCONTEXT_H + } +#endif + +# else + int frameComplete; + while ( !frameComplete ) { + if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) { + Error( "Unable to decode frame at frame: %s, continuing", + av_make_error_string(ret).c_str() ); + return 0; + } + } +#endif + return 1; +} // end int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) +void dumpPacket(AVPacket *pkt, const char *text) { + char b[10240]; + + snprintf(b, sizeof(b), + " pts: %" PRId64 ", dts: %" PRId64 + ", data: %p, size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64 + ", duration: %" +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + PRId64 +#else + "d" +#endif + "\n", + pkt->pts, + pkt->dts, + pkt->data, + pkt->size, + pkt->stream_index, + pkt->flags, + pkt->flags & AV_PKT_FLAG_KEY, + pkt->pos, + pkt->duration); + Debug(1, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); +} diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 10ad5260c..7e3cf9651 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -237,9 +237,8 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp */ #ifdef __cplusplus - inline static const std::string av_make_error_string(int errnum) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; + inline static const std::string av_make_error_string(int errnum) { + static char errbuf[AV_ERROR_MAX_STRING_SIZE]; #if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0) av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); #else @@ -327,4 +326,6 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt); bool is_video_stream( AVStream * stream ); bool is_audio_stream( AVStream * stream ); +int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ); +void dumpPacket(AVPacket *,const char *text="DEBUG"); #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index bf7a6ab13..069d68b63 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -738,7 +738,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event int ret; static char errbuf[AV_ERROR_MAX_STRING_SIZE]; - // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. + // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened + // the connection to the ffmpeg device, and we can clean up the thread. if ( mReopenThread != 0 ) { void *retval = 0; @@ -751,12 +752,10 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event mReopenThread = 0; } - int frameComplete = false; while ( ! frameComplete ) { av_init_packet( &packet ); - Debug(4,"before read frame"); ret = av_read_frame( mFormatContext, &packet ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); @@ -775,11 +774,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event } int keyframe = packet.flags & AV_PKT_FLAG_KEY; - - Debug( 4, "Got packet from stream %d packet pts (%u) dts(%u), key?(%d)", - packet.stream_index, packet.pts, packet.dts, - keyframe - ); + dumpPacket(&packet); //Video recording if ( recording.tv_sec ) { @@ -841,6 +836,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event startTime, this->getMonitor()); } // end if record_audio + if ( ! videoStore->open() ) { delete videoStore; videoStore = NULL; @@ -896,28 +892,18 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if ( keyframe ) { Debug(3, "Clearing queue"); packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId ); + packetqueue.queuePacket( &packet ); + } else if ( packetqueue.size() ) { + // it's a keyframe or we already have something in the queue + packetqueue.queuePacket( &packet ); } -#if 0 -// Not sure this is valid. While a camera will PROBABLY always have an increasing pts... it doesn't have to. -// Also, I think there are integer wrap-around issues. - -else if ( packet.pts && video_last_pts > packet.pts ) { - Warning( "Clearing queue due to out of order pts packet.pts(%d) < video_last_pts(%d)"); - packetqueue.clearQueue(); - } -#endif - } - + } else if ( packet.stream_index == mAudioStreamId ) { // The following lines should ensure that the queue always begins with a video keyframe - if ( packet.stream_index == mAudioStreamId ) { //Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() ); if ( record_audio && packetqueue.size() ) { // if it's audio, and we are doing audio, and there is already something in the queue packetqueue.queuePacket( &packet ); } - } else if ( packet.stream_index == mVideoStreamId ) { - if ( keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue - packetqueue.queuePacket( &packet ); } } // end if recording or not diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index dc097d1ab..980a41645 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1205,7 +1205,7 @@ bool Monitor::Analyse() { fps = double(fps_report_interval)/(now.tv_sec - last_fps_time); Info( "%s: %d - Analysing at %.2f fps", name, image_count, fps ); static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "UPDATE Monitors SET AnalysisFPS = '%.2lf' WHERE Id = '%d'", fps, id ); + snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (Id,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } @@ -3022,7 +3022,7 @@ Debug(4, "Return from Capture (%d)", captureResult); Info( "%s: images:%d - Capturing at %.2lf fps", name, image_count, fps ); last_fps_time = now; static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "UPDATE Monitors SET CaptureFPS='%.2lf' WHERE Id=%d", fps, id ); + snprintf( sql, sizeof(sql), "INSERT INTO Monitor_Status (Id,CaptureFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf", id, fps, fps ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 766882796..950cfd576 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -88,7 +88,7 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream ZMPacket *zm_packet = *it; AVPacket *av_packet = &(zm_packet->packet); - Debug(4, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep ); + Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep ); // Want frames_to_keep video keyframes. Otherwise, we may not have enough if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) { @@ -157,7 +157,7 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi } } if ( it == pktQueue.rend() ) { - Debug(1, "Didn't find a keyframe packet keeping all" ); + Debug(1, "Didn't find a keyframe before event starttime. keeping all" ); return; } diff --git a/src/zm_storage.cpp b/src/zm_storage.cpp index fde088db1..3f80e26aa 100644 --- a/src/zm_storage.cpp +++ b/src/zm_storage.cpp @@ -36,8 +36,8 @@ Storage::Storage() { } else { strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1 ); } - scheme = DEEP; - scheme_str = "Deep"; + scheme = MEDIUM; + scheme_str = "Medium"; } Storage::Storage( MYSQL_ROW &dbrow ) { @@ -93,6 +93,8 @@ Storage::Storage( unsigned int p_id ) { } Debug(1,"No id passed to Storage constructor. Using default path %s instead", path ); strcpy(name, "Default"); + scheme = MEDIUM; + scheme_str = "Medium"; } } diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 1b8ad0028..d530c6716 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -707,18 +707,6 @@ bool VideoStore::setup_resampler() { #endif } // end bool VideoStore::setup_resampler() -void VideoStore::dumpPacket(AVPacket *pkt) { - char b[10240]; - - snprintf(b, sizeof(b), - " pts: %" PRId64 ", dts: %" PRId64 - ", data: %p, size: %d, sindex: %d, dflags: %04x, s-pos: %" PRId64 - ", c-duration: %d\n", - pkt->pts, pkt->dts, pkt->data, pkt->size, pkt->stream_index, - pkt->flags, pkt->pos, pkt->duration); - Debug(1, "%s:%d:DEBUG: %s", __FILE__, __LINE__, b); -} - int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { av_init_packet(&opkt); @@ -826,10 +814,7 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { AVPacket safepkt; memcpy(&safepkt, &opkt, sizeof(AVPacket)); - Debug(1, - "writing video packet keyframe(%d) pts(%d) dts(%d) duration(%d) " - "ipkt.duration(%d)", - opkt.flags & AV_PKT_FLAG_KEY, opkt.pts, opkt.dts, duration, ipkt->duration); + dumpPacket( &opkt, "writing video packet" ); if ((opkt.data == NULL) || (opkt.size < 1)) { Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__); dumpPacket(ipkt); @@ -851,7 +836,7 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { Warning( "%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) " " ", - __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret)); + __FILE__, __LINE__, av_make_error_string(ret).c_str(), ret); dumpPacket(&safepkt); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_dump_codecpar(video_in_stream->codecpar); diff --git a/src/zm_videostore.h b/src/zm_videostore.h index c1a09fc35..6d2b15cd0 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -80,7 +80,6 @@ public: int writeVideoFramePacket( AVPacket *pkt ); int writeAudioFramePacket( AVPacket *pkt ); - void dumpPacket( AVPacket *pkt ); }; #endif //havelibav diff --git a/src/zmc.cpp b/src/zmc.cpp index a58e04a86..5169b0699 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -223,7 +223,7 @@ int main(int argc, char *argv[]) { Info("Starting Capture version %s", ZM_VERSION); static char sql[ZM_SQL_SML_BUFSIZ]; for ( int i = 0; i < n_monitors; i ++ ) { - snprintf( sql, sizeof(sql), "UPDATE Monitors SET Status = 'Running' WHERE Id = '%d'", monitors[i]->Id() ); + snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (Id, Status ) VALUES ('%d','Running')", monitors[i]->Id() ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } diff --git a/version b/version index fca4bdfac..9e60186a4 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.26 +1.31.28 diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 1f0245a69..94b621ed4 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -55,7 +55,7 @@ $remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.soc $max_socket_tries = 10; // FIXME This should not exceed web_ajax_timeout while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second. - Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' ); + //Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' ); usleep(200000); } diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index d06ce1dfe..c372463c1 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -171,7 +171,7 @@ class MonitorsController extends AppController { $func = $Monitor['Function']; // We don't pass the request data as the monitor object because it may be a subset of the full monitor array $this->daemonControl( $this->Monitor->id, 'stop' ); - if ( ( $func != 'None' ) and ( $Monitor['ServerId'] == ZM_SERVER_ID ) ) { + if ( ( $func != 'None' ) and ( (!defined('ZM_SERVER_ID')) or ($Monitor['ServerId']==ZM_SERVER_ID) ) ) { $this->daemonControl( $this->Monitor->id, 'start' ); } } // end function edit diff --git a/web/includes/Event.php b/web/includes/Event.php index ab3ca6687..53c808d6c 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -211,14 +211,16 @@ class Event { } function createListThumbnail( $overwrite=false ) { + if ( (!$this->SaveJPEGs()) and file_exists($this->Path().'/snapshot.jpg') ) { + $frame = null; + } else { # Load the frame with the highest score to use as a thumbnail if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) { Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} ); // FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame. return( false ); } - - $frameId = $frame['FrameId']; + } if ( ZM_WEB_LIST_THUMB_WIDTH ) { $thumbWidth = ZM_WEB_LIST_THUMB_WIDTH; @@ -259,7 +261,7 @@ class Event { if ( ( ! $frame ) and file_exists( $eventPath.'/snapshot.jpg' ) ) { # No frame specified, so look for a snapshot to use $captImage = 'snapshot.jpg'; - Debug("Frame not specified, using snapshot"); + Logger::Debug("Frame not specified, using snapshot"); } else { $captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyze.jpg', $frame['FrameId'] ); if ( ! file_exists( $eventPath.'/'.$captImage ) ) { @@ -282,7 +284,7 @@ class Event { exec( $command, $output, $retval ); Logger::Debug("Retval: $retval, output: " . implode("\n", $output)); } else { - Error("Can't create frame images from video becuase there is no video file for this event (".$Event->DefaultVideo() ); + Error("Can't create frame images from video because there is no video file for event ".$Event->Id().' at ' .$Event->Path() ); } } // end if capture file exists } // end if analyze file exists diff --git a/web/includes/actions.php b/web/includes/actions.php index 5b0347499..86a08cd72 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -505,10 +505,17 @@ Logger::Debug("NOT Auto selecting server" . $_REQUEST['newMonitor']['ServerId']) zmaControl( $monitor, 'stop' ); zmcControl( $monitor, 'stop' ); dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); - if ( isset($changes['Name']) ) { + if ( isset($changes['Name']) or isset($changes['StorageId']) ) { + $OldStorage = new Storage( $monitor['StorageId'] ); $saferOldName = basename( $monitor['Name'] ); + if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) ) + unlink( $OldStorage->Path().'/'.$saferOldName ); + + $NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); + if ( ! file_exists( $NewStorage->Path().'/'.$mid ) ) + mkdir( $NewStorage->Path().'/'.$mid, 0755 ); $saferNewName = basename( $_REQUEST['newMonitor']['Name'] ); - rename( ZM_DIR_EVENTS.'/'.$saferOldName, ZM_DIR_EVENTS.'/'.$saferNewName); + symlink( $mid, $NewStorage->Path().'/'.$saferNewName ); } if ( isset($changes['Width']) || isset($changes['Height']) ) { $newW = $_REQUEST['newMonitor']['Width']; @@ -553,9 +560,10 @@ Logger::Debug("NOT Auto selecting server" . $_REQUEST['newMonitor']['ServerId']) $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); //$view = 'none'; - mkdir( ZM_DIR_EVENTS.'/'.$mid, 0755 ); + $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); + mkdir( $Storage->Path().'/'.$mid, 0755 ); $saferName = basename($_REQUEST['newMonitor']['Name']); - symlink( $mid, ZM_DIR_EVENTS.'/'.$saferName ); + symlink( $mid, $Storage->Path().'/'.$saferName ); if ( isset($_COOKIE['zmGroup']) ) { dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) ); } diff --git a/web/includes/database.php b/web/includes/database.php index 29ec1fd77..6bc6c0264 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -134,12 +134,12 @@ function dbQuery( $sql, $params=NULL ) { } else { $result = $dbConn->query( $sql ); } -if ( defined('ZM_DB_DEBUG') ) { - if ( $params ) - Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); - else - Warning("SQL: $sql: rows:" . $result->rowCount() ); -} + if ( defined('ZM_DB_DEBUG') ) { + if ( $params ) + Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); + else + Warning("SQL: $sql: rows:" . $result->rowCount() ); + } } catch(PDOException $e) { Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') ); } diff --git a/web/includes/functions.php b/web/includes/functions.php index a5df103da..2214d1cff 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -165,7 +165,7 @@ function generateAuthHash( $useRemoteAddr ) { $backTrace = debug_backtrace(); $file = $backTrace[1]['file']; $line = $backTrace[1]['line']; - Warning("Session is not active. AuthHash will not be cached. called from $file:$line"); + Warning("Session is not active. AuthHash will not be cached. called from $file:$line. OldHash:" . $_SESSION['AuthHash'] . ' generated at ' . $_SESSION['AuthHashGeneratedAt'] . ' < ' . $time . ' - ( ' . ZM_AUTH_HASH_TTL . '* 1800 = ' . ZM_AUTH_HASH_TTL * 1800 ); } $_SESSION['AuthHash'] = $auth; $_SESSION['AuthHashGeneratedAt'] = $time; @@ -1236,31 +1236,38 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) { case 'ServerId': if ( $value == 'ZM_SERVER_ID' ) { $value = ZM_SERVER_ID; + } else if ( $value == 'NULL' ) { + } else { $value = dbEscape($value); } break; case 'StorageId': $StorageArea = new Storage( $value ); - $value = dbEscape($value); + if ( $value != 'NULL' ) + $value = dbEscape($value); break; case 'DateTime': case 'StartDateTime': case 'EndDateTime': - $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; + if ( $value != 'NULL' ) + $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; break; case 'Date': case 'StartDate': case 'EndDate': - $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; + if ( $value != 'NULL' ) + $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; case 'Time': case 'StartTime': case 'EndTime': + if ( $value != 'NULL' ) $value = "extract( hour_second from '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; default : - $value = dbEscape($value); + if ( $value != 'NULL' ) + $value = dbEscape($value); break; } $valueList[] = $value; diff --git a/web/index.php b/web/index.php index eb406ceb3..3ebc58a98 100644 --- a/web/index.php +++ b/web/index.php @@ -203,7 +203,7 @@ if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $vie require_once( 'includes/actions.php' ); # If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in. -if ( ZM_OPT_USE_AUTH && ! isset($user) ) { +if ( ZM_OPT_USE_AUTH and ! isset($user) ) { Logger::Debug("Redirecting to login" ); $view = 'login'; $request = null; diff --git a/web/skins/classic/css/base/views/control.css b/web/skins/classic/css/base/views/control.css index b3d620f5c..90757a146 100644 --- a/web/skins/classic/css/base/views/control.css +++ b/web/skins/classic/css/base/views/control.css @@ -1,7 +1,7 @@ .ptzControls { vertical-align: top; margin: 10px auto 0; - width: 500px; + width: 520px; } .ptzControls::after { diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index f384594da..8a53394fa 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -71,7 +71,9 @@ html ul.tabs li.active, html ul.tabs li.active a:hover { } --> - +