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>
# 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 } } ;
2017-11-22 00:58:15 +08:00
Event : : Event ( Monitor * p_monitor , struct timeval p_start_time , const std : : string & p_cause , const StringSetMap & p_noteSetMap ) :
2016-04-04 22:11:48 +08:00
monitor ( p_monitor ) ,
start_time ( p_start_time ) ,
cause ( p_cause ) ,
2017-11-22 00:58:15 +08:00
noteSetMap ( p_noteSetMap )
2013-03-17 07:45:21 +08:00
{
2016-04-04 22:11:48 +08:00
std : : string notes ;
createNotes ( notes ) ;
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 ;
gettimeofday ( & start_time , 0 ) ;
}
2016-04-29 21:11:14 +08:00
Storage * storage = monitor - > getStorage ( ) ;
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
2017-01-15 05:55:28 +08:00
static char sql [ ZM_SQL_MED_BUFSIZ ] ;
2017-11-22 12:56:22 +08:00
snprintf ( sql , sizeof ( sql ) , " INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '', %d ) " ,
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-11-14 10:29:15 +08:00
monitor - > GetOptSaveJPEGs ( )
2017-01-15 06:07:20 +08:00
) ;
2016-06-22 01:48:32 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2016-04-29 21:11:14 +08:00
Error ( " Can't insert event: %s. sql was (%s) " , mysql_error ( & dbconn ) , sql ) ;
2016-04-04 22:11:48 +08:00
exit ( mysql_errno ( & dbconn ) ) ;
}
id = mysql_insert_id ( & dbconn ) ;
2016-06-22 01:48:32 +08:00
if ( untimedEvent ) {
2016-04-04 22:11:48 +08:00
Warning ( " Event %d has zero time, setting to current " , id ) ;
}
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
2016-09-27 21:47:19 +08:00
struct stat statbuf ;
2017-05-20 02:52:45 +08:00
char id_file [ PATH_MAX ] ;
2017-11-28 04:43:16 +08:00
struct tm * stime = localtime ( & start_time . tv_sec ) ;
2016-09-27 21:47:19 +08:00
2016-06-22 01:48:32 +08:00
if ( config . use_deep_storage ) {
2016-04-04 22:11:48 +08:00
char * path_ptr = path ;
2016-04-29 21:11:14 +08:00
path_ptr + = snprintf ( path_ptr , sizeof ( path ) , " %s/%d " , storage - > Path ( ) , monitor - > Id ( ) ) ;
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 + + ) {
2016-04-04 22:11:48 +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 ;
2016-09-27 21:47:19 +08:00
// Do we really need to stat it? Perhaps we could do that on error, instead
2016-04-04 22:11:48 +08:00
if ( stat ( path , & statbuf ) ) {
2016-06-22 01:48:32 +08:00
if ( errno = = ENOENT | | errno = = ENOTDIR ) {
if ( mkdir ( path , 0755 ) ) {
2016-09-27 21:47:19 +08:00
// FIXME This should not be fatal. Should probably move to a different storage area.
2016-04-04 22:11:48 +08:00
Fatal ( " Can't mkdir %s: %s " , path , strerror ( errno ) ) ;
}
} else {
Warning ( " Error stat'ing %s, may be fatal. error is %s " , path , strerror ( errno ) ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
}
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 ] ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
// Create event id symlink
snprintf ( id_file , sizeof ( id_file ) , " %s/.%d " , date_path , id ) ;
if ( symlink ( time_path , id_file ) < 0 )
2017-09-26 04:22:41 +08:00
Error ( " Can't symlink %s -> %s: %s " , id_file , path , strerror ( errno ) ) ;
2016-06-22 01:48:32 +08:00
} else {
2017-10-26 04:25:44 +08:00
// Shallow Storage
2016-04-29 21:11:14 +08:00
snprintf ( path , sizeof ( path ) , " %s/%d/%d " , storage - > Path ( ) , monitor - > Id ( ) , id ) ;
2016-04-04 22:11:48 +08:00
errno = 0 ;
stat ( path , & statbuf ) ;
2016-06-22 01:48:32 +08:00
if ( errno = = ENOENT | | errno = = ENOTDIR ) {
if ( mkdir ( path , 0755 ) ) {
2016-04-04 22:11:48 +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
snprintf ( id_file , sizeof ( id_file ) , " %s/.%d " , path , id ) ;
if ( FILE * id_fp = fopen ( id_file , " w " ) )
fclose ( id_fp ) ;
else
Error ( " Can't fopen %s: %s " , id_file , strerror ( errno ) ) ;
} // 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
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 ) {
2017-11-14 14:59:15 +08:00
std : : string container = monitor - > OutputContainer ( ) ;
if ( container = = " auto " | | container = = " " ) {
if ( monitor - > OutputCodec ( ) = = " h264 " ) {
container = " mp4 " ;
} else {
container = " mkv " ;
2016-04-29 21:11:14 +08:00
}
}
2017-11-14 14:59:15 +08:00
snprintf ( video_name , sizeof ( video_name ) , " %d-%s.%s " , id , " video " , container . c_str ( ) ) ;
2017-10-19 08:46:26 +08:00
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 ) ;
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 ;
}
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
static char sql [ ZM_SQL_MED_BUFSIZ ] ;
struct DeltaTimeval delta_time ;
DELTA_TIMEVAL ( delta_time , end_time , start_time , DT_PREC_2 ) ;
2016-06-22 01:48:32 +08:00
if ( frames > last_db_frame ) {
2016-04-04 22:11:48 +08:00
Debug ( 1 , " Adding closing frame %d to DB " , frames ) ;
2017-11-22 12:56:22 +08:00
snprintf ( sql , sizeof ( sql ) ,
" insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld ) " ,
id , frames , end_time . tv_sec , delta_time . positive ? " " : " - " , delta_time . sec , delta_time . fsec ) ;
2016-06-22 01:48:32 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2016-04-04 22:11:48 +08:00
Error ( " Can't insert frame: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
}
2016-04-29 21:11:14 +08:00
/* Close the video file */
2017-11-13 23:17:46 +08:00
if ( videoStore ) {
delete videoStore ;
videoStore = NULL ;
2016-04-29 21:11:14 +08:00
}
2013-12-20 00:38:07 +08:00
2017-10-26 04:06:15 +08:00
snprintf ( sql , sizeof ( sql ) , " UPDATE Events SET Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d " , 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 ) ;
2017-05-17 00:04:56 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2016-04-04 22:11:48 +08:00
Error ( " Can't update event: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
2017-10-28 11:36:49 +08:00
} // ~Event
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +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
}
int Event : : sd = - 1 ;
2016-06-22 01:48:32 +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 ;
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 ) ;
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
delete ( ts_image ) ;
} else {
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
}
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
2017-11-13 23:17:46 +08:00
bool Event : : WritePacket ( ZMPacket & packet ) {
2017-11-14 15:39:58 +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
}
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 ( ) ) ;
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 ) ) ;
}
}
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
2016-04-04 22:11:48 +08:00
mysql_real_escape_string ( & dbconn , escapedNotes , notes . c_str ( ) , notes . length ( ) ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
snprintf ( sql , sizeof ( sql ) , " update Events set Notes = '%s' where Id = %d " , escapedNotes , id ) ;
2016-06-22 01:48:32 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2016-04-04 22:11:48 +08:00
Error ( " Can't insert event: %s " , mysql_error ( & dbconn ) ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
# endif
}
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 ] ;
strncpy ( sql , " insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values " , sizeof ( sql ) ) ;
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 ) {
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 ] ;
2017-10-19 08:46:26 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . capture_file_format , path , frames ) ;
2017-10-08 21:13:56 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 4 ) {
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 ) {
2017-11-21 00:48:56 +08:00
std : : string snapshot_file = std : : string ( path ) + " /snapshot.jpg " ;
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
}
2017-10-08 21:13:56 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 1 ) {
2016-04-29 21:11:14 +08:00
Debug ( 1 , " Writing pre-capture frame %d " , frames ) ;
WriteFrameImage ( images [ i ] , * ( timestamps [ i ] ) , event_file ) ;
2013-09-28 19:17:22 +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 ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
int sql_len = strlen ( sql ) ;
snprintf ( sql + sql_len , sizeof ( sql ) - sql_len , " ( %d, %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 + + ;
}
2013-03-17 07:45:21 +08:00
2016-06-22 01:48:32 +08:00
if ( frameCount ) {
2016-04-04 22:11:48 +08:00
Debug ( 1 , " Adding %d/%d frames to DB " , frameCount , n_frames ) ;
* ( sql + strlen ( sql ) - 2 ) = ' \0 ' ;
2016-06-22 01:48:32 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2017-10-08 21:13:56 +08:00
Error ( " Can't insert frames: %s, sql was (%s) " , mysql_error ( & dbconn ) , sql ) ;
2016-04-04 22:11:48 +08:00
exit ( mysql_errno ( & dbconn ) ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
last_db_frame = frames ;
2016-06-22 01:48:32 +08:00
} else {
2016-04-04 22:11:48 +08:00
Debug ( 1 , " No valid pre-capture frames to add " ) ;
}
2013-03-17 07:45:21 +08:00
}
2017-11-14 15:39:58 +08:00
void Event : : AddPacket ( ZMPacket * packet , int score , Image * alarm_image ) {
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 ) {
2017-11-22 00:58:15 +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 23:33:10 +08:00
} else {
Debug ( 2 , " AddPacket but no videostore?! " ) ;
2017-11-14 15:39:58 +08:00
}
2017-12-01 04:01:48 +08:00
if ( have_video_keyframe & & ( packet - > codec_type = = AVMEDIA_TYPE_VIDEO ) ) {
2017-11-22 08:55:40 +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
}
2016-06-22 01:48:32 +08:00
void Event : : AddFrame ( Image * image , struct timeval timestamp , int score , Image * alarm_image ) {
if ( ! timestamp . tv_sec ) {
2016-04-04 22:11:48 +08:00
Debug ( 1 , " Not adding new frame, zero timestamp " ) ;
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 ] ;
2017-10-19 08:46:26 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . capture_file_format , path , frames ) ;
2013-03-17 07:45:21 +08:00
2017-09-26 04:22:41 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 4 ) {
2017-10-19 08:46:26 +08:00
// Only snapshots
2016-04-29 21:11:14 +08:00
//If this is the first frame, we should add a thumbnail to the event directory
2017-11-21 00:48:56 +08:00
if ( frames = = 10 | | frames = = 1 ) {
std : : string snapshot_file = std : : string ( path ) + " /snapshot.jpg " ;
WriteFrameImage ( image , timestamp , snapshot_file . c_str ( ) ) ;
2013-03-17 07:45:21 +08:00
}
2016-04-29 21:11:14 +08:00
}
2017-09-26 04:22:41 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 1 ) {
Debug ( 1 , " Writing capture frame %d to %s " , frames , event_file ) ;
if ( ! WriteFrameImage ( image , timestamp , event_file ) ) {
Error ( " Failed to write frame image " ) ;
}
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 ;
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
2017-01-09 05:53:29 +08:00
bool db_frame = ( frame_type ! = BULK ) | | ( ( frames % config . bulk_frame_interval ) = = 0 ) | | ! frames ;
2016-06-22 01:48:32 +08:00
if ( db_frame ) {
2013-03-17 07:45:21 +08:00
2017-04-10 05:36:24 +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 ] ;
2017-12-08 23:37:35 +08:00
snprintf ( sql , sizeof ( sql ) , " INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) VALUES ( %d, %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 ) ;
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't insert frame: (%s) reason:%s " , sql , mysql_error ( & dbconn ) ) ;
zmDbClose ( ) ;
if ( ! zmDbConnect ( ) )
exit ( mysql_errno ( & dbconn ) ) ;
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't insert frame: (%s) reason:%s " , sql , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
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 ) {
2017-05-20 02:43:49 +08:00
snprintf ( sql , sizeof ( sql ) , " update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d " ,
( 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
) ;
2016-06-22 01:48:32 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2016-04-04 22:11:48 +08:00
Error ( " Can't update event: %s " , mysql_error ( & dbconn ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
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 ) {
2017-10-19 08:46:26 +08:00
snprintf ( event_file , sizeof ( event_file ) , staticConfig . analyse_file_format , path , frames ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
Debug ( 1 , " Writing analysis frame %d " , frames ) ;
2017-09-26 04:22:41 +08:00
if ( monitor - > GetOptSaveJPEGs ( ) & 2 ) {
WriteFrameImage ( alarm_image , timestamp , event_file , true ) ;
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
}