From 08afb50efa3965196b5a6bbc5262635d378409bd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 25 Jun 2020 16:04:50 -0400 Subject: [PATCH 1/9] debug, spacing, quotes --- .../NotificationProducerPort.pm | 57 ++++++++----------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/onvif/proxy/lib/WSNotification/Interfaces/WSBaseNotificationSender/NotificationProducerPort.pm b/onvif/proxy/lib/WSNotification/Interfaces/WSBaseNotificationSender/NotificationProducerPort.pm index ee55035ca..5a8f4d0b1 100644 --- a/onvif/proxy/lib/WSNotification/Interfaces/WSBaseNotificationSender/NotificationProducerPort.pm +++ b/onvif/proxy/lib/WSNotification/Interfaces/WSBaseNotificationSender/NotificationProducerPort.pm @@ -20,18 +20,16 @@ sub START { sub Subscribe { my ($self, $body, $header) = @_; die "Subscribe must be called as object method (\$self is <$self>)" if not blessed($self); +#print " proxy/lib/WSNotification/Interfaces/WSBaseNotificationSender/NotificationProducerPort.pm Subscribe\n"; return $self->SUPER::call({ operation => 'Subscribe', soap_action => 'http://docs.oasis-open.org/wsn/bw-2/Subscribe', style => 'document', body => { - - - 'use' => 'literal', - namespace => 'http://schemas.xmlsoap.org/wsdl/soap/', - encodingStyle => '', - parts => [qw( WSNotification::Elements::Subscribe )], - + use => 'literal', + namespace => 'http://schemas.xmlsoap.org/wsdl/soap/', + encodingStyle => '', + parts => [qw( WSNotification::Elements::Subscribe )], }, header => { @@ -42,39 +40,30 @@ sub Subscribe { }, $body, $header); } - sub GetCurrentMessage { - my ($self, $body, $header) = @_; - die "GetCurrentMessage must be called as object method (\$self is <$self>)" if not blessed($self); - return $self->SUPER::call({ - operation => 'GetCurrentMessage', - soap_action => 'http://docs.oasis-open.org/wsn/bw-2/GetCurrentMessage', - style => 'document', - body => { - + my ($self, $body, $header) = @_; + die "GetCurrentMessage must be called as object method (\$self is <$self>)" if not blessed($self); + return $self->SUPER::call({ + operation => 'GetCurrentMessage', + soap_action => 'http://docs.oasis-open.org/wsn/bw-2/GetCurrentMessage', + style => 'document', + body => { + use => 'literal', + namespace => 'http://schemas.xmlsoap.org/wsdl/soap/', + encodingStyle => '', + parts => [qw( WSNotification::Elements::GetCurrentMessage )], - 'use' => 'literal', - namespace => 'http://schemas.xmlsoap.org/wsdl/soap/', - encodingStyle => '', - parts => [qw( WSNotification::Elements::GetCurrentMessage )], + }, + header => { - }, - header => { - - }, - headerfault => { - - } - }, $body, $header); + }, + headerfault => { + + } + }, $body, $header); } - - - 1; - - - __END__ =pod From 13130a844e9de14341bbb6673d4d72ec63bffa07 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 25 Jun 2020 16:07:16 -0400 Subject: [PATCH 2/9] handle array args in daemonStatus --- web/includes/functions.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index f640569c3..427cd4c16 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -842,9 +842,15 @@ function daemonStatus($daemon, $args=false) { initDaemonStatus(); $string = $daemon; - if ( $args ) - $string .= ' ' . $args; - return( strpos($daemon_status, "'$string' running") !== false ); + if ( $args ) { + if ( is_array($args) ) { + $string .= join(' ', $args); +ZM\Warning("daemonStatus args: $string"); + } else { + $string .= ' ' . $args; + } + } + return ( strpos($daemon_status, "'$string' running") !== false ); } function zmcStatus($monitor) { From 41dfb8ae380ad59128a772794e995aa966a015cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 20 Jul 2020 21:02:06 -0400 Subject: [PATCH 3/9] fix duplicate jpeg_create_compress memleak --- src/zm_image.cpp | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 21ed8c1fb..5793c39cc 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -263,28 +263,20 @@ Image::~Image() { /* Should be called as part of program shutdown to free everything */ void Image::Deinitialise() { if ( !initialised ) return; - /* - delete[] y_table; - delete[] uv_table; - delete[] r_v_table; - delete[] g_v_table; - delete[] g_u_table; - delete[] b_u_table; - */ initialised = false; if ( readjpg_dcinfo ) { - jpeg_destroy_decompress( readjpg_dcinfo ); + jpeg_destroy_decompress(readjpg_dcinfo); delete readjpg_dcinfo; - readjpg_dcinfo = 0; + readjpg_dcinfo = NULL; } if ( decodejpg_dcinfo ) { - jpeg_destroy_decompress( decodejpg_dcinfo ); + jpeg_destroy_decompress(decodejpg_dcinfo); delete decodejpg_dcinfo; - decodejpg_dcinfo = 0; + decodejpg_dcinfo = NULL; } for ( unsigned int quality=0; quality <= 100; quality += 1 ) { if ( writejpg_ccinfo[quality] ) { - jpeg_destroy_compress( writejpg_ccinfo[quality] ); + jpeg_destroy_compress(writejpg_ccinfo[quality]); delete writejpg_ccinfo[quality]; writejpg_ccinfo[quality] = NULL; } @@ -984,14 +976,12 @@ bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality]; FILE *outfile = NULL; static int raw_fd = 0; - bool need_create_comp = false; raw_fd = 0; if ( !cinfo ) { cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct; cinfo->err = jpeg_std_error(&jpg_err.pub); jpeg_create_compress(cinfo); - need_create_comp = true; } if ( !on_blocking_abort ) { jpg_err.pub.error_exit = zm_jpeg_error_exit; @@ -1009,8 +999,6 @@ bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval return false; } } - if ( need_create_comp ) - jpeg_create_compress(cinfo); if ( !on_blocking_abort ) { if ( (outfile = fopen(filename, "wb")) == NULL ) { @@ -2833,7 +2821,6 @@ void Image::Deinterlace_Discard() { } else { Error("Deinterlace called with unexpected colours: %d", colours); } - } void Image::Deinterlace_Linear() { From a0be7b1fcbe055c7951c377ebbf731fc6ee1ae54 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 20 Jul 2020 21:02:49 -0400 Subject: [PATCH 4/9] fixup EmptyPreAlarmFrames --- src/zm_event.h | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/zm_event.h b/src/zm_event.h index 77199509c..38e0fa486 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -153,28 +153,34 @@ class Event { return pre_alarm_count; } static void EmptyPreAlarmFrames() { - if ( pre_alarm_count > 0 ) { - for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) { - delete pre_alarm_data[i].image; - delete pre_alarm_data[i].alarm_frame; - } - memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); - } + while ( pre_alarm_count > 0 ) { + int i = pre_alarm_count - 1; + delete pre_alarm_data[i].image; + pre_alarm_data[i].image = NULL; + if ( pre_alarm_data[i].alarm_frame ) { + delete pre_alarm_data[i].alarm_frame; + pre_alarm_data[i].alarm_frame = NULL; + } + } pre_alarm_count = 0; } - static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) { - pre_alarm_data[pre_alarm_count].image = new Image( *image ); + static void AddPreAlarmFrame(Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL) { + pre_alarm_data[pre_alarm_count].image = new Image(*image); pre_alarm_data[pre_alarm_count].timestamp = timestamp; pre_alarm_data[pre_alarm_count].score = score; if ( alarm_frame ) { - pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); + pre_alarm_data[pre_alarm_count].alarm_frame = new Image(*alarm_frame); } pre_alarm_count++; } void SavePreAlarmFrames() { for ( int i = 0; i < pre_alarm_count; i++ ) { - AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); - } + AddFrame( + pre_alarm_data[i].image, + pre_alarm_data[i].timestamp, + pre_alarm_data[i].score, + pre_alarm_data[i].alarm_frame); + } EmptyPreAlarmFrames(); } }; From 785087022db6c80c2f3aabcdc912d8ea7d980aea Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 20 Jul 2020 21:09:07 -0400 Subject: [PATCH 5/9] Clear PerAlarmFrames on shutdown --- src/zm_monitor.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 1beef3074..ef8e1fa7e 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -727,9 +727,11 @@ Monitor::~Monitor() { } delete[] pre_event_buffer; } + if ( Event::PreAlarmCount() ) + Event::EmptyPreAlarmFrames(); } else if ( purpose == CAPTURE ) { shared_data->valid = false; - memset( mem_ptr, 0, mem_size ); + memset(mem_ptr, 0, mem_size); } #if ZM_MEM_MAPPED @@ -1540,17 +1542,17 @@ bool Monitor::Analyse() { name, image_count, event->Id()); closeEvent(); } else if ( event ) { - // This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames - Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d", - Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(), - ( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length - ); - } - if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) { - // lets construct alarm cause. It will contain cause + names of zones alarmed - std::string alarm_cause = ""; - for ( int i=0; i < n_zones; i++ ) { - if ( zones[i]->Alarmed() ) { + // This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames + Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d", + Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(), + ( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length + ); + } + if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) { + // lets construct alarm cause. It will contain cause + names of zones alarmed + std::string alarm_cause = ""; + for ( int i=0; i < n_zones; i++ ) { + if ( zones[i]->Alarmed() ) { alarm_cause = alarm_cause + "," + std::string(zones[i]->Label()); } } @@ -1691,10 +1693,11 @@ bool Monitor::Analyse() { } // end if zone is alarmed } // end foreach zone - if ( state == PREALARM ) + if ( state == PREALARM ) { Event::AddPreAlarmFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL)); - else - event->AddFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL)); + } else { + event->AddFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL)); + } } else { // Not doing alarm frame storage if ( state == PREALARM ) { From 2eec85eaff6660feb83f946068da98f30ac17f16 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 21 Jul 2020 16:10:05 -0400 Subject: [PATCH 6/9] Convert use of char path[PATH_MAX] to std::string. Fixes potential overflows, quiets compile, should reduce ram quite a bit and might event be faster. Code is also more readable. --- src/zm_event.cpp | 136 ++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 69d430d86..328092fab 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -87,7 +87,7 @@ Event::Event( char sql[ZM_SQL_MED_BUFSIZ]; struct tm *stime = localtime(&start_time.tv_sec); - snprintf(sql, sizeof(sql), "INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) VALUES ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '', %d, '%s' )", + snprintf(sql, sizeof(sql), "INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) VALUES ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '%s', %d, '%s' )", monitor->Id(), storage->Id(), start_time.tv_sec, @@ -98,6 +98,7 @@ Event::Event( state_id, monitor->getOrientation(), videoEvent, + ( monitor->GetOptVideoWriter() != 0 ? "video.mp4" : "" ), monitor->GetOptSaveJPEGs(), storage->SchemeString().c_str() ); @@ -108,15 +109,13 @@ Event::Event( return; } id = mysql_insert_id(&dbconn); - // /* Update event record with DefaultVideo name if possible so image.php can extract frames if needed, while recording is in progress */ if ( monitor->GetOptVideoWriter() != 0 ) { - video_name[0] = 0; - snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4"); - Debug(1, "Updating inserted event with DefaultVideo=%s",video_name); - snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name,id); + video_name = stringtf("%" PRIu64 "-%s", id, "video.mp4"); + Debug(1, "Updating inserted event with DefaultVideo=%s", video_name.c_str()); + snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id); if ( mysql_query(&dbconn, sql) ) { Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); db_mutex.unlock(); @@ -136,14 +135,13 @@ Event::Event( max_score = 0; alarm_frame_written = false; - char id_file[PATH_MAX]; + std::string id_file; - char *path_ptr = path; - path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id()); + path = stringtf("%s/%d", storage->Path(), monitor->Id()); // Try to make the Monitor Dir. Normally this would exist, but in odd cases might not. - if ( mkdir(path, 0755) ) { + if ( mkdir(path.c_str(), 0755) ) { if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path, strerror(errno)); + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); } if ( storage->Scheme() == Storage::DEEP ) { @@ -156,74 +154,74 @@ Event::Event( dt_parts[4] = stime->tm_min; dt_parts[5] = stime->tm_sec; - char date_path[PATH_MAX] = ""; - char time_path[PATH_MAX] = ""; - char *time_path_ptr = time_path; - for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { - path_ptr += snprintf(path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i]); + std::string date_path; + std::string time_path; - errno = 0; - if ( mkdir(path, 0755) ) { + for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { + path += stringtf("/%02d", dt_parts[i]); + + if ( mkdir(path.c_str(), 0755) ) { // FIXME This should not be fatal. Should probably move to a different storage area. if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path, strerror(errno)); + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); } if ( i == 2 ) - strncpy(date_path, path, sizeof(date_path)); - else if ( i >= 3 ) - time_path_ptr += snprintf(time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i]); + date_path = path; } + time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec); + // Create event id symlink - snprintf(id_file, sizeof(id_file), "%s/.%" PRIu64, date_path, id); - if ( symlink(time_path, id_file) < 0 ) - Error("Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); + id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id); + if ( symlink(time_path.c_str(), id_file.c_str()) < 0 ) + Error("Can't symlink %s -> %s: %s", id_file.c_str(), time_path.c_str(), strerror(errno)); } else if ( storage->Scheme() == Storage::MEDIUM ) { - path_ptr += snprintf( - path_ptr, sizeof(path), "/%04d-%02d-%02d", + path += stringtf("/%04d-%02d-%02d", stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday ); - if ( mkdir(path, 0755) ) { + if ( mkdir(path.c_str(), 0755) ) { if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path, strerror(errno)); + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); } - path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id); - if ( mkdir(path, 0755) ) { + path += stringtf("/%" PRIu64, id); + if ( mkdir(path.c_str(), 0755) ) { if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path, strerror(errno)); + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); } } else { - path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id); - if ( mkdir(path, 0755) ) { + path += stringtf("/%" PRIu64, id); + if ( mkdir(path.c_str(), 0755) ) { if ( errno != EEXIST ) - Error("Can't mkdir %s: %s", path, strerror(errno)); + Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); } // Create empty id tag file - snprintf(id_file, sizeof(id_file), "%s/.%" PRIu64, path, id); - if ( FILE *id_fp = fopen(id_file, "w") ) + id_file = stringtf("%s/.%" PRIu64, path.c_str(), id); + if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) fclose(id_fp); else - Error("Can't fopen %s: %s", id_file, strerror(errno)); + Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno)); } // deep storage or not last_db_frame = 0; - video_name[0] = 0; + video_name = ""; - snprintf(snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path); - snprintf(alarm_file, sizeof(alarm_file), "%s/alarm.jpg", path); + snapshot_file = path + "/snapshot.jpg"; + alarm_file = path + "/alarm.jpg"; /* Save as video */ if ( monitor->GetOptVideoWriter() != 0 ) { - snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4"); - snprintf(video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name); - Debug(1,"Writing video file to %s", video_file ); + video_file = path + video_name; + Debug(1, "Writing video file to %s", video_file.c_str()); /* X264 MP4 video writer */ if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) { #if ZM_HAVE_VIDEOWRITER_X264MP4 - videowriter = new X264MP4Writer(video_file, monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder(), monitor->GetOptEncoderParams()); + videowriter = new X264MP4Writer(video_file.c_str(), + monitor->Width(), monitor->Height(), + monitor->Colours(), monitor->SubpixelOrder(), + monitor->GetOptEncoderParams()); #else Error("ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2)"); #endif @@ -242,11 +240,9 @@ Event::Event( /* No video object */ videowriter = NULL; } - } // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent ) 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. /* Close the video file */ @@ -276,13 +272,12 @@ Event::~Event() { // update frame deltas to refer to video start time which may be a few frames before event start struct timeval video_offset = {0}; struct timeval video_start_time = monitor->GetVideoWriterStartTime(); - if (video_start_time.tv_sec > 0) { + if ( video_start_time.tv_sec > 0 ) { timersub(&video_start_time, &start_time, &video_offset); Debug(1, "Updating frames delta by %d sec %d usec", video_offset.tv_sec, video_offset.tv_usec); UpdateFramesDelta(video_offset.tv_sec + video_offset.tv_usec*1e-6); - } - else { + } else { Debug(3, "Video start_time %d sec %d usec not valid -- frame deltas not updated", video_start_time.tv_sec, video_start_time.tv_usec); } @@ -290,12 +285,12 @@ Event::~Event() { // Should not be static because we might be multi-threaded char sql[ZM_SQL_LGE_BUFSIZ]; snprintf(sql, sizeof(sql), - "UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64, + "UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, - video_name, id ); + id); db_mutex.lock(); while ( mysql_query(&dbconn, sql) && !zm_terminate ) { db_mutex.unlock(); @@ -304,7 +299,6 @@ Event::~Event() { db_mutex.lock(); } db_mutex.unlock(); - } // Event::~Event() void Event::createNotes(std::string ¬es) { @@ -329,7 +323,7 @@ bool Event::WriteFrameImage( int thisquality = (alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ? - config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default + config.jpeg_alarm_file_quality : 0; // quality to use, zero is default bool rc; @@ -507,17 +501,16 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str frames++; if ( monitor->GetOptSaveJPEGs() & 1 ) { - static char event_file[PATH_MAX]; - snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames); + std::string event_file = stringtf(staticConfig.capture_file_format, path, frames); Debug(1, "Writing pre-capture frame %d", frames); - WriteFrameImage(images[i], *(timestamps[i]), event_file); + WriteFrameImage(images[i], *(timestamps[i]), event_file.c_str()); } //If this is the first frame, we should add a thumbnail to the event directory // ICON: We are working through the pre-event frames so this snapshot won't // neccessarily be of the motion. But some events are less than 10 frames, // so I am changing this to 1, but we should overwrite it later with a better snapshot. if ( frames == 1 ) { - WriteFrameImage(images[i], *(timestamps[i]), snapshot_file); + WriteFrameImage(images[i], *(timestamps[i]), snapshot_file.c_str()); } if ( videowriter != NULL ) { @@ -589,14 +582,14 @@ void Event::WriteDbFrames() { void Event::UpdateFramesDelta(double offset) { char sql[ZM_SQL_MED_BUFSIZ]; - if (offset == 0.0) return; + if ( offset == 0.0 ) return; // the table is set to auto update timestamp so we force it to keep current value snprintf(sql, sizeof(sql), "UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64, offset, id); db_mutex.lock(); - if (mysql_query(&dbconn, sql)) { + if ( mysql_query(&dbconn, sql) ) { db_mutex.unlock(); Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql); return; @@ -605,7 +598,6 @@ void Event::UpdateFramesDelta(double offset) { Info("Updating frames delta by %0.2f sec to match video file", offset); } - void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) { if ( !timestamp.tv_sec ) { Debug(1, "Not adding new frame, zero timestamp"); @@ -620,10 +612,9 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a score = 0; if ( monitor->GetOptSaveJPEGs() & 1 ) { - static char event_file[PATH_MAX]; - snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames); - Debug(1, "Writing capture frame %d to %s", frames, event_file); - if ( !WriteFrameImage(image, timestamp, event_file) ) { + std::string event_file = stringtf(staticConfig.capture_file_format, path, frames); + Debug(1, "Writing capture frame %d to %s", frames, event_file.c_str()); + if ( !WriteFrameImage(image, timestamp, event_file.c_str()) ) { Error("Failed to write frame image"); } } @@ -631,7 +622,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a // If this is the first frame, we should add a thumbnail to the event directory if ( (frames == 1) || (score > (int)max_score) ) { write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it. - WriteFrameImage(image, timestamp, snapshot_file); + WriteFrameImage(image, timestamp, snapshot_file.c_str()); } // We are writing an Alarm frame @@ -640,7 +631,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a if ( !alarm_frame_written ) { write_to_db = true; // OD processing will need it, so the db needs to know about it alarm_frame_written = true; - WriteFrameImage(image, timestamp, alarm_file); + WriteFrameImage(image, timestamp, alarm_file.c_str()); } alarm_frames++; @@ -650,10 +641,9 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a if ( alarm_image ) { if ( monitor->GetOptSaveJPEGs() & 2 ) { - static char event_file[PATH_MAX]; - snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames); + std::string event_file = stringtf(staticConfig.analyse_file_format, path, frames); Debug(1, "Writing analysis frame %d", frames); - if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) { + if ( ! WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true) ) { Error("Failed to write analysis frame image"); } } @@ -673,10 +663,12 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a // The idea is to write out 1/sec frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score)); - if ( write_to_db || (frame_data.size() > (int)monitor->get_fps()) ) { - Debug(1, "Adding %d frames to DB", frame_data.size()); + if ( write_to_db || (frame_data.size() > (unsigned int)monitor->get_fps()) ) { + Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > fps %d", + frame_data.size(), write_to_db, (unsigned int)monitor->get_fps()); WriteDbFrames(); last_db_frame = frames; + Debug(1, "Adding %d frames to DB, done", frame_data.size()); } // We are writing a Bulk frame From f5932611361ed5865cfca137ff86099c1fd1207f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 21 Jul 2020 16:16:16 -0400 Subject: [PATCH 7/9] Convert use of char path[PATH_MAX] to std::string. Fixes potential overflows, quiets compile, should reduce ram quite a bit and might event be faster. Code is also more readable. --- src/zm_event.h | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/zm_event.h b/src/zm_event.h index 38e0fa486..257987500 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -89,15 +89,17 @@ class Event { bool alarm_frame_written; unsigned int tot_score; unsigned int max_score; - char path[PATH_MAX]; - char snapshot_file[PATH_MAX]; - char alarm_file[PATH_MAX]; + std::string path; + std::string snapshot_file; + std::string alarm_file; + VideoWriter* videowriter; FILE* timecodes_fd; - char video_name[PATH_MAX]; - char video_file[PATH_MAX]; - char timecodes_name[PATH_MAX]; - char timecodes_file[PATH_MAX]; + std::string video_name; + std::string video_file; + + std::string timecodes_name; + std::string timecodes_file; int last_db_frame; Storage::Schemes scheme; @@ -137,15 +139,15 @@ class Event { public: static const char *getSubPath( struct tm *time ) { static char subpath[PATH_MAX] = ""; - snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); - return( subpath ); + snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); + return subpath; } static const char *getSubPath( time_t *time ) { return Event::getSubPath( localtime( time ) ); } - char* getEventFile(void) { - return video_file; + const char* getEventFile(void) { + return video_file.c_str(); } public: @@ -155,12 +157,14 @@ class Event { static void EmptyPreAlarmFrames() { while ( pre_alarm_count > 0 ) { int i = pre_alarm_count - 1; +Debug(1, "EmptyreAlarmFrame: %d", i); delete pre_alarm_data[i].image; pre_alarm_data[i].image = NULL; if ( pre_alarm_data[i].alarm_frame ) { delete pre_alarm_data[i].alarm_frame; pre_alarm_data[i].alarm_frame = NULL; } + pre_alarm_count--; } pre_alarm_count = 0; } @@ -174,6 +178,7 @@ class Event { pre_alarm_count++; } void SavePreAlarmFrames() { +Debug(1, "SavePreAlarmFrame: %d", pre_alarm_count); for ( int i = 0; i < pre_alarm_count; i++ ) { AddFrame( pre_alarm_data[i].image, From f217ab54a32423aaf08c721cea29617e810ba40d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 21 Jul 2020 16:17:46 -0400 Subject: [PATCH 8/9] When event_close_mode == CLOSE_ALARM and so we want to close the recording and start a new one, don't do it when alarm_frame_count > 1 until alarm_frames > alarm_frame_count. I believe this fixes a mem leak --- src/zm_monitor.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index ef8e1fa7e..c33c41fad 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1537,13 +1537,14 @@ bool Monitor::Analyse() { && (!event->AlarmFrames()) && (event_close_mode == CLOSE_ALARM) && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length ) + && ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) ) { Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", name, image_count, event->Id()); closeEvent(); } else if ( event ) { // This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames - Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d", + Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d min", Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(), ( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length ); @@ -1629,11 +1630,13 @@ bool Monitor::Analyse() { } } event->AddFrames(pre_event_images, images, timestamps); - } + } // end if pre_event_images + if ( alarm_frame_count ) { +Debug(1, "alarm frame count so SavePreAlarmFrames"); event->SavePreAlarmFrames(); } - } + } // end if event } else if ( state != PREALARM ) { Info("%s: %03d - Gone into prealarm state", name, image_count); shared_data->state = state = PREALARM; @@ -1672,10 +1675,11 @@ bool Monitor::Analyse() { } else { shared_data->state = state = TAPE; } - } - if ( Event::PreAlarmCount() ) - Event::EmptyPreAlarmFrames(); - } // end if score or not + // Not in PREALARM state anymore, can clear PreAlarmCount + if ( Event::PreAlarmCount() ) + Event::EmptyPreAlarmFrames(); + } + } // end if score or not if ( state != IDLE ) { if ( state == PREALARM || state == ALARM ) { From 13e8ed19243d1121792f1316f6410ab2438df4c9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 21 Jul 2020 16:34:46 -0400 Subject: [PATCH 9/9] Clean up code that updates event with video_name. Fix video_file assignment missing / --- src/zm_event.cpp | 50 +++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 328092fab..0193172ac 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -110,20 +110,6 @@ Event::Event( } id = mysql_insert_id(&dbconn); - /* Update event record with DefaultVideo name if possible so image.php can extract frames - if needed, while recording is in progress */ - if ( monitor->GetOptVideoWriter() != 0 ) { - video_name = stringtf("%" PRIu64 "-%s", id, "video.mp4"); - Debug(1, "Updating inserted event with DefaultVideo=%s", video_name.c_str()); - snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); - db_mutex.unlock(); - return; - } - } else { - Debug (1, "GetOptVideoWriter() returned 0, not updating DefaultVideo"); - } db_mutex.unlock(); if ( untimedEvent ) { Warning("Event %d has zero time, setting to current", id); @@ -196,10 +182,11 @@ Event::Event( // Create empty id tag file id_file = stringtf("%s/.%" PRIu64, path.c_str(), id); - if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) + if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) { fclose(id_fp); - else + } else { Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno)); + } } // deep storage or not last_db_frame = 0; @@ -212,8 +199,15 @@ Event::Event( /* Save as video */ if ( monitor->GetOptVideoWriter() != 0 ) { - video_file = path + video_name; - Debug(1, "Writing video file to %s", video_file.c_str()); + video_name = stringtf("%" PRIu64 "-%s", id, "video.mp4"); + snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); + db_mutex.unlock(); + return; + } + video_file = path + "/" + video_name; + Debug(1, "Writing video file to %s", video_file.c_str()); /* X264 MP4 video writer */ if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) { @@ -225,17 +219,17 @@ Event::Event( #else Error("ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2)"); #endif - } - if ( videowriter != NULL ) { - /* Open the video stream */ - int nRet = videowriter->Open(); - if ( nRet != 0 ) { - Error("Failed opening video stream"); - delete videowriter; - videowriter = NULL; - } - } + if ( videowriter != NULL ) { + /* Open the video stream */ + int nRet = videowriter->Open(); + if ( nRet != 0 ) { + Error("Failed opening video stream"); + delete videowriter; + videowriter = NULL; + } + } + } } else { /* No video object */ videowriter = NULL;