zoneminder/src/zm_event.cpp

577 lines
21 KiB
C++
Raw Normal View History

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
// 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
const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" };
2013-03-17 07:45:21 +08:00
int Event::pre_alarm_count = 0;
2013-03-17 07:45:21 +08:00
Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } };
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
) :
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
{
std::string notes;
createNotes(notes);
struct timeval now;
gettimeofday(&now, 0);
bool untimedEvent = false;
if ( !start_time.tv_sec ) {
untimedEvent = true;
start_time = now;
} else if ( start_time.tv_sec > now.tv_sec ) {
Error("StartTime in the future");
start_time = now;
}
2016-04-29 21:11:14 +08:00
Storage * storage = monitor->getStorage();
unsigned int state_id = 0;
zmDbRow dbrow;
2017-10-11 00:58:06 +08:00
if ( dbrow.fetch("SELECT Id FROM States WHERE IsActive=1") ) {
state_id = atoi(dbrow[0]);
}
2016-04-29 21:11:14 +08:00
static char sql[ZM_SQL_MED_BUFSIZ];
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' )",
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 ),
monitor->GetOptSaveJPEGs(),
2017-12-19 02:25:24 +08:00
storage->SchemeString().c_str()
);
2018-02-16 04:54:13 +08:00
db_mutex.lock();
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 );
exit( mysql_errno( &dbconn ) );
}
id = mysql_insert_id( &dbconn );
2018-02-16 04:54:13 +08:00
db_mutex.unlock();
if ( untimedEvent ) {
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;
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
2017-12-20 00:01:03 +08:00
if ( storage->Scheme() == Storage::DEEP ) {
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() );
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;
for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) {
path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] );
2013-03-17 07:45:21 +08:00
errno = 0;
if ( mkdir( path, 0755 ) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST ) {
Error( "Can't mkdir %s: %s", path, strerror(errno));
2013-03-17 07:45:21 +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
}
// Create event id symlink
snprintf( id_file, sizeof(id_file), "%s/.%d", 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 ) {
char *path_ptr = path;
2017-12-20 00:01:03 +08:00
path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d/%04d-%02d-%02d",
storage->Path(), monitor->Id(), stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
);
if ( mkdir( path, 0755 ) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST )
Error( "Can't mkdir %s: %s", path, strerror(errno));
}
path_ptr += snprintf( path_ptr, sizeof(path), "/%d", id );
if ( mkdir( path, 0755 ) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST )
Error( "Can't mkdir %s: %s", path, strerror(errno));
}
} 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 );
if ( mkdir( path, 0755 ) ) {
if ( errno != EEXIST ) {
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
// 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
last_db_frame = 0;
2013-03-17 07:45:21 +08:00
2016-04-29 21:11:14 +08:00
video_name[0] = 0;
2016-04-29 21:11:14 +08:00
/* Save as video */
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() == 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
snprintf( video_name, sizeof(video_name), "%d-%s.%s", id, "video", container.c_str() );
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,
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
}
} // 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
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);
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
if ( frames > last_db_frame ) {
Debug( 1, "Adding closing frame %d to DB", frames );
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 );
2018-02-16 04:54:13 +08:00
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %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-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
}
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 );
2018-02-16 04:54:13 +08:00
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) );
}
2018-02-16 04:54:13 +08:00
db_mutex.unlock();
2017-10-28 11:36:49 +08:00
} // ~Event
2013-03-17 07:45:21 +08:00
void Event::createNotes( std::string &notes ) {
notes.clear();
2017-11-17 20:52:26 +08:00
for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); ++mapIter ) {
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 ) {
if ( setIter != stringSet.begin() )
notes += ", ";
notes += *setIter;
2013-03-17 07:45:21 +08:00
}
}
2013-03-17 07:45:21 +08:00
}
int Event::sd = -1;
bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) {
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
bool rc;
Debug(3, "Writing image to %s", event_file );
2017-01-17 01:56:09 +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 ) {
if ( videoStore->writePacket( &packet ) < 0 )
2016-04-29 21:11:14 +08:00
return false;
return true;
}
void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
bool update = false;
2013-03-17 07:45:21 +08:00
//Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() );
if ( newNoteSetMap.size() > 0 ) {
if ( noteSetMap.size() == 0 ) {
noteSetMap = newNoteSetMap;
update = true;
} else {
2017-11-17 20:52:26 +08:00
for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); ++newNoteSetMapIter ) {
const std::string &newNoteGroup = newNoteSetMapIter->first;
const StringSet &newNoteSet = newNoteSetMapIter->second;
//Info( "Got %d new strings", newNoteSet.size() );
if ( newNoteSet.size() > 0 ) {
StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup );
if ( noteSetMapIter == noteSetMap.end() ) {
//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;
} else {
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 ) {
const std::string &newNote = *newNoteSetIter;
StringSet::iterator noteSetIter = noteSet.find( newNote );
if ( noteSetIter == noteSet.end() ) {
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
if ( update ) {
std::string notes;
createNotes( notes );
2013-03-17 07:45:21 +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
static MYSQL_STMT *stmt = 0;
2013-03-17 07:45:21 +08:00
char notesStr[ZM_SQL_MED_BUFSIZ] = "";
unsigned long notesLen = 0;
2013-03-17 07:45:21 +08:00
if ( !stmt ) {
const char *sql = "update Events set Notes = ? where Id = ?";
stmt = mysql_stmt_init( &dbconn );
if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) {
Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) );
}
/* Get the parameter count from the statement */
if ( mysql_stmt_param_count( stmt ) != 2 ) {
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 */
if ( mysql_stmt_bind_param( stmt, bind ) ) {
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
strncpy( notesStr, notes.c_str(), sizeof(notesStr) );
2013-03-17 07:45:21 +08:00
if ( mysql_stmt_execute( stmt ) ) {
Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) );
}
2013-03-17 07:45:21 +08:00
#else
static char escapedNotes[ZM_SQL_MED_BUFSIZ];
2013-03-17 07:45:21 +08:00
mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() );
2013-03-17 07:45:21 +08:00
2018-02-16 04:54:13 +08:00
db_mutex.lock();
snprintf( sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %d", escapedNotes, id );
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();
#endif
}
2013-03-17 07:45:21 +08:00
}
void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) {
for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) {
AddFramesInternal(n_frames, i, images, timestamps);
}
}
void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) {
static char sql[ZM_SQL_LGE_BUFSIZ];
strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) );
int frameCount = 0;
for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) {
if ( timestamps[i]->tv_sec <= 0 ) {
Debug( 1, "Not adding pre-capture frame %d, zero or less than 0 timestamp", i );
continue;
} 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 );
}
2013-03-17 07:45:21 +08:00
frames++;
2013-03-17 07:45:21 +08:00
static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
if ( monitor->GetOptSaveJPEGs() & 1 ) {
Debug( 1, "Writing pre-capture frame %d", frames );
WriteFrameImage( images[i], *(timestamps[i]), event_file );
} 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 ) {
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
}
}
2013-03-17 07:45:21 +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 ) {
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
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
frameCount++;
}
2013-03-17 07:45:21 +08:00
if ( frameCount ) {
Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames );
*(sql+strlen(sql)-2) = '\0';
2018-02-16 04:54:13 +08:00
db_mutex.lock();
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 );
2013-03-17 07:45:21 +08:00
}
2018-02-16 04:54:13 +08:00
db_mutex.unlock();
last_db_frame = frames;
} else {
Debug( 1, "No valid pre-capture frames to add" );
}
2013-03-17 07:45:21 +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 ) {
2018-02-23 00:43:04 +08:00
Debug(2,"Have video keyframe, writing packet to videostore");
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");
}
//FIXME if it fails, we should write a jpeg
} else {
Debug(2,"AddPacket but no videostore?!");
}
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;
}
void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) {
if ( !timestamp.tv_sec ) {
Debug( 1, "Not adding new frame, zero timestamp" );
return;
}
2013-03-17 07:45:21 +08:00
frames++;
2013-03-17 07:45:21 +08:00
static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
2013-03-17 07:45:21 +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");
}
} else {
//If this is the first frame, we should add a thumbnail to the event directory
if ( frames == 10 || frames == 1 ) {
std::string snapshot_file = std::string(path) + "/snapshot.jpg";
WriteFrameImage( image, timestamp, snapshot_file.c_str() );
}
2016-04-29 21:11:14 +08:00
}
2013-03-17 07:45:21 +08:00
struct DeltaTimeval delta_time;
DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 );
2013-03-17 07:45:21 +08:00
FrameType frame_type = score>0?ALARM:(score<0?BULK:NORMAL);
// < 0 means no motion detection is being done.
if ( score < 0 )
score = 0;
2013-03-17 07:45:21 +08:00
bool db_frame = ( frame_type != BULK ) || ((frames%config.bulk_frame_interval)==0) || !frames;
if ( db_frame ) {
2013-03-17 07:45:21 +08:00
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
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 );
2018-02-16 04:54:13 +08:00
db_mutex.lock();
2017-12-08 23:37:35 +08:00
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert frame: (%s) reason:%s", sql, mysql_error(&dbconn));
zmDbClose();
2018-02-16 04:54:13 +08:00
if ( ! zmDbConnect() ) {
Error("Unable to connect to db");
2017-12-08 23:37:35 +08:00
exit(mysql_errno(&dbconn));
2018-02-16 04:54:13 +08:00
}
2017-12-08 23:37:35 +08:00
if ( mysql_query(&dbconn, sql) ) {
2018-02-16 04:54:13 +08:00
Error("REALLY Can't insert frame: (%s) reason:%s", sql, mysql_error(&dbconn));
2017-12-08 23:37:35 +08:00
exit(mysql_errno(&dbconn));
}
}
2018-02-16 04:54:13 +08:00
db_mutex.unlock();
last_db_frame = frames;
2013-03-17 07:45:21 +08:00
// We are writing a Bulk frame
if ( frame_type == BULK ) {
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
);
2018-02-16 04:54:13 +08:00
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) );
}
2018-02-16 04:54:13 +08:00
db_mutex.unlock();
2013-03-17 07:45:21 +08:00
}
} // end if db_frame
2013-03-17 07:45:21 +08:00
end_time = timestamp;
2013-03-17 07:45:21 +08:00
// We are writing an Alarm frame
if ( frame_type == ALARM ) {
alarm_frames++;
2013-03-17 07:45:21 +08:00
tot_score += score;
if ( score > (int)max_score )
max_score = score;
2013-03-17 07:45:21 +08:00
if ( alarm_image ) {
snprintf( event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames );
2013-03-17 07:45:21 +08:00
Debug( 1, "Writing analysis frame %d", frames );
if ( monitor->GetOptSaveJPEGs() & 2 ) {
WriteFrameImage(alarm_image, timestamp, event_file, true);
}
2013-03-17 07:45:21 +08:00
}
}
2013-03-17 07:45:21 +08:00
}