2013-03-17 07:45:21 +08:00
//
// ZoneMinder Event Class Implementation, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
2016-12-26 23:23:16 +08:00
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2013-03-17 07:45:21 +08:00
//
# include <fcntl.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <sys/un.h>
# include <sys/uio.h>
# include <sys/ipc.h>
# include <sys/msg.h>
# include <getopt.h>
# include <arpa/inet.h>
# include <glob.h>
2018-04-22 09:55:21 +08:00
# include <cinttypes>
2013-03-17 07:45:21 +08:00
# include "zm.h"
# include "zm_db.h"
# include "zm_time.h"
# include "zm_signal.h"
# include "zm_event.h"
# include "zm_monitor.h"
//#define USE_PREPARED_SQL 1
2017-04-13 21:47:19 +08:00
const char * Event : : frame_type_names [ 3 ] = { " Normal " , " Bulk " , " Alarm " } ;
2013-03-17 07:45:21 +08:00
int Event : : pre_alarm_count = 0 ;
2017-02-05 00:20:21 +08:00
2013-03-17 07:45:21 +08:00
Event : : PreAlarmData Event : : pre_alarm_data [ MAX_PRE_ALARM_FRAMES ] = { { 0 } } ;
2018-01-27 01:21:12 +08:00
Event : : Event (
Monitor * p_monitor ,
struct timeval p_start_time ,
const std : : string & p_cause ,
const StringSetMap & p_noteSetMap ,
bool p_videoEvent ) :
2018-05-04 01:54:05 +08:00
monitor ( p_monitor ) ,
start_time ( p_start_time ) ,
cause ( p_cause ) ,
noteSetMap ( p_noteSetMap ) ,
videoEvent ( p_videoEvent ) ,
videowriter ( NULL )
2013-03-17 07:45:21 +08:00
{
2016-04-04 22:11:48 +08:00
std : : string notes ;
2018-01-27 01:21:12 +08:00
createNotes ( notes ) ;
2016-04-04 22:11:48 +08:00
2018-02-01 03:33:20 +08:00
struct timeval now ;
gettimeofday ( & now , 0 ) ;
2016-04-04 22:11:48 +08:00
bool untimedEvent = false ;
2016-06-22 01:48:32 +08:00
if ( ! start_time . tv_sec ) {
2016-04-04 22:11:48 +08:00
untimedEvent = true ;
2018-02-01 03:33:20 +08:00
start_time = now ;
} else if ( start_time . tv_sec > now . tv_sec ) {
2018-05-24 22:59:26 +08:00
Error (
" StartTime in the future %u.%u > %u.%u " ,
2018-04-30 22:12:34 +08:00
start_time . tv_sec , start_time . tv_usec , now . tv_sec , now . tv_usec
) ;
2018-02-01 03:33:20 +08:00
start_time = now ;
2016-04-04 22:11:48 +08:00
}
2016-04-29 21:11:14 +08:00
Storage * storage = monitor - > getStorage ( ) ;
2018-04-12 22:22:46 +08:00
scheme = storage - > Scheme ( ) ;
2016-04-04 22:11:48 +08:00
2017-01-15 05:55:28 +08:00
unsigned int state_id = 0 ;
2017-01-15 06:07:20 +08:00
zmDbRow dbrow ;
2017-10-11 00:58:06 +08:00
if ( dbrow . fetch ( " SELECT Id FROM States WHERE IsActive=1 " ) ) {
2017-01-15 05:55:28 +08:00
state_id = atoi ( dbrow [ 0 ] ) ;
}
2016-04-29 21:11:14 +08:00
2018-04-12 22:22:46 +08:00
char sql [ ZM_SQL_MED_BUFSIZ ] ;
struct tm * stime = localtime ( & start_time . tv_sec ) ;
2017-12-19 02:17:43 +08:00
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' ) " ,
2017-01-15 06:07:20 +08:00
monitor - > Id ( ) ,
storage - > Id ( ) ,
start_time . tv_sec ,
monitor - > Width ( ) ,
monitor - > Height ( ) ,
cause . c_str ( ) ,
notes . c_str ( ) ,
state_id ,
monitor - > getOrientation ( ) ,
2017-11-22 12:56:22 +08:00
videoEvent ,
2017-12-19 01:52:26 +08:00
monitor - > GetOptSaveJPEGs ( ) ,
2017-12-19 02:25:24 +08:00
storage - > SchemeString ( ) . c_str ( )
2017-01-15 06:07:20 +08:00
) ;
2018-03-02 11:20:52 +08:00
db_mutex . lock ( ) ;
2018-04-12 22:22:46 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't insert event: %s. sql was (%s) " , mysql_error ( & dbconn ) , sql ) ;
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
return ;
2016-04-04 22:11:48 +08:00
}
2018-04-12 22:22:46 +08:00
id = mysql_insert_id ( & dbconn ) ;
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
2016-06-22 01:48:32 +08:00
if ( untimedEvent ) {
2018-04-12 22:22:46 +08:00
Warning ( " Event %d has zero time, setting to current " , id ) ;
2016-04-04 22:11:48 +08:00
}
end_time . tv_sec = 0 ;
frames = 0 ;
alarm_frames = 0 ;
tot_score = 0 ;
max_score = 0 ;
2018-11-13 01:43:20 +08:00
alarm_frame_written = false ;
2016-04-04 22:11:48 +08:00
2017-05-20 02:52:45 +08:00
char id_file [ PATH_MAX ] ;
2016-09-27 21:47:19 +08:00
2018-09-20 07:50:26 +08:00
char * path_ptr = path ;
path_ptr + = snprintf ( path_ptr , sizeof ( path ) , " %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 ( errno ! = EEXIST )
Error ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
}
2017-12-20 00:01:03 +08:00
if ( storage - > Scheme ( ) = = Storage : : DEEP ) {
2016-04-04 22:11:48 +08:00
int dt_parts [ 6 ] ;
dt_parts [ 0 ] = stime - > tm_year - 100 ;
dt_parts [ 1 ] = stime - > tm_mon + 1 ;
dt_parts [ 2 ] = stime - > tm_mday ;
dt_parts [ 3 ] = stime - > tm_hour ;
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 ;
2016-06-22 01:48:32 +08:00
for ( unsigned int i = 0 ; i < sizeof ( dt_parts ) / sizeof ( * dt_parts ) ; i + + ) {
2018-05-04 01:54:05 +08:00
path_ptr + = snprintf ( path_ptr , sizeof ( path ) - ( path_ptr - path ) , " /%02d " , dt_parts [ i ] ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
errno = 0 ;
2018-05-04 01:54:05 +08:00
if ( mkdir ( path , 0755 ) ) {
2017-12-19 01:52:26 +08:00
// FIXME This should not be fatal. Should probably move to a different storage area.
2018-09-24 04:47:06 +08:00
if ( errno ! = EEXIST )
2018-05-04 01:54:05 +08:00
Error ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
2016-04-04 22:11:48 +08:00
}
if ( i = = 2 )
2018-05-04 01:54:05 +08:00
strncpy ( date_path , path , sizeof ( date_path ) ) ;
2016-04-04 22:11:48 +08:00
else if ( i > = 3 )
2018-05-04 01:54:05 +08:00
time_path_ptr + = snprintf ( time_path_ptr , sizeof ( time_path ) - ( time_path_ptr - time_path ) , " %s%02d " , i > 3 ? " / " : " " , dt_parts [ i ] ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
// Create event id symlink
2018-05-04 01:54:05 +08:00
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 ) ) ;
2017-12-20 00:01:03 +08:00
} else if ( storage - > Scheme ( ) = = Storage : : MEDIUM ) {
2018-05-04 01:54:05 +08:00
path_ptr + = snprintf (
2018-09-20 07:50:26 +08:00
path_ptr , sizeof ( path ) , " /%04d-%02d-%02d " ,
stime - > tm_year + 1900 , stime - > tm_mon + 1 , stime - > tm_mday
2017-12-19 01:52:26 +08:00
) ;
2018-05-04 01:54:05 +08:00
if ( mkdir ( path , 0755 ) ) {
2017-12-19 01:52:26 +08:00
if ( errno ! = EEXIST )
2018-05-04 01:54:05 +08:00
Error ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
2017-12-19 01:52:26 +08:00
}
2018-05-04 01:54:05 +08:00
path_ptr + = snprintf ( path_ptr , sizeof ( path ) , " /% " PRIu64 , id ) ;
if ( mkdir ( path , 0755 ) ) {
2017-12-19 01:52:26 +08:00
if ( errno ! = EEXIST )
2018-05-04 01:54:05 +08:00
Error ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
2017-12-19 01:52:26 +08:00
}
2016-06-22 01:48:32 +08:00
} else {
2018-12-14 21:54:29 +08:00
path_ptr + = snprintf ( path_ptr , sizeof ( path ) , " /% " PRIu64 , id ) ;
2018-05-04 01:54:05 +08:00
if ( mkdir ( path , 0755 ) ) {
2018-09-20 07:50:26 +08:00
if ( errno ! = EEXIST )
2018-05-04 01:54:05 +08:00
Error ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
2013-03-17 07:45:21 +08:00
}
2016-09-27 21:47:19 +08:00
2017-09-26 04:22:41 +08:00
// Create empty id tag file
2018-05-04 01:54:05 +08:00
snprintf ( id_file , sizeof ( id_file ) , " %s/.% " PRIu64 , path , id ) ;
if ( FILE * id_fp = fopen ( id_file , " w " ) )
fclose ( id_fp ) ;
2017-09-26 04:22:41 +08:00
else
2018-05-04 01:54:05 +08:00
Error ( " Can't fopen %s: %s " , id_file , strerror ( errno ) ) ;
2017-09-26 04:22:41 +08:00
} // deep storage or not
2016-09-27 21:47:19 +08:00
2016-04-04 22:11:48 +08:00
last_db_frame = 0 ;
2013-03-17 07:45:21 +08:00
2016-04-29 21:11:14 +08:00
video_name [ 0 ] = 0 ;
2013-12-20 00:38:07 +08:00
2019-04-05 00:28:08 +08:00
snprintf ( snapshot_file , sizeof ( snapshot_file ) , " %s/snapshot.jpg " , path ) ;
snprintf ( alarm_file , sizeof ( alarm_file ) , " %s/alarm.jpg " , path ) ;
2016-04-29 21:11:14 +08:00
/* Save as video */
2015-01-12 18:42:17 +08:00
2016-04-29 21:11:14 +08:00
if ( monitor - > GetOptVideoWriter ( ) ! = 0 ) {
2018-05-04 01:54:05 +08:00
snprintf ( video_name , sizeof ( video_name ) , " % " PRIu64 " -%s " , id , " video.mp4 " ) ;
snprintf ( video_file , sizeof ( video_file ) , staticConfig . video_file_format , path , video_name ) ;
2017-10-11 00:58:06 +08:00
Debug ( 1 , " Writing video file to %s " , video_file ) ;
2013-12-20 00:38:07 +08:00
2016-04-29 21:11:14 +08:00
/* X264 MP4 video writer */
2017-05-20 21:54:03 +08:00
if ( monitor - > GetOptVideoWriter ( ) = = Monitor : : X264ENCODE ) {
2013-12-20 00:38:07 +08:00
# if ZM_HAVE_VIDEOWRITER_X264MP4
2016-04-29 21:11:14 +08:00
videowriter = new X264MP4Writer ( video_file , monitor - > Width ( ) , monitor - > Height ( ) , monitor - > Colours ( ) , monitor - > SubpixelOrder ( ) , monitor - > GetOptEncoderParams ( ) ) ;
2013-12-20 00:38:07 +08:00
# else
2016-04-29 21:11:14 +08:00
Error ( " ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2) " ) ;
2013-12-20 00:38:07 +08:00
# endif
2016-04-29 21:11:14 +08:00
}
2017-05-20 20:34:46 +08:00
if ( videowriter ! = NULL ) {
2016-04-29 21:11:14 +08:00
/* Open the video stream */
2017-01-17 10:11:28 +08:00
int nRet = videowriter - > Open ( ) ;
2017-11-23 04:10:54 +08:00
if ( nRet ! = 0 ) {
2016-04-29 21:11:14 +08:00
Error ( " Failed opening video stream " ) ;
delete videowriter ;
videowriter = NULL ;
}
}
} else {
/* No video object */
videowriter = NULL ;
}
2013-12-20 00:38:07 +08:00
2017-01-17 10:11:28 +08:00
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
Event : : ~ Event ( ) {
2016-09-27 21:47:19 +08:00
2018-04-12 23:39:30 +08:00
// 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.
2016-04-04 22:11:48 +08:00
2016-04-29 21:11:14 +08:00
/* Close the video file */
if ( videowriter ! = NULL ) {
2017-05-17 00:04:56 +08:00
int nRet = videowriter - > Close ( ) ;
2017-10-12 23:54:43 +08:00
if ( nRet ! = 0 ) {
2016-04-29 21:11:14 +08:00
Error ( " Failed closing video stream " ) ;
}
delete videowriter ;
videowriter = NULL ;
}
2013-12-20 00:38:07 +08:00
2018-03-03 10:25:20 +08:00
struct DeltaTimeval delta_time ;
DELTA_TIMEVAL ( delta_time , end_time , start_time , DT_PREC_2 ) ;
2018-04-12 23:39:30 +08:00
Debug ( 2 , " start_time:%d.%d end_time%d.%d " , start_time . tv_sec , start_time . tv_usec , end_time . tv_sec , end_time . tv_usec ) ;
2018-03-03 10:25:20 +08:00
if ( frames > last_db_frame ) {
2018-04-12 23:39:30 +08:00
Debug ( 1 , " Adding closing frame %d to DB " , frames ) ;
2019-02-20 01:00:17 +08:00
frame_data . push ( new Frame ( id , frames , NORMAL , end_time , delta_time , 0 ) ) ;
2018-03-03 10:25:20 +08:00
}
2019-02-20 01:00:17 +08:00
if ( frame_data . size ( ) )
WriteDbFrames ( ) ;
2018-03-03 10:25:20 +08:00
2019-02-20 01:00:17 +08:00
// Should not be static because we might be multi-threaded
char sql [ ZM_SQL_MED_BUFSIZ ] ;
2018-04-13 04:40:11 +08:00
snprintf ( sql , sizeof ( sql ) ,
2018-04-18 01:57:19 +08:00
" 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 ,
2018-12-06 02:18:21 +08:00
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 ) ;
2018-03-02 11:20:52 +08:00
db_mutex . lock ( ) ;
2018-04-28 04:20:38 +08:00
while ( mysql_query ( & dbconn , sql ) & & ! zm_terminate ) {
2018-04-12 22:22:46 +08:00
db_mutex . unlock ( ) ;
2018-12-15 01:22:10 +08:00
Error ( " Can't update event: %s reason: %s " , sql , mysql_error ( & dbconn ) ) ;
2018-04-04 05:41:32 +08:00
sleep ( 1 ) ;
2018-04-12 22:22:46 +08:00
db_mutex . lock ( ) ;
2016-04-04 22:11:48 +08:00
}
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
2018-04-12 22:22:46 +08:00
2018-04-12 23:39:30 +08:00
} // Event::~Event()
2013-03-17 07:45:21 +08:00
2018-04-12 23:39:30 +08:00
void Event : : createNotes ( std : : string & notes ) {
2016-04-04 22:11:48 +08:00
notes . clear ( ) ;
2017-11-17 20:52:26 +08:00
for ( StringSetMap : : const_iterator mapIter = noteSetMap . begin ( ) ; mapIter ! = noteSetMap . end ( ) ; + + mapIter ) {
2016-04-04 22:11:48 +08:00
notes + = mapIter - > first ;
notes + = " : " ;
const StringSet & stringSet = mapIter - > second ;
2017-11-17 20:52:26 +08:00
for ( StringSet : : const_iterator setIter = stringSet . begin ( ) ; setIter ! = stringSet . end ( ) ; + + setIter ) {
2016-04-04 22:11:48 +08:00
if ( setIter ! = stringSet . begin ( ) )
notes + = " , " ;
notes + = * setIter ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
2018-05-04 01:54:05 +08:00
bool Event : : WriteFrameImage ( Image * image , struct timeval timestamp , const char * event_file , bool alarm_frame ) {
2016-04-04 22:11:48 +08:00
2017-01-17 01:56:09 +08:00
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
2017-09-26 04:22:41 +08:00
bool rc ;
2018-08-03 03:40:33 +08:00
Debug ( 3 , " Writing image to %s " , event_file ) ;
2017-01-17 01:56:09 +08:00
2017-09-26 04:22:41 +08:00
if ( ! config . timestamp_on_capture ) {
// stash the image we plan to use in another pointer regardless if timestamped.
Image * ts_image = new Image ( * image ) ;
2018-05-04 01:54:05 +08:00
monitor - > TimestampImage ( ts_image , & timestamp ) ;
rc = ts_image - > WriteJpeg ( event_file , thisquality , ( monitor - > Exif ( ) ? timestamp : ( timeval ) { 0 , 0 } ) ) ; // exif is only timestamp at present this switches on or off for write
2017-09-26 04:22:41 +08:00
delete ( ts_image ) ;
} else {
2018-05-04 01:54:05 +08:00
rc = image - > WriteJpeg ( event_file , thisquality , ( monitor - > Exif ( ) ? timestamp : ( timeval ) { 0 , 0 } ) ) ; // exif is only timestamp at present this switches on or off for write
2017-09-26 04:22:41 +08:00
}
return rc ;
2013-03-17 07:45:21 +08:00
}
2016-06-22 01:48:32 +08:00
bool Event : : WriteFrameVideo ( const Image * image , const struct timeval timestamp , VideoWriter * videow ) {
2016-04-29 21:11:14 +08:00
const Image * frameimg = image ;
Image ts_image ;
/* Checking for invalid parameters */
if ( videow = = NULL ) {
Error ( " NULL Video object " ) ;
return false ;
}
/* If the image does not contain a timestamp, add the timestamp */
2017-10-11 03:08:24 +08:00
if ( ! config . timestamp_on_capture ) {
2016-04-29 21:11:14 +08:00
ts_image = * image ;
monitor - > TimestampImage ( & ts_image , & timestamp ) ;
frameimg = & ts_image ;
}
/* Calculate delta time */
struct DeltaTimeval delta_time3 ;
DELTA_TIMEVAL ( delta_time3 , timestamp , start_time , DT_PREC_3 ) ;
unsigned int timeMS = ( delta_time3 . sec * delta_time3 . prec ) + delta_time3 . fsec ;
/* Encode and write the frame */
2017-10-11 03:08:24 +08:00
if ( videowriter - > Encode ( frameimg , timeMS ) ! = 0 ) {
2016-04-29 21:11:14 +08:00
Error ( " Failed encoding video frame " ) ;
}
2018-04-04 05:41:32 +08:00
return true ;
2013-12-20 00:38:07 +08:00
}
2016-06-22 01:48:32 +08:00
void Event : : updateNotes ( const StringSetMap & newNoteSetMap ) {
2016-04-04 22:11:48 +08:00
bool update = false ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
//Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() );
2016-06-22 01:48:32 +08:00
if ( newNoteSetMap . size ( ) > 0 ) {
if ( noteSetMap . size ( ) = = 0 ) {
2016-04-04 22:11:48 +08:00
noteSetMap = newNoteSetMap ;
update = true ;
2016-06-22 01:48:32 +08:00
} else {
2017-11-17 20:52:26 +08:00
for ( StringSetMap : : const_iterator newNoteSetMapIter = newNoteSetMap . begin ( ) ; newNoteSetMapIter ! = newNoteSetMap . end ( ) ; + + newNoteSetMapIter ) {
2016-04-04 22:11:48 +08:00
const std : : string & newNoteGroup = newNoteSetMapIter - > first ;
const StringSet & newNoteSet = newNoteSetMapIter - > second ;
//Info( "Got %d new strings", newNoteSet.size() );
2016-06-22 01:48:32 +08:00
if ( newNoteSet . size ( ) > 0 ) {
2016-04-04 22:11:48 +08:00
StringSetMap : : iterator noteSetMapIter = noteSetMap . find ( newNoteGroup ) ;
2016-06-22 01:48:32 +08:00
if ( noteSetMapIter = = noteSetMap . end ( ) ) {
2016-04-04 22:11:48 +08:00
//Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() );
noteSetMap . insert ( StringSetMap : : value_type ( newNoteGroup , newNoteSet ) ) ;
2013-03-17 07:45:21 +08:00
update = true ;
2016-06-22 01:48:32 +08:00
} else {
2016-04-04 22:11:48 +08:00
StringSet & noteSet = noteSetMapIter - > second ;
//Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() );
2017-11-17 20:52:26 +08:00
for ( StringSet : : const_iterator newNoteSetIter = newNoteSet . begin ( ) ; newNoteSetIter ! = newNoteSet . end ( ) ; + + newNoteSetIter ) {
2016-04-04 22:11:48 +08:00
const std : : string & newNote = * newNoteSetIter ;
StringSet : : iterator noteSetIter = noteSet . find ( newNote ) ;
2016-06-22 01:48:32 +08:00
if ( noteSetIter = = noteSet . end ( ) ) {
2016-04-04 22:11:48 +08:00
noteSet . insert ( newNote ) ;
update = true ;
}
2017-05-20 20:26:55 +08:00
} // end for
} // end if ( noteSetMap.size() == 0
} // end if newNoteSetupMap.size() > 0
} // end foreach newNoteSetMap
} // end if have old notes
} // end if have new notes
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
if ( update ) {
2016-04-04 22:11:48 +08:00
std : : string notes ;
createNotes ( notes ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
Debug ( 2 , " Updating notes for event %d, '%s' " , id , notes . c_str ( ) ) ;
2018-12-06 02:18:21 +08:00
static char sql [ ZM_SQL_LGE_BUFSIZ ] ;
2013-03-17 07:45:21 +08:00
# if USE_PREPARED_SQL
2016-04-04 22:11:48 +08:00
static MYSQL_STMT * stmt = 0 ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
char notesStr [ ZM_SQL_MED_BUFSIZ ] = " " ;
unsigned long notesLen = 0 ;
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
if ( ! stmt ) {
2016-04-04 22:11:48 +08:00
const char * sql = " update Events set Notes = ? where Id = ? " ;
stmt = mysql_stmt_init ( & dbconn ) ;
2016-06-22 01:48:32 +08:00
if ( mysql_stmt_prepare ( stmt , sql , strlen ( sql ) ) ) {
2016-04-04 22:11:48 +08:00
Fatal ( " Unable to prepare sql '%s': %s " , sql , mysql_stmt_error ( stmt ) ) ;
}
/* Get the parameter count from the statement */
2016-06-22 01:48:32 +08:00
if ( mysql_stmt_param_count ( stmt ) ! = 2 ) {
2016-04-04 22:11:48 +08:00
Fatal ( " Unexpected parameter count %ld in sql '%s' " , mysql_stmt_param_count ( stmt ) , sql ) ;
}
MYSQL_BIND bind [ 2 ] ;
memset ( bind , 0 , sizeof ( bind ) ) ;
/* STRING PARAM */
bind [ 0 ] . buffer_type = MYSQL_TYPE_STRING ;
bind [ 0 ] . buffer = ( char * ) notesStr ;
bind [ 0 ] . buffer_length = sizeof ( notesStr ) ;
bind [ 0 ] . is_null = 0 ;
bind [ 0 ] . length = & notesLen ;
bind [ 1 ] . buffer_type = MYSQL_TYPE_LONG ;
bind [ 1 ] . buffer = ( char * ) & id ;
bind [ 1 ] . is_null = 0 ;
bind [ 1 ] . length = 0 ;
/* Bind the buffers */
2016-06-22 01:48:32 +08:00
if ( mysql_stmt_bind_param ( stmt , bind ) ) {
2016-04-04 22:11:48 +08:00
Fatal ( " Unable to bind sql '%s': %s " , sql , mysql_stmt_error ( stmt ) ) ;
}
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
strncpy ( notesStr , notes . c_str ( ) , sizeof ( notesStr ) ) ;
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
if ( mysql_stmt_execute ( stmt ) ) {
2016-04-04 22:11:48 +08:00
Fatal ( " Unable to execute sql '%s': %s " , sql , mysql_stmt_error ( stmt ) ) ;
}
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
static char escapedNotes [ ZM_SQL_MED_BUFSIZ ] ;
2013-03-17 07:45:21 +08:00
2018-04-28 04:20:38 +08:00
mysql_real_escape_string ( & dbconn , escapedNotes , notes . c_str ( ) , notes . length ( ) ) ;
2013-03-17 07:45:21 +08:00
2018-04-28 04:20:38 +08:00
snprintf ( sql , sizeof ( sql ) , " UPDATE Events SET Notes = '%s' WHERE Id = % " PRIu64 , escapedNotes , id ) ;
2018-03-02 11:20:52 +08:00
db_mutex . lock ( ) ;
2018-04-28 04:20:38 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't insert event: %s " , mysql_error ( & dbconn ) ) ;
2013-03-17 07:45:21 +08:00
}
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
2016-04-04 22:11:48 +08:00
# endif
2018-04-28 04:20:38 +08:00
} // end if update
2013-03-17 07:45:21 +08:00
}
2016-06-22 01:48:32 +08:00
void Event : : AddFrames ( int n_frames , Image * * images , struct timeval * * timestamps ) {
2016-04-04 22:11:48 +08:00
for ( int i = 0 ; i < n_frames ; i + = ZM_SQL_BATCH_SIZE ) {
AddFramesInternal ( n_frames , i , images , timestamps ) ;
}
2013-10-27 09:41:12 +08:00
}
2016-06-22 01:48:32 +08:00
void Event : : AddFramesInternal ( int n_frames , int start_frame , Image * * images , struct timeval * * timestamps ) {
2016-04-04 22:11:48 +08:00
static char sql [ ZM_SQL_LGE_BUFSIZ ] ;
2018-05-04 01:54:05 +08:00
strncpy ( sql , " insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values " , sizeof ( sql ) ) ;
2016-04-04 22:11:48 +08:00
int frameCount = 0 ;
2016-06-22 01:48:32 +08:00
for ( int i = start_frame ; i < n_frames & & i - start_frame < ZM_SQL_BATCH_SIZE ; i + + ) {
2017-10-23 21:56:30 +08:00
if ( timestamps [ i ] - > tv_sec < = 0 ) {
2018-05-04 01:54:05 +08:00
Debug ( 1 , " Not adding pre-capture frame %d, zero or less than 0 timestamp " , i ) ;
2016-04-04 22:11:48 +08:00
continue ;
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
frames + + ;
2013-03-17 07:45:21 +08:00
2018-01-14 04:15:14 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 1 ) {
2019-04-05 00:37:25 +08:00
static char event_file [ PATH_MAX ] ;
snprintf ( event_file , sizeof ( event_file ) , staticConfig . capture_file_format , path , frames ) ;
2018-05-04 01:54:05 +08:00
Debug ( 1 , " Writing pre-capture frame %d " , frames ) ;
WriteFrameImage ( images [ i ] , * ( timestamps [ i ] ) , event_file ) ;
2018-01-14 04:15:14 +08:00
} else {
2016-04-29 21:11:14 +08:00
//If this is the first frame, we should add a thumbnail to the event directory
2017-10-08 21:13:56 +08:00
// 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 ) {
2018-05-04 01:54:05 +08:00
WriteFrameImage ( images [ i ] , * ( timestamps [ i ] ) , snapshot_file ) ;
2016-04-29 21:11:14 +08:00
}
2014-12-25 02:45:50 +08:00
}
2013-12-20 00:38:07 +08:00
if ( videowriter ! = NULL ) {
2018-05-04 01:54:05 +08:00
WriteFrameVideo ( images [ i ] , * ( timestamps [ i ] ) , videowriter ) ;
2013-12-20 00:38:07 +08:00
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
struct DeltaTimeval delta_time ;
DELTA_TIMEVAL ( delta_time , * ( timestamps [ i ] ) , start_time , DT_PREC_2 ) ;
2018-01-31 00:23:09 +08:00
// Delta is Decimal(8,2) so 6 integer digits and 2 decimal digits
if ( delta_time . sec > 999999 ) {
2018-01-27 01:21:12 +08:00
Warning ( " Invalid delta_time from_unixtime(%ld), %s%ld.%02ld " ,
timestamps [ i ] - > tv_sec , delta_time . positive ? " " : " - " , delta_time . sec , delta_time . fsec ) ;
delta_time . sec = 0 ;
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
int sql_len = strlen ( sql ) ;
2018-05-04 01:54:05 +08:00
snprintf ( sql + sql_len , sizeof ( sql ) - sql_len , " ( % " PRIu64 " , %d, from_unixtime(%ld), %s%ld.%02ld ), " , id , frames , timestamps [ i ] - > tv_sec , delta_time . positive ? " " : " - " , delta_time . sec , delta_time . fsec ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
frameCount + + ;
2018-04-28 04:20:38 +08:00
} // end foreach frame
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
if ( frameCount ) {
2018-05-04 01:54:05 +08:00
Debug ( 1 , " Adding %d/%d frames to DB " , frameCount , n_frames ) ;
2016-04-04 22:11:48 +08:00
* ( sql + strlen ( sql ) - 2 ) = ' \0 ' ;
2018-03-02 11:20:52 +08:00
db_mutex . lock ( ) ;
2016-06-22 01:48:32 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2018-05-04 01:54:05 +08:00
Error ( " Can't insert frames: %s, sql was (%s) " , mysql_error ( & dbconn ) , sql ) ;
2013-03-17 07:45:21 +08:00
}
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
2016-04-04 22:11:48 +08:00
last_db_frame = frames ;
2016-06-22 01:48:32 +08:00
} else {
2018-05-04 01:54:05 +08:00
Debug ( 1 , " No valid pre-capture frames to add " ) ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
2018-12-06 02:18:21 +08:00
void Event : : WriteDbFrames ( ) {
static char sql [ ZM_SQL_LGE_BUFSIZ ] ;
char * sql_ptr = ( char * ) & sql ;
sql_ptr + = snprintf ( sql , sizeof ( sql ) ,
" INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) VALUES "
) ;
while ( frame_data . size ( ) ) {
Frame * frame = frame_data . front ( ) ;
frame_data . pop ( ) ;
sql_ptr + = snprintf ( sql_ptr , sizeof ( sql ) - ( sql_ptr - ( char * ) & sql ) , " ( % " PRIu64 " , %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d ), " ,
id , frame - > frame_id , frame_type_names [ frame - > type ] ,
frame - > timestamp . tv_sec ,
frame - > delta . positive ? " " : " - " ,
frame - > delta . sec ,
frame - > delta . fsec ,
frame - > score ) ;
delete frame ;
}
* ( sql_ptr - 2 ) = ' \0 ' ;
db_mutex . lock ( ) ;
if ( mysql_query ( & dbconn , sql ) ) {
db_mutex . unlock ( ) ;
2019-02-20 01:00:17 +08:00
Error ( " Can't insert frames: %s, sql was %s " , mysql_error ( & dbconn ) , sql ) ;
2018-12-06 02:18:21 +08:00
return ;
}
db_mutex . unlock ( ) ;
2019-02-20 01:00:17 +08:00
} // end void Event::WriteDbFrames()
2018-12-06 02:18:21 +08:00
2018-05-04 01:54:05 +08:00
void Event : : AddFrame ( Image * image , struct timeval timestamp , int score , Image * alarm_image ) {
2016-06-22 01:48:32 +08:00
if ( ! timestamp . tv_sec ) {
2018-05-04 01:54:05 +08:00
Debug ( 1 , " Not adding new frame, zero timestamp " ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
frames + + ;
2013-03-17 07:45:21 +08:00
2017-09-26 04:22:41 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 1 ) {
2019-04-05 00:37:25 +08:00
static char event_file [ PATH_MAX ] ;
snprintf ( event_file , sizeof ( event_file ) , staticConfig . capture_file_format , path , frames ) ;
2018-05-04 01:54:05 +08:00
Debug ( 1 , " Writing capture frame %d to %s " , frames , event_file ) ;
if ( ! WriteFrameImage ( image , timestamp , event_file ) ) {
2017-09-26 04:22:41 +08:00
Error ( " Failed to write frame image " ) ;
}
2018-01-14 04:15:14 +08:00
} else {
//If this is the first frame, we should add a thumbnail to the event directory
2019-04-05 00:37:25 +08:00
if ( ( frames = = 1 ) | | ( score > ( int ) max_score ) ) {
2018-05-04 01:54:05 +08:00
WriteFrameImage ( image , timestamp , snapshot_file ) ;
2018-01-14 04:15:14 +08:00
}
2018-11-13 01:43:20 +08:00
// The first frame with a score will be the frame that alarmed the event
2019-04-05 00:37:25 +08:00
if ( ( ! alarm_frame_written ) & & ( score > 0 ) ) {
2018-11-13 01:43:20 +08:00
alarm_frame_written = true ;
WriteFrameImage ( image , timestamp , alarm_file ) ;
}
2016-04-29 21:11:14 +08:00
}
if ( videowriter ! = NULL ) {
2017-09-26 04:22:41 +08:00
WriteFrameVideo ( image , timestamp , videowriter ) ;
2016-04-29 21:11:14 +08:00
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
struct DeltaTimeval delta_time ;
2018-05-04 01:54:05 +08:00
DELTA_TIMEVAL ( delta_time , timestamp , start_time , DT_PREC_2 ) ;
2013-03-17 07:45:21 +08:00
2017-01-09 05:53:29 +08:00
FrameType frame_type = score > 0 ? ALARM : ( score < 0 ? BULK : NORMAL ) ;
2017-10-19 08:46:26 +08:00
// < 0 means no motion detection is being done.
2016-04-04 22:11:48 +08:00
if ( score < 0 )
score = 0 ;
2013-03-17 07:45:21 +08:00
2018-09-12 01:19:33 +08:00
bool db_frame = ( frame_type ! = BULK ) | | ( ! frames ) | | ( ( frames % config . bulk_frame_interval ) = = 0 ) ;
2016-06-22 01:48:32 +08:00
if ( db_frame ) {
2016-04-04 22:11:48 +08:00
static char sql [ ZM_SQL_MED_BUFSIZ ] ;
2018-12-06 02:18:21 +08:00
2019-02-20 01:00:17 +08:00
frame_data . push ( new Frame ( id , frames , frame_type , timestamp , delta_time , score ) ) ;
if ( frame_data . size ( ) > 20 ) {
2018-12-06 02:18:21 +08:00
WriteDbFrames ( ) ;
2019-02-20 01:00:17 +08:00
Debug ( 1 , " Adding 20 frames to DB " ) ;
2018-12-06 02:18:21 +08:00
last_db_frame = frames ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
// We are writing a Bulk frame
2017-04-10 05:36:24 +08:00
if ( frame_type = = BULK ) {
2018-04-28 04:20:38 +08:00
snprintf ( sql , sizeof ( sql ) ,
2018-04-18 01:57:19 +08:00
" UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = % " PRIu64 ,
2017-05-20 02:43:49 +08:00
( delta_time . positive ? " " : " - " ) ,
delta_time . sec , delta_time . fsec ,
frames ,
alarm_frames ,
tot_score ,
( int ) ( alarm_frames ? ( tot_score / alarm_frames ) : 0 ) ,
max_score ,
id
) ;
2018-03-02 11:20:52 +08:00
db_mutex . lock ( ) ;
2018-04-28 04:20:38 +08:00
while ( mysql_query ( & dbconn , sql ) & & ! zm_terminate ) {
2018-04-13 04:40:11 +08:00
Error ( " Can't update event: %s " , mysql_error ( & dbconn ) ) ;
db_mutex . unlock ( ) ;
2018-04-04 05:41:32 +08:00
sleep ( 1 ) ;
2018-04-13 04:40:11 +08:00
db_mutex . lock ( ) ;
2016-04-04 22:11:48 +08:00
}
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
2018-12-06 02:18:21 +08:00
} // end if frame_type == BULK
2017-09-26 04:22:41 +08:00
} // end if db_frame
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
end_time = timestamp ;
2013-03-17 07:45:21 +08:00
2017-01-09 05:53:29 +08:00
// We are writing an Alarm frame
2017-04-10 05:36:24 +08:00
if ( frame_type = = ALARM ) {
2016-04-04 22:11:48 +08:00
alarm_frames + + ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
tot_score + = score ;
if ( score > ( int ) max_score )
max_score = score ;
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
if ( alarm_image ) {
2017-09-26 04:22:41 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 2 ) {
2018-08-03 03:40:33 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . analyse_file_format , path , frames ) ;
Debug ( 1 , " Writing analysis frame %d " , frames ) ;
if ( ! WriteFrameImage ( alarm_image , timestamp , event_file , true ) ) {
Error ( " Failed to write analysis frame image " ) ;
}
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
2019-04-05 00:37:25 +08:00
} // end if frame_type == ALARM
2016-04-29 21:11:14 +08:00
/* This makes viewing the diagnostic images impossible because it keeps deleting them
2017-05-20 02:43:49 +08:00
if ( config . record_diag_images ) {
2016-04-04 22:11:48 +08:00
char diag_glob [ PATH_MAX ] = " " ;
2017-06-13 09:39:37 +08:00
snprintf ( diag_glob , sizeof ( diag_glob ) , " %s/%d/diag-*.jpg " , staticConfig . DIR_EVENTS . c_str ( ) , monitor - > Id ( ) ) ;
2016-04-04 22:11:48 +08:00
glob_t pglob ;
int glob_status = glob ( diag_glob , 0 , 0 , & pglob ) ;
2017-05-20 02:43:49 +08:00
if ( glob_status ! = 0 ) {
if ( glob_status < 0 ) {
2016-04-04 22:11:48 +08:00
Error ( " Can't glob '%s': %s " , diag_glob , strerror ( errno ) ) ;
2017-05-20 02:43:49 +08:00
} else {
2016-04-04 22:11:48 +08:00
Debug ( 1 , " Can't glob '%s': %d " , diag_glob , glob_status ) ;
}
2017-05-20 02:43:49 +08:00
} else {
2016-04-04 22:11:48 +08:00
char new_diag_path [ PATH_MAX ] = " " ;
2017-05-20 02:43:49 +08:00
for ( int i = 0 ; i < pglob . gl_pathc ; i + + ) {
2016-04-04 22:11:48 +08:00
char * diag_path = pglob . gl_pathv [ i ] ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
char * diag_file = strstr ( diag_path , " diag- " ) ;
2013-03-17 07:45:21 +08:00
2017-05-20 02:43:49 +08:00
if ( diag_file ) {
2016-04-04 22:11:48 +08:00
snprintf ( new_diag_path , sizeof ( new_diag_path ) , general_file_format , path , frames , diag_file ) ;
2013-03-17 07:45:21 +08:00
2017-05-20 02:43:49 +08:00
if ( rename ( diag_path , new_diag_path ) < 0 ) {
2016-04-04 22:11:48 +08:00
Error ( " Can't rename '%s' to '%s': %s " , diag_path , new_diag_path , strerror ( errno ) ) ;
}
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
globfree ( & pglob ) ;
}
*/
2013-03-17 07:45:21 +08:00
}