Merge branch 'master' into multistream

This commit is contained in:
Isaac Connor 2022-01-13 12:29:34 -05:00
commit 3504b13e2a
7 changed files with 261 additions and 201 deletions

View File

@ -5,7 +5,8 @@
Description=ZoneMinder CCTV recording and surveillance system
After=network.target mysql.service
# 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]
#User=www-data

View File

@ -639,9 +639,9 @@ $log->debug("Have array for $k $$search{$k}") if DEBUG_ALL;
if ( ! ( $db_field =~ /\?/ ) ) {
if ( @{$$search{$k}} != 1 ) {
push @where, $db_field .' IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
push @where, '`'.$db_field .'` IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
} else {
push @where, $db_field.'=?';
push @where, '`'.$db_field.'`=?';
} # end if
} else {
$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}} ) {
my $v = $$search{$k}{$p_k};
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};
} else {
push @where, $db_field.'=?';
push @where, '`'.$db_field.'`=?';
push @values, $p_k, $v;
} # end if
} # 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';
} else {
if ( ! ( $db_field =~ /\?/ ) ) {
push @where, $db_field .'=?';
push @where, '`'.$db_field .'`=?';
} else {
push @where, $db_field;
}

View File

@ -42,6 +42,7 @@ use constant SELECT_TIMEOUT => 0.25;
@EXTRA_PERL_LIB@
use ZoneMinder;
use ZoneMinder::Monitor;
use ZoneMinder::Trigger::Channel::Inet;
use ZoneMinder::Trigger::Channel::Unix;
use ZoneMinder::Trigger::Channel::Serial;
@ -203,14 +204,10 @@ while (!$zm_terminate) {
# Check for alarms that might have happened
my @out_messages;
foreach my $monitor ( values %monitors ) {
if ($$monitor{Function} eq 'None') {
$monitor_reload_time = 0;
next;
}
if (!zmMemVerify($monitor)) {
if (!$monitor->connect()) {
# 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.
Debug("Failed connect, putting on reloads");
push @needsReload, $monitor;
next;
}
@ -249,6 +246,7 @@ while (!$zm_terminate) {
}
$monitor->{LastState} = $state;
$monitor->{LastEvent} = $last_event;
$monitor->disconnect();
} # end foreach monitor
foreach my $connection ( @out_connections ) {
@ -298,15 +296,22 @@ while (!$zm_terminate) {
# Reload all monitors from the dB every 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();
@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
} elsif ( @needsReload ) {
foreach my $monitor ( @needsReload ) {
loadMonitor($monitor);
} elsif (@needsReload) {
foreach my $monitor (@needsReload) {
$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 = ();
}
@ -317,40 +322,21 @@ while (!$zm_terminate) {
Info('Trigger daemon exiting');
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 {
$monitor_reload_time = time();
my %new_monitors = ();
%monitors = ();
my $sql = 'SELECT * FROM `Monitors`
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect,Record\' )'.
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' )
;
my $sth = $dbh->prepare_cached( $sql )
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
foreach my $monitor ( ZoneMinder::Monitor->find(
Function=>['Modect','Mocord','Nodect','Record'],
($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ()),
)) {
if ($monitor->connect()) { # This will re-init shared memory
$monitor->{LastState} = zmGetMonitorState($monitor);
$monitor->{LastEvent} = zmGetLastEvent($monitor);
}
$new_monitors{$monitor->{Id}} = $monitor;
$monitors{$monitor->{Id}} = $monitor;
} # end while fetchrow
%monitors = %new_monitors;
} # end sub loadMonitors
sub handleMessage {

View File

@ -23,6 +23,14 @@ void AnalysisThread::Start() {
void AnalysisThread::Run() {
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),
have_video_keyframe(false),
//scheme
save_jpegs(0)
save_jpegs(0),
terminate_(false)
{
std::string notes;
createNotes(notes);
@ -133,98 +134,22 @@ Event::Event(
);
id = zmDbDoInsert(sql);
if (!SetPath(storage)) {
// 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;
thread_ = std::thread(&Event::Run, this);
}
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 */
// 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) {
Debug(4, "Deleting video store");
delete videoStore;
@ -377,6 +302,12 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
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 ||
( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) &&
( packet->keyframe || monitor->GetOptVideoWriter() == Monitor::ENCODE) );
@ -655,3 +586,112 @@ bool Event::SetPath(Storage *storage) {
} // deep storage or not
return true;
} // 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_define.h"
#include "zm_packet.h"
#include "zm_storage.h"
#include "zm_time.h"
#include "zm_utils.h"
#include "zm_zone.h"
#include <atomic>
#include <condition_variable>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <set>
#include <thread>
class EventStream;
class Frame;
@ -98,6 +105,15 @@ class Event {
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:
static bool OpenFrameSocket(int);
static bool ValidateFrameSocket(int);
@ -118,6 +134,7 @@ class Event {
SystemTimePoint EndTime() const { return end_time; }
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 SendFrameImage(const Image *image, bool alarm_frame=false);
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
@ -130,6 +147,12 @@ class Event {
int score = 0,
Image *alarm_image = nullptr);
void Stop() {
terminate_ = true;
packet_queue_condition.notify_all();
}
bool Stopped() const { return terminate_; }
private:
void WriteDbFrames();
bool SetPath(Storage *storage);

View File

@ -1866,12 +1866,10 @@ bool Monitor::Analyse() {
// Need to guard around event creation/deletion from Reload()
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 (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) {
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
if (!Ready()) {
Debug(3, "Not ready?");
@ -1879,6 +1877,7 @@ bool Monitor::Analyse() {
return false;
}
int score = 0;
std::string cause;
Event::StringSetMap noteSetMap;
@ -1887,11 +1886,18 @@ bool Monitor::Analyse() {
score += 9;
Debug(1, "Triggered on ONVIF");
if (!event) {
Event::StringSet noteSet;
noteSet.insert("ONVIF2");
noteSetMap[MOTION_CAUSE] = noteSet;
event = openEvent(snap, "ONVIF", noteSetMap);
cause += "ONVIF";
} else {
event->addNote(MOTION_CAUSE, "ONVIF2");
// Add to cause
}
Event::StringSet noteSet;
noteSet.insert("ONVIF2");
noteSetMap[MOTION_CAUSE] = noteSet;
// Regardless of previous state, we go to ALARM
shared_data->state = state = 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)
ONVIF_Trigger_State = FALSE;
@ -1903,11 +1909,16 @@ bool Monitor::Analyse() {
score += trigger_data->trigger_score;
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
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;
noteSet.insert(trigger_data->trigger_text);
noteSetMap[trigger_data->trigger_cause] = noteSet;
shared_data->state = state = ALARM;
} // end if trigger_on
// FIXME this snap might not be the one that caused the signal change. Need to store that in the packet.
@ -1921,7 +1932,7 @@ bool Monitor::Analyse() {
}
} else if (recording == RECORDING_ALWAYS) {
if (!event) {
if (cause.length()) cause += ", ";
if (!cause.empty()) cause += ", ";
cause += SIGNAL_CAUSE + std::string(": Reacquired");
} else {
event->addNote(SIGNAL_CAUSE, "Reacquired");
@ -1979,12 +1990,10 @@ bool Monitor::Analyse() {
packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all
packetqueue.wait();
// Everything may have changed, just return and start again. This needs to be more RAII
return false;
return true;
} // end while ! decoded
} // end if decoding enabled
SystemTimePoint timestamp = snap->timestamp;
if (shared_data->analysing) {
Debug(3, "signal and capturing and doing motion detection");
@ -1996,18 +2005,20 @@ bool Monitor::Analyse() {
}
if (snap->image) {
alarm_image.Assign(*(snap->image));
// decoder may not have been able to provide an image
if (!ref_image.Buffer()) {
Debug(1, "Assigning instead of Detecting");
ref_image.Assign(*(snap->image));
alarm_image.Assign(*(snap->image));
} else if (!(analysis_image_count % (motion_frame_skip+1))) {
Event::StringSet zoneSet;
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
// 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
snap->zone_stats.reserve(zones.size());
for (const Zone &zone : zones) {
@ -2017,25 +2028,29 @@ bool Monitor::Analyse() {
if (zone.Alarmed()) {
if (!snap->alarm_cause.empty()) snap->alarm_cause += ",";
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)",
score, last_motion_score, motion_score);
score, last_motion_score, snap->score);
motion_frame_count += 1;
last_motion_score = motion_score;
last_motion_score = snap->score;
if (motion_score) {
if (snap->score) {
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE+std::string(":")+snap->alarm_cause;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
} else {
Debug(1, "Skipped motion detection last motion score was %d", last_motion_score);
alarm_image.Assign(*(snap->image));
}
} else {
Debug(1, "no image so skipping motion detection");
} // end if has image
score += last_motion_score;
//score += last_motion_score;
} else {
Debug(1, "Not analysing %d", shared_data->analysing);
} // end if active and doing motion detection
@ -2051,9 +2066,9 @@ bool Monitor::Analyse() {
name.c_str(),
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>(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()));
closeEvent();
} // end if section_length
@ -2071,7 +2086,7 @@ bool Monitor::Analyse() {
} // end if ! event
} // end if RECORDING
if (score) {
if ((snap->score > 0) and (function != MONITOR)) {
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
// If we should end then previous continuous event and start a new non-continuous event
if (event) {
@ -2090,7 +2105,7 @@ bool Monitor::Analyse() {
Event::PreAlarmCount(), pre_event_count,
event->Frames(),
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()),
(event_close_mode == CLOSE_ALARM));
}
@ -2105,11 +2120,12 @@ bool Monitor::Analyse() {
if (!event and (recording != RECORDING_NONE)) {
event = openEvent(snap, cause, noteSetMap);
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
if (alarm_frame_count) {
Debug(1, "alarm frame count so SavePreAlarmFrames");
event->SavePreAlarmFrames();
}
} // end if no event, so start it
shared_data->state = state = ALARM;
if (alarm_frame_count) {
Debug(1, "alarm frame count so SavePreAlarmFrames");
event->SavePreAlarmFrames();
}
} else if (state != PREALARM) {
Info("%s: %03d - Gone into prealarm state", name.c_str(), analysis_image_count);
shared_data->state = state = PREALARM;
@ -2133,15 +2149,17 @@ bool Monitor::Analyse() {
if (state == ALARM) {
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
} 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
if (state == ALARM) {
Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
shared_data->state = state = ALERT;
} else if (state == ALERT) {
if (((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",
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
@ -2172,7 +2190,7 @@ bool Monitor::Analyse() {
analysis_image_count,
last_alarm_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>(Seconds(min_section_length).count()));
}
@ -2180,46 +2198,22 @@ bool Monitor::Analyse() {
Event::EmptyPreAlarmFrames();
} // end if score or not
snap->score = score;
if (score > snap->score)
snap->score = score;
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
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr);
Event::AddPreAlarmFrame(snap->image, snap->timestamp, score, nullptr);
} 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 (noteSetMap.size() > 0)
event->updateNotes(noteSetMap);
if (section_length != Seconds(min_section_length) && (timestamp - event->StartTime() > section_length)) {
if (section_length != Seconds(min_section_length) && (snap->timestamp - event->StartTime() >= section_length)) {
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
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>(timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(snap->timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(section_length).count()));
closeEvent();
event = openEvent(snap, cause, noteSetMap);
@ -2227,11 +2221,11 @@ bool Monitor::Analyse() {
} else {
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.
if ((noteSetMap.size() > 0) and event)
event->updateNotes(noteSetMap);
} else if ( state == TAPE ) {
} else if (state == TAPE) {
// bulk frame code moved to event.
} // end if state machine
@ -2262,11 +2256,18 @@ bool Monitor::Analyse() {
// 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.
if (snap->image and (videowriter == PASSTHROUGH) and !savejpegs) {
Debug(1, "Deleting image data for %d", snap->image_index);
// Don't need raw images anymore
delete snap->image;
snap->image = nullptr;
if (snap->image and (videowriter == PASSTHROUGH)) {
if (!savejpegs) {
Debug(1, "Deleting image data for %d", snap->image_index);
// Don't need raw images anymore
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);
@ -2277,7 +2278,8 @@ bool Monitor::Analyse() {
analysis_image_count++;
}
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());
return true;