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);
|
2019-10-31 04:25:09 +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
|
|
|
|
2019-08-28 22:17:23 +08:00
|
|
|
#if 0 // This closing frame has no image. There is no point in adding a db record for it, I think. ICON
|
2018-03-03 10:25:20 +08:00
|
|
|
if ( frames > last_db_frame ) {
|
2019-06-07 01:49:01 +08:00
|
|
|
frames ++;
|
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-08-28 22:17:23 +08:00
|
|
|
#endif
|
2019-02-20 01:00:17 +08:00
|
|
|
if ( frame_data.size() )
|
|
|
|
WriteDbFrames();
|
2018-03-03 10:25:20 +08:00
|
|
|
|
2019-11-02 05:30:57 +08:00
|
|
|
// update frame deltas to refer to video start time which may be a few frames before event start
|
|
|
|
struct timeval video_offset = {0};
|
|
|
|
struct timeval video_start_time = monitor->GetVideoWriterStartTime();
|
|
|
|
if (video_start_time.tv_sec > 0) {
|
|
|
|
timersub(&video_start_time, &start_time, &video_offset);
|
|
|
|
Debug(1, "Updating frames delta by %d sec %d usec",
|
|
|
|
video_offset.tv_sec, video_offset.tv_usec);
|
|
|
|
UpdateFramesDelta(video_offset.tv_sec + video_offset.tv_usec*1e-6);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Debug(3, "Video start_time %d sec %d usec not valid -- frame deltas not updated",
|
|
|
|
video_start_time.tv_sec, video_start_time.tv_usec);
|
|
|
|
}
|
|
|
|
|
2019-02-20 01:00:17 +08:00
|
|
|
// Should not be static because we might be multi-threaded
|
2019-04-05 00:55:35 +08:00
|
|
|
char sql[ZM_SQL_LGE_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
|
|
|
|
2019-10-31 04:25:09 +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 ¬es) {
|
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
|
|
|
}
|
2019-10-31 04:25:09 +08:00
|
|
|
} // void Event::createNotes(std::string ¬es)
|
|
|
|
|
|
|
|
bool Event::WriteFrameImage(
|
|
|
|
Image *image,
|
|
|
|
struct timeval timestamp,
|
|
|
|
const char *event_file,
|
|
|
|
bool alarm_frame) {
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-10-31 04:25: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
|
2016-04-04 22:11:48 +08:00
|
|
|
|
2017-09-26 04:22:41 +08:00
|
|
|
bool rc;
|
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.
|
2019-10-31 04:25:09 +08:00
|
|
|
// exif is only timestamp at present this switches on or off for write
|
2017-09-26 04:22:41 +08:00
|
|
|
Image *ts_image = new Image(*image);
|
2018-05-04 01:54:05 +08:00
|
|
|
monitor->TimestampImage(ts_image, ×tamp);
|
2019-10-31 04:25:09 +08:00
|
|
|
rc = ts_image->WriteJpeg(event_file, thisquality,
|
|
|
|
(monitor->Exif() ? timestamp : (timeval){0,0}));
|
2017-09-26 04:22:41 +08:00
|
|
|
delete(ts_image);
|
|
|
|
} else {
|
2019-10-31 04:25:09 +08:00
|
|
|
rc = image->WriteJpeg(event_file, thisquality,
|
|
|
|
(monitor->Exif() ? timestamp : (timeval){0,0}));
|
2017-09-26 04:22:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
|
|
|
|
2019-10-31 04:25:09 +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;
|
2019-10-31 04:25:09 +08:00
|
|
|
monitor->TimestampImage(&ts_image, ×tamp);
|
2016-04-29 21:11:14 +08:00
|
|
|
frameimg = &ts_image;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate delta time */
|
|
|
|
struct DeltaTimeval delta_time3;
|
2019-10-31 04:25:09 +08:00
|
|
|
DELTA_TIMEVAL(delta_time3, timestamp, start_time, DT_PREC_3);
|
2016-04-29 21:11:14 +08:00
|
|
|
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;
|
2019-10-31 04:25:09 +08:00
|
|
|
} // bool Event::WriteFrameVideo
|
2013-12-20 00:38:07 +08:00
|
|
|
|
2019-10-31 04:25:09 +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 {
|
2019-10-31 04:25:09 +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 ) {
|
2019-10-31 04:25:09 +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() );
|
2019-10-31 04:25:09 +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 ¬eSet = noteSetMapIter->second;
|
|
|
|
//Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() );
|
2019-10-31 04:25:09 +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;
|
2019-10-31 04:25:09 +08:00
|
|
|
StringSet::iterator noteSetIter = noteSet.find(newNote);
|
2016-06-22 01:48:32 +08:00
|
|
|
if ( noteSetIter == noteSet.end() ) {
|
2019-10-31 04:25:09 +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;
|
2019-10-31 04:25:09 +08:00
|
|
|
createNotes(notes);
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-10-31 04:25:09 +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 ) {
|
2020-02-26 03:16:30 +08:00
|
|
|
const char *sql = "UPDATE `Events` SET `Notes` = CONCAT(`Notes`,?) WHERE `Id` = ?";
|
2016-04-04 22:11:48 +08:00
|
|
|
|
2019-10-31 04:25:09 +08:00
|
|
|
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));
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the parameter count from the statement */
|
2019-10-31 04:25:09 +08:00
|
|
|
if ( mysql_stmt_param_count(stmt) != 2 ) {
|
|
|
|
Error("Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count(stmt), sql);
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 = ¬esLen;
|
|
|
|
|
|
|
|
bind[1].buffer_type= MYSQL_TYPE_LONG;
|
|
|
|
bind[1].buffer= (char *)&id;
|
|
|
|
bind[1].is_null= 0;
|
|
|
|
bind[1].length= 0;
|
|
|
|
|
|
|
|
/* Bind the buffers */
|
2019-10-31 04:25:09 +08:00
|
|
|
if ( mysql_stmt_bind_param(stmt, bind) ) {
|
|
|
|
Error("Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt));
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
|
|
|
}
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-10-31 04:25:09 +08:00
|
|
|
strncpy(notesStr, notes.c_str(), sizeof(notesStr));
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-10-31 04:25:09 +08:00
|
|
|
if ( mysql_stmt_execute(stmt) ) {
|
|
|
|
Error("Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt));
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
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
|
|
|
|
2020-02-26 03:16:30 +08:00
|
|
|
snprintf(sql, sizeof(sql), "UPDATE `Events` SET `Notes` = CONCAT(`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
|
2019-10-31 04:25:09 +08:00
|
|
|
} // end if update
|
|
|
|
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-10-31 04:25:09 +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
|
|
|
}
|
|
|
|
|
2019-10-31 04:25:09 +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];
|
2019-10-31 04:25:09 +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);
|
2014-12-25 02:45:50 +08:00
|
|
|
}
|
2019-10-31 04:25:09 +08:00
|
|
|
//If this is the first frame, we should add a thumbnail to the event directory
|
|
|
|
// ICON: We are working through the pre-event frames so this snapshot won't
|
|
|
|
// neccessarily be of the motion. But some events are less than 10 frames,
|
|
|
|
// so I am changing this to 1, but we should overwrite it later with a better snapshot.
|
|
|
|
if ( frames == 1 ) {
|
|
|
|
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file);
|
|
|
|
}
|
|
|
|
|
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;
|
2019-10-31 04:25:09 +08:00
|
|
|
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",
|
2019-10-31 04:25:09 +08:00
|
|
|
timestamps[i]->tv_sec,
|
|
|
|
(delta_time.positive?"":"-"),
|
|
|
|
delta_time.sec,
|
|
|
|
delta_time.fsec);
|
2018-01-27 01:21:12 +08:00
|
|
|
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);
|
2019-06-07 01:49:01 +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();
|
2019-06-07 01:49:01 +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
|
|
|
}
|
2019-10-31 04:25:09 +08:00
|
|
|
} // void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, struct timeval **timestamps)
|
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
|
|
|
|
2019-11-02 05:30:57 +08:00
|
|
|
// Subtract an offset time from frames deltas to match with video start time
|
|
|
|
void Event::UpdateFramesDelta(double offset) {
|
|
|
|
char sql[ZM_SQL_MED_BUFSIZ];
|
|
|
|
|
|
|
|
if (offset == 0.0) return;
|
|
|
|
// the table is set to auto update timestamp so we force it to keep current value
|
|
|
|
snprintf(sql, sizeof(sql),
|
|
|
|
"UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64,
|
|
|
|
offset, id);
|
|
|
|
|
|
|
|
db_mutex.lock();
|
|
|
|
if (mysql_query(&dbconn, sql)) {
|
|
|
|
db_mutex.unlock();
|
|
|
|
Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
db_mutex.unlock();
|
|
|
|
Info("Updating frames delta by %0.2f sec to match video file", offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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++;
|
2019-04-07 22:29:02 +08:00
|
|
|
bool write_to_db = false;
|
2019-10-31 04:25:09 +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
|
|
|
|
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);
|
2019-09-17 22:30:00 +08:00
|
|
|
if ( !WriteFrameImage(image, timestamp, event_file) ) {
|
2017-09-26 04:22:41 +08:00
|
|
|
Error("Failed to write frame image");
|
|
|
|
}
|
2019-10-31 04:25:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If this is the first frame, we should add a thumbnail to the event directory
|
|
|
|
if ( (frames == 1) || (score > (int)max_score) ) {
|
|
|
|
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
|
|
|
|
WriteFrameImage(image, timestamp, snapshot_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We are writing an Alarm frame
|
|
|
|
if ( frame_type == ALARM ) {
|
2018-11-13 01:43:20 +08:00
|
|
|
// The first frame with a score will be the frame that alarmed the event
|
2019-10-31 04:25:09 +08:00
|
|
|
if ( !alarm_frame_written ) {
|
2019-04-07 22:29:02 +08:00
|
|
|
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
2018-11-13 01:43:20 +08:00
|
|
|
alarm_frame_written = true;
|
|
|
|
WriteFrameImage(image, timestamp, alarm_file);
|
|
|
|
}
|
2019-10-31 04:25:09 +08:00
|
|
|
alarm_frames++;
|
|
|
|
|
|
|
|
tot_score += score;
|
|
|
|
if ( score > (int)max_score )
|
|
|
|
max_score = score;
|
|
|
|
|
|
|
|
if ( alarm_image ) {
|
|
|
|
if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
|
|
|
static char event_file[PATH_MAX];
|
|
|
|
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
|
|
|
|
Debug(1, "Writing analysis frame %d", frames);
|
|
|
|
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
|
|
|
|
Error("Failed to write analysis frame image");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // end if frame_type == ALARM
|
|
|
|
|
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
|
|
|
|
2019-06-11 00:57:57 +08:00
|
|
|
bool db_frame = ( frame_type != BULK ) || (frames==1) || ((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));
|
2019-04-07 22:29:02 +08:00
|
|
|
if ( write_to_db || ( frame_data.size() > 20 ) ) {
|
2019-06-11 01:59:01 +08:00
|
|
|
Debug(1, "Adding %d frames to DB", frame_data.size());
|
2019-06-11 02:13:21 +08:00
|
|
|
WriteDbFrames();
|
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),
|
2019-06-11 01:59:01 +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;
|
2019-10-31 04:25:09 +08:00
|
|
|
} // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image)
|