Merge branch 'ZoneMinder:master' into janus

This commit is contained in:
Jonathan Bennett 2022-01-13 12:16:16 -06:00 committed by GitHub
commit 212d51f933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 310 additions and 251 deletions

@ -1 +1 @@
Subproject commit 1b40f1661f93f50fd5805f239d1e466a3bcf888f Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878

View File

@ -5,7 +5,8 @@
Description=ZoneMinder CCTV recording and surveillance system Description=ZoneMinder CCTV recording and surveillance system
After=network.target mysql.service After=network.target mysql.service
# Remarked out so that it will start ZM on machines that don't have mysql installed # Remarked out so that it will start ZM on machines that don't have mysql installed
#Requires=mysql.service # Override it by placing an override.conf in /etc/systemd/system/zoneminder.service.d
#BindsTo=mysql.service
[Service] [Service]
#User=www-data #User=www-data

View File

@ -273,6 +273,9 @@ sub zmDbDo {
if ( ! defined $rows ) { if ( ! defined $rows ) {
$sql =~ s/\?/'%s'/; $sql =~ s/\?/'%s'/;
Error(sprintf("Failed $sql :", @_).$dbh->errstr()); Error(sprintf("Failed $sql :", @_).$dbh->errstr());
} elsif ( ZoneMinder::Logger::logLevel() > INFO ) {
$sql =~ s/\?/'%s'/;
Debug(sprintf("Succeeded $sql : $rows rows affected", @_));
} }
return $rows; return $rows;
} }

View File

@ -639,9 +639,9 @@ $log->debug("Have array for $k $$search{$k}") if DEBUG_ALL;
if ( ! ( $db_field =~ /\?/ ) ) { if ( ! ( $db_field =~ /\?/ ) ) {
if ( @{$$search{$k}} != 1 ) { if ( @{$$search{$k}} != 1 ) {
push @where, $db_field .' IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')'; push @where, '`'.$db_field .'` IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
} else { } else {
push @where, $db_field.'=?'; push @where, '`'.$db_field.'`=?';
} # end if } # end if
} else { } else {
$log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL; $log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
@ -656,10 +656,10 @@ $log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
foreach my $p_k ( keys %{$$search{$k}} ) { foreach my $p_k ( keys %{$$search{$k}} ) {
my $v = $$search{$k}{$p_k}; my $v = $$search{$k}{$p_k};
if ( ref $v eq 'ARRAY' ) { if ( ref $v eq 'ARRAY' ) {
push @where, $db_field.' IN ('.join(',', map {'?'} @{$v} ) . ')'; push @where, '`'.$db_field.'` IN ('.join(',', map {'?'} @{$v} ) . ')';
push @values, $p_k, @{$v}; push @values, $p_k, @{$v};
} else { } else {
push @where, $db_field.'=?'; push @where, '`'.$db_field.'`=?';
push @values, $p_k, $v; push @values, $p_k, $v;
} # end if } # end if
} # end foreach p_k } # end foreach p_k
@ -667,7 +667,7 @@ $log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
push @where, $db_field.' IS NULL'; push @where, $db_field.' IS NULL';
} else { } else {
if ( ! ( $db_field =~ /\?/ ) ) { if ( ! ( $db_field =~ /\?/ ) ) {
push @where, $db_field .'=?'; push @where, '`'.$db_field .'`=?';
} else { } else {
push @where, $db_field; push @where, $db_field;
} }

View File

@ -42,6 +42,7 @@ use constant SELECT_TIMEOUT => 0.25;
@EXTRA_PERL_LIB@ @EXTRA_PERL_LIB@
use ZoneMinder; use ZoneMinder;
use ZoneMinder::Monitor;
use ZoneMinder::Trigger::Channel::Inet; use ZoneMinder::Trigger::Channel::Inet;
use ZoneMinder::Trigger::Channel::Unix; use ZoneMinder::Trigger::Channel::Unix;
use ZoneMinder::Trigger::Channel::Serial; use ZoneMinder::Trigger::Channel::Serial;
@ -203,14 +204,10 @@ while (!$zm_terminate) {
# Check for alarms that might have happened # Check for alarms that might have happened
my @out_messages; my @out_messages;
foreach my $monitor ( values %monitors ) { foreach my $monitor ( values %monitors ) {
if ($$monitor{Function} eq 'None') { if (!$monitor->connect()) {
$monitor_reload_time = 0;
next;
}
if (!zmMemVerify($monitor)) {
# Our attempt to verify the memory handle failed. We should reload the monitors. # Our attempt to verify the memory handle failed. We should reload the monitors.
# Don't need to zmMemInvalidate because the monitor reload will do it. # Don't need to zmMemInvalidate because the monitor reload will do it.
Debug("Failed connect, putting on reloads");
push @needsReload, $monitor; push @needsReload, $monitor;
next; next;
} }
@ -249,6 +246,7 @@ while (!$zm_terminate) {
} }
$monitor->{LastState} = $state; $monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event; $monitor->{LastEvent} = $last_event;
$monitor->disconnect();
} # end foreach monitor } # end foreach monitor
foreach my $connection ( @out_connections ) { foreach my $connection ( @out_connections ) {
@ -298,15 +296,22 @@ while (!$zm_terminate) {
# Reload all monitors from the dB every MONITOR_RELOAD_INTERVAL # Reload all monitors from the dB every MONITOR_RELOAD_INTERVAL
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) { if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) {
foreach my $monitor ( values(%monitors) ) {
zmMemInvalidate( $monitor ); # Free up any used memory handle
}
loadMonitors(); loadMonitors();
@needsReload = (); # We just reloaded all monitors so no need reload a specific monitor @needsReload = (); # We just reloaded all monitors so no need reload a specific monitor
# If we have NOT just reloaded all monitors, reload a specific monitor if its shared mem changed # If we have NOT just reloaded all monitors, reload a specific monitor if its shared mem changed
} elsif ( @needsReload ) { } elsif (@needsReload) {
foreach my $monitor ( @needsReload ) { foreach my $monitor (@needsReload) {
loadMonitor($monitor); $monitor = $monitors{$monitor->Id()} = ZoneMinder::Monitor->find_one(Id=>$monitor->Id());
if ( $$monitor{Function} eq 'None' ) {
delete $monitors{$monitor->Id()};
} elsif ( $Config{ZM_SERVER_ID} and ($$monitor{ServerId} != $Config{ZM_SERVER_ID})) {
delete $monitors{$monitor->Id()};
} else {
if ($monitor->connect()) {
$monitor->{LastState} = zmGetMonitorState($monitor);
$monitor->{LastEvent} = zmGetLastEvent($monitor);
}
}
} }
@needsReload = (); @needsReload = ();
} }
@ -317,40 +322,21 @@ while (!$zm_terminate) {
Info('Trigger daemon exiting'); Info('Trigger daemon exiting');
exit; exit;
sub loadMonitor {
my $monitor = shift;
Debug('Loading monitor '.$monitor);
zmMemInvalidate($monitor);
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState($monitor);
$monitor->{LastEvent} = zmGetLastEvent($monitor);
}
} # end sub loadMonitor
sub loadMonitors { sub loadMonitors {
$monitor_reload_time = time(); $monitor_reload_time = time();
my %new_monitors = (); %monitors = ();
my $sql = 'SELECT * FROM `Monitors` foreach my $monitor ( ZoneMinder::Monitor->find(
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect,Record\' )'. Function=>['Modect','Mocord','Nodect','Record'],
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' ) ($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ()),
; )) {
my $sth = $dbh->prepare_cached( $sql ) if ($monitor->connect()) { # This will re-init shared memory
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
or Fatal( "Can't execute: ".$sth->errstr() );
while ( my $monitor = $sth->fetchrow_hashref() ) {
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState($monitor); $monitor->{LastState} = zmGetMonitorState($monitor);
$monitor->{LastEvent} = zmGetLastEvent($monitor); $monitor->{LastEvent} = zmGetLastEvent($monitor);
} }
$new_monitors{$monitor->{Id}} = $monitor; $monitors{$monitor->{Id}} = $monitor;
} # end while fetchrow } # end while fetchrow
%monitors = %new_monitors;
} # end sub loadMonitors } # end sub loadMonitors
sub handleMessage { sub handleMessage {

View File

@ -2,7 +2,7 @@
# #
# ========================================================================== # ==========================================================================
# #
# ZoneMinder Update Script, $Date$, $Revision$ # ZoneMinder Update Script
# Copyright (C) 2001-2008 Philip Coombes # Copyright (C) 2001-2008 Philip Coombes
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
@ -31,29 +31,30 @@ zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u <dbu
=head1 DESCRIPTION =head1 DESCRIPTION
This script just checks what the most recent release of ZoneMinder is This script checks what the most recent release of ZoneMinder is
at the the moment. It will eventually be responsible for applying and at the the moment by downloading https://update.zoneminder.com/version.txt.
configuring upgrades etc, including on the fly upgrades. It can also apply and configure upgrades etc, including on the fly upgrades.
=head1 OPTIONS =head1 OPTIONS
-c, --check - Check for updated versions of ZoneMinder -c, --check - Check for updated versions of ZoneMinder.
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi If not interactive zmupdate.pl will stay running, checking every hour.
--migrate-events - Update database structures as per USE_DEEP_STORAGE setting. If interactive will try once, print out result and quit.
-v <version>, --version=<version> - Force upgrade to the current version from <version> -f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
-u <dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB --migrate-events - Update database structures as per USE_DEEP_STORAGE setting.
-p <dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB -v <version>, --version=<version> - Force upgrade to the current version from <version>
-s, --super - Use system maintenance account on debian based systems instead of unprivileged account -u <dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
-d <dir>, --dir=<dir> - Directory containing update files if not in default build location -p <dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
-interactive - interact with the user -s, --super - Use system maintenance account on debian based systems instead of unprivileged account
-nointeractive - do not interact with the user -d <dir>, --dir=<dir> - Directory containing update files if not in default build location
-interactive - interact with the user
-nointeractive - do not interact with the user
=cut =cut
use strict; use strict;
use warnings;
use bytes; use bytes;
use version; use version;
use Crypt::Eksblowfish::Bcrypt;
use Data::Entropy::Algorithms qw(rand_bits);
# ========================================================================== # ==========================================================================
# #
@ -122,9 +123,8 @@ GetOptions(
) or pod2usage(-exitstatus => -1); ) or pod2usage(-exitstatus => -1);
my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } ); my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } );
if ( !$dbh ) { die "Unable to connect to db\n" if !$dbh;
die "Unable to connect to db\n";
}
$Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_USER} = $dbUser;
$Config{ZM_DB_PASS} = $dbPass; $Config{ZM_DB_PASS} = $dbPass;
# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it. # we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it.
@ -144,8 +144,10 @@ if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0))
pod2usage(-exitstatus => -1); pod2usage(-exitstatus => -1);
} }
if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { if ($check and ($Config{ZM_CHECK_FOR_UPDATES} or $interactive) ) {
print('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n"); if (!$interactive) {
Info('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
}
my $currVersion = $Config{ZM_DYN_CURR_VERSION}; my $currVersion = $Config{ZM_DYN_CURR_VERSION};
my $lastVersion = $Config{ZM_DYN_LAST_VERSION}; my $lastVersion = $Config{ZM_DYN_LAST_VERSION};
@ -153,16 +155,14 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
if ( !$currVersion ) { if ( !$currVersion ) {
$currVersion = $Config{ZM_VERSION}; $currVersion = $Config{ZM_VERSION};
zmDbDo("UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_CURR_VERSION'", $currVersion);
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'";
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($currVersion) or die("Can't execute: ".$sth->errstr());
$sth->finish();
} }
while ( 1 ) { while ( 1 ) {
my $now = time(); my $now = time();
if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) { if ( !$interactive and $lastVersion and $lastCheck and (($now-$lastCheck) <= CHECK_INTERVAL) ) {
Debug("Not checking for updates since we already have less than " . CHECK_INTERVAL . " seconds ago.");
} else {
Info('Checking for updates'); Info('Checking for updates');
use LWP::UserAgent; use LWP::UserAgent;
@ -175,21 +175,18 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
my $res = $ua->request($req); my $res = $ua->request($req);
if ( $res->is_success ) { if ( $res->is_success ) {
$lastVersion = $res->content; my $latestVersion = $res->content;
chomp($lastVersion); chomp($latestVersion);
$lastCheck = $now; $lastCheck = $now;
Info('Got version: '.$lastVersion); Info('Got version: '.$latestVersion);
my $lv_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\''; zmDbDo('UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'', $latestVersion);
my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr()); zmDbDo('UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\'', $lastCheck);
my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr()); if ($interactive) {
$lv_sth->finish(); print("Last version $lastVersion, Latest version $latestVersion, our version " . ZM_VERSION."\n");
exit(0);
my $lc_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\''; }
my $lc_sth = $dbh->prepare_cached($lc_sql) or die("Can't prepare '$lc_sql': ".$dbh->errstr());
my $lc_res = $lc_sth->execute($lastCheck) or die("Can't execute: ".$lc_sth->errstr());
$lc_sth->finish();
} else { } else {
Error('Error check failed: \''.$res->status_line().'\''); Error('Error check failed: \''.$res->status_line().'\'');
} }
@ -1044,14 +1041,16 @@ sub patchDB {
} # end sub patchDB } # end sub patchDB
sub migratePasswords { sub migratePasswords {
print ("Migratings passwords, if any...\n"); use Crypt::Eksblowfish::Bcrypt;
use Data::Entropy::Algorithms qw(rand_bits);
print("Migratings passwords, if any...\n");
my $sql = 'SELECT * FROM `Users`'; my $sql = 'SELECT * FROM `Users`';
my $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() ); 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()); my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
while( my $user = $sth->fetchrow_hashref() ) { while ( my $user = $sth->fetchrow_hashref() ) {
my $scheme = substr($user->{Password}, 0, 1); my $scheme = substr($user->{Password}, 0, 1);
if ($scheme eq '*') { if ($scheme eq '*') {
print ('-->'.$user->{Username}." password will be migrated\n"); print('-->'.$user->{Username}." password will be migrated\n");
my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8));
my $settings = '$2a$10$'.$salt; my $settings = '$2a$10$'.$salt;
my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings);

View File

@ -23,6 +23,14 @@ void AnalysisThread::Start() {
void AnalysisThread::Run() { void AnalysisThread::Run() {
while (!(terminate_ or zm_terminate)) { while (!(terminate_ or zm_terminate)) {
monitor_->Analyse(); // Some periodic updates are required for variable capturing framerate
if (!monitor_->Analyse()) {
if (!(terminate_ or zm_terminate)) {
// We only sleep when Analyse returns false because it is an error condition and we will spin like mad if it persists.
Microseconds sleep_for = monitor_->Active() ? Microseconds(ZM_SAMPLE_RATE) : Microseconds(ZM_SUSPENDED_RATE);
Debug(2, "Sleeping for %" PRId64 "us", int64(sleep_for.count()));
std::this_thread::sleep_for(sleep_for);
}
}
} }
} }

View File

@ -65,7 +65,8 @@ Event::Event(
last_db_frame(0), last_db_frame(0),
have_video_keyframe(false), have_video_keyframe(false),
//scheme //scheme
save_jpegs(0) save_jpegs(0),
terminate_(false)
{ {
std::string notes; std::string notes;
createNotes(notes); createNotes(notes);
@ -133,98 +134,22 @@ Event::Event(
); );
id = zmDbDoInsert(sql); id = zmDbDoInsert(sql);
if (!SetPath(storage)) { thread_ = std::thread(&Event::Run, this);
// Try another
Warning("Failed creating event dir at %s", storage->Path());
sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id());
if (monitor->ServerId())
sql += stringtf(" AND ServerId=%u", monitor->ServerId());
storage = nullptr;
MYSQL_RES *result = zmDbFetch(sql);
if (result) {
for (int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
storage = new Storage(atoi(dbrow[0]));
if (SetPath(storage))
break;
delete storage;
storage = nullptr;
} // end foreach row of Storage
mysql_free_result(result);
result = nullptr;
}
if (!storage) {
Info("No valid local storage area found. Trying all other areas.");
// Try remote
sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL";
if (monitor->ServerId())
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
result = zmDbFetch(sql);
if (result) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0]));
if (SetPath(storage))
break;
delete storage;
storage = nullptr;
} // end foreach row of Storage
mysql_free_result(result);
result = nullptr;
}
}
if (!storage) {
storage = new Storage();
Warning("Failed to find a storage area to save events.");
}
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
zmDbDo(sql);
} // end if ! setPath(Storage)
Debug(1, "Using storage area at %s", path.c_str());
snapshot_file = path + "/snapshot.jpg";
alarm_file = path + "/alarm.jpg";
video_incomplete_path = path + "/" + video_incomplete_file;
if (monitor->GetOptVideoWriter() != 0) {
/* Save as video */
videoStore = new VideoStore(
video_incomplete_path.c_str(),
container.c_str(),
monitor->GetVideoStream(),
monitor->GetVideoCodecContext(),
( monitor->RecordAudio() ? monitor->GetAudioStream() : nullptr ),
( monitor->RecordAudio() ? monitor->GetAudioCodecContext() : nullptr ),
monitor );
if ( !videoStore->open() ) {
Warning("Failed to open videostore, turning on jpegs");
delete videoStore;
videoStore = nullptr;
if ( ! ( save_jpegs & 1 ) ) {
save_jpegs |= 1; // Turn on jpeg storage
sql = stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id);
zmDbDo(sql);
}
} else {
std::string codec = videoStore->get_codec();
video_file = stringtf("%" PRIu64 "-%s.%s.%s", id, "video", codec.c_str(), container.c_str());
video_path = path + "/" + video_file;
Debug(1, "Video file is %s", video_file.c_str());
}
} // end if GetOptVideoWriter
if (storage != monitor->getStorage())
delete storage;
} }
Event::~Event() { Event::~Event() {
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet. Debug(1, "Deleting event, calling stop");
Stop();
if (thread_.joinable()) {
// Should be. Issuing the stop and then getting the lock
Debug(1, "Joinable");
thread_.join();
} else {
Debug(1, "Not Joinable");
}
/* Close the video file */ /* Close the video file */
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet.
if (videoStore != nullptr) { if (videoStore != nullptr) {
Debug(4, "Deleting video store"); Debug(4, "Deleting video store");
delete videoStore; delete videoStore;
@ -377,6 +302,12 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
} // void Event::updateNotes(const StringSetMap &newNoteSetMap) } // void Event::updateNotes(const StringSetMap &newNoteSetMap)
void Event::AddPacket(const std::shared_ptr<ZMPacket>&packet) { void Event::AddPacket(const std::shared_ptr<ZMPacket>&packet) {
std::unique_lock<std::mutex> lck(packet_queue_mutex);
packet_queue.push(packet);
packet_queue_condition.notify_one();
}
void Event::AddPacket_(const std::shared_ptr<ZMPacket>&packet) {
have_video_keyframe = have_video_keyframe || have_video_keyframe = have_video_keyframe ||
( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) && ( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) &&
( packet->keyframe || monitor->GetOptVideoWriter() == Monitor::ENCODE) ); ( packet->keyframe || monitor->GetOptVideoWriter() == Monitor::ENCODE) );
@ -655,3 +586,112 @@ bool Event::SetPath(Storage *storage) {
} // deep storage or not } // deep storage or not
return true; return true;
} // end bool Event::SetPath } // end bool Event::SetPath
void Event::Run() {
Storage *storage = monitor->getStorage();
if (!SetPath(storage)) {
// Try another
Warning("Failed creating event dir at %s", storage->Path());
std::string sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id());
if (monitor->ServerId())
sql += stringtf(" AND ServerId=%u", monitor->ServerId());
storage = nullptr;
MYSQL_RES *result = zmDbFetch(sql);
if (result) {
for (int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
storage = new Storage(atoi(dbrow[0]));
if (SetPath(storage))
break;
delete storage;
storage = nullptr;
} // end foreach row of Storage
mysql_free_result(result);
result = nullptr;
}
if (!storage) {
Info("No valid local storage area found. Trying all other areas.");
// Try remote
sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL";
if (monitor->ServerId())
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
result = zmDbFetch(sql);
if (result) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0]));
if (SetPath(storage))
break;
delete storage;
storage = nullptr;
} // end foreach row of Storage
mysql_free_result(result);
result = nullptr;
}
}
if (!storage) {
storage = new Storage();
Warning("Failed to find a storage area to save events.");
}
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
zmDbDo(sql);
} // end if ! setPath(Storage)
Debug(1, "Using storage area at %s", path.c_str());
snapshot_file = path + "/snapshot.jpg";
alarm_file = path + "/alarm.jpg";
video_incomplete_path = path + "/" + video_incomplete_file;
if (monitor->GetOptVideoWriter() != 0) {
/* Save as video */
videoStore = new VideoStore(
video_incomplete_path.c_str(),
container.c_str(),
monitor->GetVideoStream(),
monitor->GetVideoCodecContext(),
( monitor->RecordAudio() ? monitor->GetAudioStream() : nullptr ),
( monitor->RecordAudio() ? monitor->GetAudioCodecContext() : nullptr ),
monitor );
if ( !videoStore->open() ) {
Warning("Failed to open videostore, turning on jpegs");
delete videoStore;
videoStore = nullptr;
if ( ! ( save_jpegs & 1 ) ) {
save_jpegs |= 1; // Turn on jpeg storage
zmDbDo(stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id));
}
} else {
std::string codec = videoStore->get_codec();
video_file = stringtf("%" PRIu64 "-%s.%s.%s", id, "video", codec.c_str(), container.c_str());
video_path = path + "/" + video_file;
Debug(1, "Video file is %s", video_file.c_str());
}
} // end if GetOptVideoWriter
if (storage != monitor->getStorage())
delete storage;
std::unique_lock<std::mutex> lck(packet_queue_mutex);
// The idea is to process the queue no matter what so that all packets get processed.
// We only break if the queue is empty
while (true) {
if (!packet_queue.empty()) {
Debug(1, "adding packet");
this->AddPacket_(packet_queue.front());
packet_queue.pop();
} else {
if (terminate_ or zm_terminate) {
Debug(1, "terminating");
break;
}
Debug(1, "waiting");
packet_queue_condition.wait(lck);
Debug(1, "wakeing");
}
}
}

View File

@ -22,14 +22,21 @@
#include "zm_config.h" #include "zm_config.h"
#include "zm_define.h" #include "zm_define.h"
#include "zm_packet.h"
#include "zm_storage.h" #include "zm_storage.h"
#include "zm_time.h" #include "zm_time.h"
#include "zm_utils.h" #include "zm_utils.h"
#include "zm_zone.h" #include "zm_zone.h"
#include <atomic>
#include <condition_variable>
#include <map> #include <map>
#include <memory>
#include <mutex>
#include <queue> #include <queue>
#include <set> #include <set>
#include <thread>
class EventStream; class EventStream;
class Frame; class Frame;
@ -98,6 +105,15 @@ class Event {
void createNotes(std::string &notes); void createNotes(std::string &notes);
std::queue<std::shared_ptr<ZMPacket>> packet_queue;
std::mutex packet_queue_mutex;
std::condition_variable packet_queue_condition;
void Run();
std::atomic<bool> terminate_;
std::thread thread_;
public: public:
static bool OpenFrameSocket(int); static bool OpenFrameSocket(int);
static bool ValidateFrameSocket(int); static bool ValidateFrameSocket(int);
@ -118,6 +134,7 @@ class Event {
SystemTimePoint EndTime() const { return end_time; } SystemTimePoint EndTime() const { return end_time; }
void AddPacket(const std::shared_ptr<ZMPacket> &p); void AddPacket(const std::shared_ptr<ZMPacket> &p);
void AddPacket_(const std::shared_ptr<ZMPacket> &p);
bool WritePacket(const std::shared_ptr<ZMPacket> &p); bool WritePacket(const std::shared_ptr<ZMPacket> &p);
bool SendFrameImage(const Image *image, bool alarm_frame=false); bool SendFrameImage(const Image *image, bool alarm_frame=false);
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const; bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
@ -130,6 +147,12 @@ class Event {
int score = 0, int score = 0,
Image *alarm_image = nullptr); Image *alarm_image = nullptr);
void Stop() {
terminate_ = true;
packet_queue_condition.notify_all();
}
bool Stopped() const { return terminate_; }
private: private:
void WriteDbFrames(); void WriteDbFrames();
bool SetPath(Storage *storage); bool SetPath(Storage *storage);

View File

@ -1862,12 +1862,10 @@ bool Monitor::Analyse() {
// Need to guard around event creation/deletion from Reload() // Need to guard around event creation/deletion from Reload()
std::lock_guard<std::mutex> lck(event_mutex); std::lock_guard<std::mutex> lck(event_mutex);
Debug(3, "Have event lock");
// if we have been told to be OFF, then we are off and don't do any processing. // if we have been told to be OFF, then we are off and don't do any processing.
if (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) { if (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) {
Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state)); Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state));
int score = 0;
// Ready means that we have captured the warmup # of frames // Ready means that we have captured the warmup # of frames
if (!Ready()) { if (!Ready()) {
Debug(3, "Not ready?"); Debug(3, "Not ready?");
@ -1875,6 +1873,7 @@ bool Monitor::Analyse() {
return false; return false;
} }
int score = 0;
std::string cause; std::string cause;
Event::StringSetMap noteSetMap; Event::StringSetMap noteSetMap;
@ -1883,11 +1882,18 @@ bool Monitor::Analyse() {
score += 9; score += 9;
Debug(1, "Triggered on ONVIF"); Debug(1, "Triggered on ONVIF");
if (!event) { if (!event) {
Event::StringSet noteSet;
noteSet.insert("ONVIF2");
noteSetMap[MOTION_CAUSE] = noteSet;
event = openEvent(snap, "ONVIF", noteSetMap);
cause += "ONVIF"; cause += "ONVIF";
} else {
event->addNote(MOTION_CAUSE, "ONVIF2");
// Add to cause
} }
Event::StringSet noteSet; // Regardless of previous state, we go to ALARM
noteSet.insert("ONVIF2"); shared_data->state = state = ALARM;
noteSetMap[MOTION_CAUSE] = noteSet;
//If the camera isn't going to send an event close, we need to close it here, but only after it has actually triggered an alarm. //If the camera isn't going to send an event close, we need to close it here, but only after it has actually triggered an alarm.
if (!ONVIF_Closes_Event && state == ALARM) if (!ONVIF_Closes_Event && state == ALARM)
ONVIF_Trigger_State = FALSE; ONVIF_Trigger_State = FALSE;
@ -1899,11 +1905,16 @@ bool Monitor::Analyse() {
score += trigger_data->trigger_score; score += trigger_data->trigger_score;
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score); Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
if (!event) { if (!event) {
cause += trigger_data->trigger_cause; Event::StringSet noteSet;
noteSet.insert(trigger_data->trigger_text);
noteSetMap[trigger_data->trigger_cause] = noteSet;
event = openEvent(snap, trigger_data->trigger_cause, noteSetMap);
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
} else {
event->addNote(trigger_data->trigger_cause, trigger_data->trigger_text);
// Need to know if we should end the previous and start a new one, or just add the data
} }
Event::StringSet noteSet; shared_data->state = state = ALARM;
noteSet.insert(trigger_data->trigger_text);
noteSetMap[trigger_data->trigger_cause] = noteSet;
} // end if trigger_on } // end if trigger_on
// FIXME this snap might not be the one that caused the signal change. Need to store that in the packet. // FIXME this snap might not be the one that caused the signal change. Need to store that in the packet.
@ -1917,7 +1928,7 @@ bool Monitor::Analyse() {
} }
} else if (function == MOCORD or function == RECORD) { } else if (function == MOCORD or function == RECORD) {
if (!event) { if (!event) {
if (cause.length()) cause += ", "; if (!cause.empty()) cause += ", ";
cause += SIGNAL_CAUSE + std::string(": Reacquired"); cause += SIGNAL_CAUSE + std::string(": Reacquired");
} else { } else {
event->addNote(SIGNAL_CAUSE, "Reacquired"); event->addNote(SIGNAL_CAUSE, "Reacquired");
@ -1976,12 +1987,10 @@ bool Monitor::Analyse() {
packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all
packetqueue.wait(); packetqueue.wait();
// Everything may have changed, just return and start again. This needs to be more RAII // Everything may have changed, just return and start again. This needs to be more RAII
return false; return true;
} // end while ! decoded } // end while ! decoded
} // end if decoding enabled } // end if decoding enabled
SystemTimePoint timestamp = snap->timestamp;
if (Active() and (function == MODECT or function == MOCORD)) { if (Active() and (function == MODECT or function == MOCORD)) {
Debug(3, "signal and active and modect"); Debug(3, "signal and active and modect");
Event::StringSet zoneSet; Event::StringSet zoneSet;
@ -1994,17 +2003,19 @@ bool Monitor::Analyse() {
} }
if (snap->image) { if (snap->image) {
alarm_image.Assign(*(snap->image));
// decoder may not have been able to provide an image // decoder may not have been able to provide an image
if (!ref_image.Buffer()) { if (!ref_image.Buffer()) {
Debug(1, "Assigning instead of Detecting"); Debug(1, "Assigning instead of Detecting");
ref_image.Assign(*(snap->image)); ref_image.Assign(*(snap->image));
alarm_image.Assign(*(snap->image));
} else if (!(analysis_image_count % (motion_frame_skip+1))) { } else if (!(analysis_image_count % (motion_frame_skip+1))) {
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image); Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
// Get new score. // Get new score.
int motion_score = DetectMotion(*(snap->image), zoneSet); snap->score = DetectMotion(*(snap->image), zoneSet);
if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image));
// lets construct alarm cause. It will contain cause + names of zones alarmed // lets construct alarm cause. It will contain cause + names of zones alarmed
snap->zone_stats.reserve(zones.size()); snap->zone_stats.reserve(zones.size());
for (const Zone &zone : zones) { for (const Zone &zone : zones) {
@ -2014,27 +2025,31 @@ bool Monitor::Analyse() {
if (zone.Alarmed()) { if (zone.Alarmed()) {
if (!snap->alarm_cause.empty()) snap->alarm_cause += ","; if (!snap->alarm_cause.empty()) snap->alarm_cause += ",";
snap->alarm_cause += std::string(zone.Label()); snap->alarm_cause += std::string(zone.Label());
if (zone.AlarmImage())
snap->analysis_image->Overlay(*(zone.AlarmImage()));
} }
} }
alarm_image.Assign(*(snap->analysis_image));
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)", Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score); score, last_motion_score, snap->score);
motion_frame_count += 1; motion_frame_count += 1;
last_motion_score = motion_score; last_motion_score = snap->score;
if (motion_score) { if (snap->score) {
if (cause.length()) cause += ", "; if (cause.length()) cause += ", ";
cause += MOTION_CAUSE+std::string(":")+snap->alarm_cause; cause += MOTION_CAUSE+std::string(":")+snap->alarm_cause;
noteSetMap[MOTION_CAUSE] = zoneSet; noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score } // end if motion_score
} else { } else {
Debug(1, "Skipped motion detection last motion score was %d", last_motion_score); Debug(1, "Skipped motion detection last motion score was %d", last_motion_score);
alarm_image.Assign(*(snap->image));
} }
} else { } else {
Debug(1, "no image so skipping motion detection"); Debug(1, "no image so skipping motion detection");
} // end if has image } // end if has image
score += last_motion_score; //score += last_motion_score;
} else { } else {
Debug(1, "Not Active(%d) enabled %d active %d doing motion detection: %d", Debug(1, "Not Active(%d) enabled %d shared->active %d doing motion detection: %d",
Active(), enabled, shared_data->active, Active(), enabled, shared_data->active,
(function == MODECT or function == MOCORD) (function == MODECT or function == MOCORD)
); );
@ -2045,17 +2060,17 @@ bool Monitor::Analyse() {
if (event) { if (event) {
Debug(2, "Have event %" PRIu64 " in record", event->Id()); Debug(2, "Have event %" PRIu64 " in record", event->Id());
if (section_length != Seconds(0) && (timestamp - event->StartTime() >= section_length) if (section_length != Seconds(0) && (snap->timestamp - event->StartTime() >= section_length)
&& ((function == MOCORD && event_close_mode != CLOSE_TIME) && ((function == MOCORD && event_close_mode != CLOSE_TIME)
|| (function == RECORD && event_close_mode == CLOSE_TIME) || (function == RECORD && event_close_mode == CLOSE_TIME)
|| std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()) % section_length == Seconds(0))) { || std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()) % section_length == Seconds(0))) {
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64 , Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64 ,
name.c_str(), name.c_str(),
image_count, image_count,
event->Id(), event->Id(),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(event->StartTime().time_since_epoch()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(event->StartTime().time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - event->StartTime()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - event->StartTime()).count()),
static_cast<int64>(Seconds(section_length).count())); static_cast<int64>(Seconds(section_length).count()));
closeEvent(); closeEvent();
} // end if section_length } // end if section_length
@ -2073,13 +2088,14 @@ bool Monitor::Analyse() {
} // end if ! event } // end if ! event
} // end if RECORDING } // end if RECORDING
if (score and (function != MONITOR)) { if ((snap->score > 0) and (function != MONITOR)) {
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) { if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
// If we should end then previous continuous event and start a new non-continuous event // If we should end then previous continuous event and start a new non-continuous event
if (event && event->Frames() if (event && event->Frames()
&& !event->AlarmFrames() && !event->AlarmFrames()
&& (event_close_mode == CLOSE_ALARM) && (event_close_mode == CLOSE_ALARM)
&& ((timestamp - event->StartTime()) >= min_section_length) // FIXME since we won't be including this snap in the event if we close it, we should be looking at event->duration() instead
&& ((snap->timestamp - event->StartTime()) >= min_section_length)
&& ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count - 1))) { && ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count - 1))) {
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
name.c_str(), image_count, event->Id()); name.c_str(), image_count, event->Id());
@ -2091,7 +2107,7 @@ bool Monitor::Analyse() {
Event::PreAlarmCount(), pre_event_count, Event::PreAlarmCount(), pre_event_count,
event->Frames(), event->Frames(),
event->AlarmFrames(), event->AlarmFrames(),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - event->StartTime()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - event->StartTime()).count()),
static_cast<int64>(Seconds(min_section_length).count()), static_cast<int64>(Seconds(min_section_length).count()),
(event_close_mode == CLOSE_ALARM)); (event_close_mode == CLOSE_ALARM));
} }
@ -2101,12 +2117,10 @@ bool Monitor::Analyse() {
if (!event) { if (!event) {
event = openEvent(snap, cause, noteSetMap); event = openEvent(snap, cause, noteSetMap);
shared_data->state = state = ALARM;
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id()); Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
} else {
shared_data->state = state = ALARM;
} // end if no event, so start it } // end if no event, so start it
if ( alarm_frame_count ) { shared_data->state = state = ALARM;
if (alarm_frame_count) {
Debug(1, "alarm frame count so SavePreAlarmFrames"); Debug(1, "alarm frame count so SavePreAlarmFrames");
event->SavePreAlarmFrames(); event->SavePreAlarmFrames();
} }
@ -2128,13 +2142,14 @@ bool Monitor::Analyse() {
Debug(1, "Was in TAPE, going into ALARM"); Debug(1, "Was in TAPE, going into ALARM");
} else { } else {
Debug(1, "Staying in %s", State_Strings[state].c_str()); Debug(1, "Staying in %s", State_Strings[state].c_str());
} }
if (state == ALARM) { if (state == ALARM) {
last_alarm_count = analysis_image_count; last_alarm_count = analysis_image_count;
} // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT } // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT
} else { // no score? } else if (!score and snap->score == 0) { // snap->score means -1 which means didn't do motion detection so don't do state transition
Debug(1, "!score");
alert_to_alarm_frame_count = alarm_frame_count; // load same value configured for alarm_frame_count alert_to_alarm_frame_count = alarm_frame_count; // load same value configured for alarm_frame_count
if (state == ALARM) { if (state == ALARM) {
Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count); Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
shared_data->state = state = ALERT; shared_data->state = state = ALERT;
@ -2142,7 +2157,7 @@ bool Monitor::Analyse() {
if ( if (
((analysis_image_count - last_alarm_count) > post_event_count) ((analysis_image_count - last_alarm_count) > post_event_count)
&& &&
((timestamp - event->StartTime()) >= min_section_length)) { ((snap->timestamp - event->StartTime()) >= min_section_length)) {
Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images", Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames()); name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
if ( if (
@ -2169,7 +2184,7 @@ bool Monitor::Analyse() {
analysis_image_count, analysis_image_count,
last_alarm_count, last_alarm_count,
post_event_count, post_event_count,
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
static_cast<int64>(Seconds(min_section_length).count())); static_cast<int64>(Seconds(min_section_length).count()));
} }
@ -2177,46 +2192,22 @@ bool Monitor::Analyse() {
Event::EmptyPreAlarmFrames(); Event::EmptyPreAlarmFrames();
} // end if score or not } // end if score or not
snap->score = score; if (score > snap->score)
snap->score = score;
if (state == PREALARM) { if (state == PREALARM) {
// Generate analysis images if necessary
if (snap->image) {
for (const Zone &zone : zones) {
if (zone.Alarmed() and zone.AlarmImage()) {
if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zone.AlarmImage()));
} // end if zone is alarmed
} // end foreach zone
if (snap->analysis_image != nullptr)
alarm_image.Assign(*(snap->analysis_image));
} // end if image.
// incremement pre alarm image count // incremement pre alarm image count
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr); Event::AddPreAlarmFrame(snap->image, snap->timestamp, score, nullptr);
} else if (state == ALARM) { } else if (state == ALARM) {
if (snap->image) {
for (const Zone &zone : zones) {
if (zone.Alarmed() and zone.AlarmImage()) {
if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zone.AlarmImage()));
} // end if zone is alarmed
} // end foreach zone
if (snap->analysis_image != nullptr)
alarm_image.Assign(*(snap->analysis_image));
}
if (event) { if (event) {
if (noteSetMap.size() > 0) if (noteSetMap.size() > 0)
event->updateNotes(noteSetMap); event->updateNotes(noteSetMap);
if (section_length != Seconds(0) && (timestamp - event->StartTime() >= section_length)) { if (section_length != Seconds(0) && (snap->timestamp - event->StartTime() >= section_length)) {
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64, Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
name.c_str(), analysis_image_count, event->Id(), name.c_str(), analysis_image_count, event->Id(),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(section_length).count())); static_cast<int64>(Seconds(section_length).count()));
closeEvent(); closeEvent();
event = openEvent(snap, cause, noteSetMap); event = openEvent(snap, cause, noteSetMap);
@ -2224,15 +2215,15 @@ bool Monitor::Analyse() {
} else { } else {
Error("ALARM but no event"); Error("ALARM but no event");
} }
} else if ( state == ALERT ) { } else if (state == ALERT) {
// Alert means this frame has no motion, but we were alarmed and are still recording. // Alert means this frame has no motion, but we were alarmed and are still recording.
if ((noteSetMap.size() > 0) and event) if ((noteSetMap.size() > 0) and event)
event->updateNotes(noteSetMap); event->updateNotes(noteSetMap);
} else if ( state == TAPE ) { } else if (state == TAPE) {
// bulk frame code moved to event. // bulk frame code moved to event.
} // end if state machine } // end if state machine
if ( (function == MODECT or function == MOCORD) and snap->image ) { if ((function == MODECT or function == MOCORD) and snap->image) {
if (!ref_image.Buffer()) { if (!ref_image.Buffer()) {
Debug(1, "Assigning"); Debug(1, "Assigning");
ref_image.Assign(*(snap->image)); ref_image.Assign(*(snap->image));
@ -2259,11 +2250,18 @@ bool Monitor::Analyse() {
// In the case where people have pre-alarm frames, the web ui will generate the frame images // In the case where people have pre-alarm frames, the web ui will generate the frame images
// from the mp4. So no one will notice anyways. // from the mp4. So no one will notice anyways.
if (snap->image and (videowriter == PASSTHROUGH) and !savejpegs) { if (snap->image and (videowriter == PASSTHROUGH)) {
Debug(1, "Deleting image data for %d", snap->image_index); if (!savejpegs) {
// Don't need raw images anymore Debug(1, "Deleting image data for %d", snap->image_index);
delete snap->image; // Don't need raw images anymore
snap->image = nullptr; delete snap->image;
snap->image = nullptr;
}
if (snap->analysis_image and !(savejpegs & 2)) {
Debug(1, "Deleting analysis image data for %d", snap->image_index);
delete snap->analysis_image;
snap->analysis_image = nullptr;
}
} }
packetqueue.clearPackets(snap); packetqueue.clearPackets(snap);
@ -2274,7 +2272,8 @@ bool Monitor::Analyse() {
analysis_image_count++; analysis_image_count++;
} }
packetqueue.increment_it(analysis_it); packetqueue.increment_it(analysis_it);
packetqueue.unlock(packet_lock); delete packet_lock;
//packetqueue.unlock(packet_lock);
shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
return true; return true;

@ -1 +1 @@
Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938 Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef