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 ,
2018-01-30 00:52:17 +08:00
const StringSetMap & p_noteSetMap
2018-01-27 02:02:16 +08:00
) :
2018-04-14 23:03:08 +08:00
monitor ( p_monitor ) ,
start_time ( p_start_time ) ,
cause ( p_cause ) ,
noteSetMap ( p_noteSetMap )
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-05-20 22:39:14 +08:00
// Copy it in case opening the mp4 doesn't work we can set it to another value
save_jpegs = monitor - > GetOptSaveJPEGs ( ) ;
2018-04-12 22:22:46 +08:00
char sql [ ZM_SQL_MED_BUFSIZ ] ;
2018-04-14 23:03:08 +08:00
snprintf ( sql , sizeof ( sql ) ,
" INSERT INTO Events "
" ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) "
2018-08-11 22:08:30 +08:00
" 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 00:58:15 +08:00
( monitor - > GetOptVideoWriter ( ) ! = 0 ? 1 : 0 ) ,
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-02-16 04:54:13 +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-02-16 04:54:13 +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 ;
2017-11-22 00:58:15 +08:00
have_video_keyframe = false ;
2016-04-04 22:11:48 +08:00
2018-04-14 23:03:08 +08:00
struct tm * stime = localtime ( & start_time . tv_sec ) ;
2016-09-27 21:47:19 +08:00
2018-10-21 05:31:14 +08:00
path = stringtf ( " %s/%d " , storage - > Path ( ) , monitor - > Id ( ) ) ;
2018-09-20 07:50:26 +08:00
// Try to make the Monitor Dir. Normally this would exist, but in odd cases might not.
2018-10-21 05:31:14 +08:00
if ( mkdir ( path . c_str ( ) , 0755 ) ) {
2018-09-20 07:50:26 +08:00
if ( errno ! = EEXIST )
Error ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
}
2016-04-04 22:11:48 +08:00
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-04-14 23:03:08 +08:00
path + = stringtf ( " /%02d " , dt_parts [ i ] ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
errno = 0 ;
2018-04-14 23:03:08 +08:00
if ( mkdir ( path . c_str ( ) , 0755 ) ) {
2017-12-19 01:52:26 +08:00
// FIXME This should not be fatal. Should probably move to a different storage area.
2018-08-10 23:16:04 +08:00
if ( errno ! = EEXIST )
2018-04-14 23:03:08 +08:00
Error ( " Can't mkdir %s: %s " , path . c_str ( ) , strerror ( errno ) ) ;
2016-04-04 22:11:48 +08:00
}
if ( i = = 2 )
2018-04-14 23:03:08 +08:00
strncpy ( date_path , path . c_str ( ) , sizeof ( date_path ) ) ;
2016-04-04 22:11:48 +08:00
else if ( i > = 3 )
2018-04-14 23:03:08 +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-06 00:25:11 +08:00
std : : string id_file = stringtf ( " %s/.% " PRIu64 , date_path , id ) ;
2018-04-14 23:03:08 +08:00
if ( symlink ( time_path , id_file . c_str ( ) ) < 0 )
Error ( " Can't symlink %s -> %s: %s " , id_file . c_str ( ) , path . c_str ( ) , strerror ( errno ) ) ;
2017-12-20 00:01:03 +08:00
} else if ( storage - > Scheme ( ) = = Storage : : MEDIUM ) {
2018-10-21 05:31:14 +08:00
path + = stringtf ( " /%04d-%02d-%02d " ,
stime - > tm_year + 1900 , stime - > tm_mon + 1 , stime - > tm_mday
2017-12-19 01:52:26 +08:00
) ;
2018-04-14 23:03:08 +08:00
if ( mkdir ( path . c_str ( ) , 0755 ) ) {
2017-12-19 01:52:26 +08:00
if ( errno ! = EEXIST )
2018-04-14 23:03:08 +08:00
Error ( " Can't mkdir %s: %s " , path . c_str ( ) , strerror ( errno ) ) ;
2017-12-19 01:52:26 +08:00
}
2018-05-06 00:25:11 +08:00
path + = stringtf ( " /% " PRIu64 , id ) ;
2018-04-14 23:03:08 +08:00
if ( mkdir ( path . c_str ( ) , 0755 ) ) {
2017-12-19 01:52:26 +08:00
if ( errno ! = EEXIST )
2018-04-14 23:03:08 +08:00
Error ( " Can't mkdir %s: %s " , path . c_str ( ) , strerror ( errno ) ) ;
2017-12-19 01:52:26 +08:00
}
2016-06-22 01:48:32 +08:00
} else {
2017-10-26 04:25:44 +08:00
// Shallow Storage
2018-10-21 05:31:14 +08:00
path = stringtf ( " /% " PRIu64 , id ) ;
2018-04-14 23:03:08 +08:00
if ( mkdir ( path . c_str ( ) , 0755 ) ) {
2018-09-20 07:50:26 +08:00
if ( errno ! = EEXIST )
2018-04-14 23:03:08 +08:00
Error ( " Can't mkdir %s: %s " , path . c_str ( ) , 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-08-11 22:08:30 +08:00
std : : string id_file = stringtf ( " %s/.% " PRIu64 , path . c_str ( ) , id ) ;
2018-04-14 23:03:08 +08:00
if ( FILE * id_fp = fopen ( id_file . c_str ( ) , " w " ) )
fclose ( id_fp ) ;
2017-09-26 04:22:41 +08:00
else
2018-04-14 23:03:08 +08:00
Error ( " Can't fopen %s: %s " , id_file . c_str ( ) , strerror ( errno ) ) ;
2017-09-26 04:22:41 +08:00
} // deep storage or not
2018-08-18 04:02:40 +08:00
2018-08-11 22:08:30 +08:00
Debug ( 2 , " Created event %d at %s " , id , path . c_str ( ) ) ;
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
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-08-12 06:49:30 +08:00
Debug ( 2 , " Video writer was %d " , monitor - > GetOptVideoWriter ( ) ) ;
2017-11-14 14:59:15 +08:00
std : : string container = monitor - > OutputContainer ( ) ;
if ( container = = " auto " | | container = = " " ) {
2018-02-28 23:17:16 +08:00
if ( monitor - > OutputCodec ( ) = = AV_CODEC_ID_H264 ) {
2017-11-14 14:59:15 +08:00
container = " mp4 " ;
} else {
container = " mkv " ;
2016-04-29 21:11:14 +08:00
}
}
2017-11-14 14:59:15 +08:00
2018-05-13 07:44:20 +08:00
snprintf ( video_name , sizeof ( video_name ) , " % " PRIu64 " -%s.%s " , id , " video " , container . c_str ( ) ) ;
2018-04-14 23:03:08 +08:00
snprintf ( video_file , sizeof ( video_file ) , staticConfig . video_file_format , path . c_str ( ) , video_name ) ;
Debug ( 1 , " Writing video file to %s " , video_file ) ;
2017-11-13 23:17:46 +08:00
Camera * camera = monitor - > getCamera ( ) ;
videoStore = new VideoStore (
video_file ,
2017-11-14 15:39:58 +08:00
container . c_str ( ) ,
2017-11-13 23:17:46 +08:00
camera - > get_VideoStream ( ) ,
( monitor - > RecordAudio ( ) ? camera - > get_AudioStream ( ) : NULL ) ,
monitor ) ;
if ( ! videoStore - > open ( ) ) {
delete videoStore ;
videoStore = NULL ;
2018-05-20 22:39:14 +08:00
save_jpegs | = 1 ; // Turn on jpeg storage
2018-04-14 23:03:08 +08:00
}
2016-04-29 21:11:14 +08:00
}
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 */
2018-04-14 23:03:08 +08:00
if ( videoStore ) {
2018-08-12 06:49:30 +08:00
Debug ( 2 , " Deleting video store " ) ;
2018-04-14 23:03:08 +08:00
delete videoStore ;
videoStore = NULL ;
2016-04-29 21:11:14 +08:00
}
2013-12-20 00:38:07 +08:00
2018-04-12 23:39:30 +08:00
// Should not be static because we are multi-threaded
2018-04-12 22:22:46 +08:00
char sql [ ZM_SQL_MED_BUFSIZ ] ;
2016-09-27 21:47:19 +08:00
struct DeltaTimeval delta_time ;
2018-01-27 01:21:12 +08:00
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 ) ;
2016-09-27 21:47:19 +08:00
2016-06-22 01:48:32 +08:00
if ( frames > last_db_frame ) {
2018-04-12 23:39:30 +08:00
Debug ( 1 , " Adding closing frame %d to DB " , frames ) ;
2018-04-11 04:06:36 +08:00
snprintf ( sql , sizeof ( sql ) ,
2018-04-18 01:57:19 +08:00
" INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( % " PRIu64 " , %d, from_unixtime( %ld ), %s%ld.%02ld ) " ,
2018-04-12 23:39:30 +08:00
id , frames , end_time . tv_sec , delta_time . positive ? " " : " - " , delta_time . sec , delta_time . fsec ) ;
2018-02-16 04:54:13 +08:00
db_mutex . lock ( ) ;
2018-04-12 23:39:30 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't insert frame: %s " , mysql_error ( & dbconn ) ) ;
2018-03-03 10:25:20 +08:00
} else {
Debug ( 1 , " Success writing last frame " ) ;
2013-03-17 07:45:21 +08:00
}
2018-02-16 04:54:13 +08:00
db_mutex . unlock ( ) ;
2016-04-04 22:11:48 +08:00
}
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-04-13 04:40:11 +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-02-16 04:54:13 +08:00
db_mutex . lock ( ) ;
2018-04-28 04:20:38 +08:00
while ( mysql_query ( & dbconn , sql ) & & ! zm_terminate ) {
2018-04-11 04:06:36 +08:00
Error ( " Can't update event: %s reason: %s " , sql , mysql_error ( & dbconn ) ) ;
2018-04-12 22:22:46 +08:00
db_mutex . unlock ( ) ;
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-02-16 04:54:13 +08:00
db_mutex . unlock ( ) ;
2013-03-17 07:45:21 +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 ) {
2018-08-18 04:02:40 +08:00
if ( ! notes . empty ( ) ) {
notes . clear ( ) ;
for ( StringSetMap : : const_iterator mapIter = noteSetMap . begin ( ) ; mapIter ! = noteSetMap . end ( ) ; + + mapIter ) {
notes + = mapIter - > first ;
notes + = " : " ;
const StringSet & stringSet = mapIter - > second ;
for ( StringSet : : const_iterator setIter = stringSet . begin ( ) ; setIter ! = stringSet . end ( ) ; + + setIter ) {
if ( setIter ! = stringSet . begin ( ) )
notes + = " , " ;
notes + = * setIter ;
}
2013-03-17 07:45:21 +08:00
}
2018-08-18 04:02:40 +08:00
} else {
notes = " " ;
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 ;
2017-11-13 23:17:46 +08:00
} // end Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame )
2013-03-17 07:45:21 +08:00
2018-08-18 04:02:40 +08:00
bool Event : : WritePacket ( ZMPacket & packet ) {
2017-11-13 23:17:46 +08:00
2018-08-18 04:02:40 +08:00
if ( videoStore - > writePacket ( & packet ) < 0 )
2016-04-29 21:11:14 +08:00
return false ;
2017-11-14 15:39:58 +08:00
return true ;
2013-12-20 00:38:07 +08:00
}
2018-08-18 04:02:40 +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 ) {
2018-08-18 04:02:40 +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() );
2018-08-18 04:02:40 +08:00
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 ;
2018-08-18 04:02:40 +08:00
StringSet : : iterator noteSetIter = noteSet . find ( newNote ) ;
2016-06-22 01:48:32 +08:00
if ( noteSetIter = = noteSet . end ( ) ) {
2018-08-18 04:02:40 +08:00
noteSet . insert ( newNote ) ;
2016-04-04 22:11:48 +08:00
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 ;
2018-08-18 04:02:40 +08:00
createNotes ( notes ) ;
2013-03-17 07:45:21 +08:00
2018-08-18 04:02:40 +08:00
Debug ( 2 , " Updating notes for event %d, '%s' " , id , notes . c_str ( ) ) ;
2016-04-04 22:11:48 +08:00
static char sql [ ZM_SQL_MED_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 ) ) ;
}
2018-02-16 04:54:13 +08:00
} // end if ! 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-14 23:03:08 +08:00
mysql_real_escape_string ( & dbconn , escapedNotes , notes . c_str ( ) , notes . length ( ) ) ;
2013-03-17 07:45:21 +08:00
2018-05-06 00:25:11 +08:00
snprintf ( sql , sizeof ( sql ) , " UPDATE Events SET Notes = '%s' WHERE Id = % " PRIu64 , escapedNotes , id ) ;
2018-02-16 04:54:13 +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-02-16 04:54:13 +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
}
2018-04-14 23:03:08 +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
}
2018-04-14 23:03:08 +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-03-04 05:28:01 +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 ;
2017-10-10 02:58:07 +08:00
} else if ( timestamps [ i ] - > tv_sec < 0 ) {
Warning ( " Not adding pre-capture frame %d, negative timestamp " , i ) ;
continue ;
} else {
Debug ( 3 , " Adding pre-capture frame %d, timestamp = (%d), start_time=(%d) " , i , timestamps [ i ] - > tv_sec , start_time . tv_sec ) ;
2016-04-04 22:11:48 +08:00
}
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
2016-04-04 22:11:48 +08:00
static char event_file [ PATH_MAX ] ;
2018-08-11 22:08:30 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . capture_file_format , path . c_str ( ) , frames ) ;
2018-05-20 22:39:14 +08:00
if ( save_jpegs & 1 ) {
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-08-11 22:08:30 +08:00
std : : string snapshot_file = path + " /snapshot.jpg " ;
2018-05-06 00:57:50 +08:00
WriteFrameImage ( images [ i ] , * ( timestamps [ i ] ) , snapshot_file . c_str ( ) ) ;
2016-04-29 21:11:14 +08:00
}
2014-12-25 02:45:50 +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-08-12 06:49:30 +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-02-16 04:54:13 +08:00
db_mutex . lock ( ) ;
2018-08-12 06:49:30 +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-02-16 04:54:13 +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-08-12 06:49:30 +08:00
void Event : : AddPacket ( ZMPacket * packet , int score , Image * alarm_image ) {
2017-11-14 15:39:58 +08:00
2017-12-01 20:26:34 +08:00
have_video_keyframe = have_video_keyframe | | ( ( packet - > codec_type = = AVMEDIA_TYPE_VIDEO ) & & packet - > keyframe ) ;
2017-12-01 04:01:48 +08:00
if ( videoStore ) {
2017-12-01 03:37:36 +08:00
if ( have_video_keyframe ) {
2018-04-14 23:03:08 +08:00
videoStore - > writePacket ( packet ) ;
2017-12-01 03:37:36 +08:00
} else {
Debug ( 2 , " No video keyframe yet, not writing " ) ;
}
2017-11-14 15:39:58 +08:00
//FIXME if it fails, we should write a jpeg
}
2017-12-01 04:01:48 +08:00
if ( have_video_keyframe & & ( packet - > codec_type = = AVMEDIA_TYPE_VIDEO ) ) {
2018-04-14 23:03:08 +08:00
AddFrame ( packet - > image , * packet - > timestamp , score , alarm_image ) ;
2017-11-21 00:48:56 +08:00
} // end if is video
return ;
2017-11-14 15:39:58 +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
2016-04-04 22:11:48 +08:00
static char event_file [ PATH_MAX ] ;
2018-08-11 22:08:30 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . capture_file_format , path . c_str ( ) , frames ) ;
2013-03-17 07:45:21 +08:00
2018-05-20 22:39:14 +08:00
if ( save_jpegs & 1 ) {
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
2018-05-06 00:57:50 +08:00
// On the first frame, max_score will be zero, this effectively makes us write out a thumbnail
// for the first frame as well.
2018-05-22 23:31:03 +08:00
if ( frames = = 1 | | score > ( int ) max_score ) {
2018-08-11 22:08:30 +08:00
std : : string snapshot_file = path + " /snapshot.jpg " ;
WriteFrameImage ( image , timestamp , snapshot_file . c_str ( ) ) ;
2018-01-14 04:15:14 +08:00
}
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 ) {
2013-03-17 07:45:21 +08:00
2018-04-14 23:03:08 +08:00
Debug ( 1 , " Adding frame %d of type \" %s \" to DB " , frames , Event : : frame_type_names [ frame_type ] ) ;
2016-04-04 22:11:48 +08:00
static char sql [ ZM_SQL_MED_BUFSIZ ] ;
2018-04-28 04:20:38 +08:00
snprintf ( sql , sizeof ( sql ) ,
" INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) "
" VALUES ( % " PRIu64 " , %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d ) " ,
id , frames , frame_type_names [ frame_type ] , timestamp . tv_sec , delta_time . positive ? " " : " - " , delta_time . sec , delta_time . fsec , score ) ;
2018-02-16 04:54:13 +08:00
db_mutex . lock ( ) ;
2017-12-08 23:37:35 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2018-04-11 04:06:36 +08:00
Error ( " Can't insert frame: %s " , mysql_error ( & dbconn ) ) ;
Error ( " SQL was %s " , sql ) ;
2018-03-02 11:20:52 +08:00
db_mutex . unlock ( ) ;
return ;
2016-04-04 22:11:48 +08:00
}
2018-02-16 04:54:13 +08:00
db_mutex . unlock ( ) ;
2016-04-04 22:11:48 +08:00
last_db_frame = frames ;
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-02-16 04:54:13 +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-02-16 04:54:13 +08:00
db_mutex . unlock ( ) ;
2013-03-17 07:45:21 +08:00
}
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 ) {
2018-05-20 22:39:14 +08:00
if ( save_jpegs & 2 ) {
2018-08-11 22:08:30 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . analyse_file_format , path . c_str ( ) , frames ) ;
2018-08-03 03:40:33 +08:00
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
}
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}