// // ZoneMinder Core Interfaces, $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. // #ifndef ZM_EVENT_H #define ZM_EVENT_H #include "zm_config.h" #include "zm_define.h" #include "zm_packet.h" #include "zm_packetqueue.h" #include "zm_storage.h" #include "zm_time.h" #include "zm_utils.h" #include "zm_zone.h" #include #include #include #include #include #include #include #include class EventStream; class Frame; class Image; class Monitor; class Tag; class VideoStore; class ZMPacket; class Zone; // True when a failed link(2) means renaming instead is worth trying, rather // than something being wrong that a rename would hit too. EXDEV is deliberately // excluded: a cross-device link is one rename(2) cannot do either. // // EOPNOTSUPP (and its alias ENOTSUP) and ENOSYS mean the filesystem has no hard // links at all, on both platforms. // // EPERM only means that on Linux, where the VFS returns it for a filesystem // with no link operation, which is the exFAT and FAT case. Linux overloads it // with fs.protected_hardlinks, immutable sources and idmapped mounts, so this // is a broader "try the rename" than the errno alone proves; rename is the // right attempt in all of those, and where it genuinely cannot proceed it fails // and gets reported. FreeBSD is excluded because there EPERM only ever means a // directory source, an immutable source or an immutable target directory, all // real errors, and absent link support arrives as EOPNOTSUPP above. // See https://github.com/ZoneMinder/zoneminder/issues/5048 inline bool ErrnoMeansNoHardLinkSupport(int err) { if ((err == EOPNOTSUPP) || (err == ENOTSUP) || (err == ENOSYS)) return true; #ifndef __FreeBSD__ if (err == EPERM) return true; #endif return false; } // Event::max_score starts at -1 so that the first frame, even with score 0, // becomes the max-score frame. Events.MaxScore is unsigned: an event closed // before its first frame has to store 0, or strict SQL mode rejects the // whole closing UPDATE and the event stays open in the database. inline int EventMaxScoreForDb(int max_score) { return max_score < 0 ? 0 : max_score; } // Maximum number of prealarm frames that can be stored #define MAX_PRE_ALARM_FRAMES 16 typedef uint64_t event_id_t; // // Class describing events, i.e. captured periods of activity. // class Event { friend class EventStream; public: typedef std::set StringSet; typedef std::map StringSetMap; protected: static const char * frame_type_names[3]; struct PreAlarmData { Image *image; struct timeval timestamp; unsigned int score; Image *alarm_frame; }; std::queue frame_data; static int pre_alarm_count; static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; uint64_t id; Monitor *monitor; Storage *storage; PacketQueue * packetqueue; packetqueue_iterator * packetqueue_it; SystemTimePoint start_time; // Written by the event thread (AddPacket_), read by Analyse through // Duration()/Frames()/AlarmFrames(): atomic. std::atomic end_time; std::string cause; StringSetMap noteSetMap; std::atomic frames; std::atomic alarm_frames; bool alarm_frame_written; int tot_score; int max_score; int max_score_frame_id; std::string path; std::string snapshot_file; bool snapshot_file_written; std::string alarm_file; VideoStore *videoStore; std::string container; std::string codec; std::string video_file; std::string video_path; std::string video_incomplete_file; std::string video_incomplete_path; int last_db_frame; bool have_video_keyframe; // a flag to tell us if we have had a video keyframe when writing an mp4. The first frame SHOULD be a video keyframe. Storage::Schemes scheme; int save_jpegs; void createNotes(std::string ¬es); void Run(); std::atomic terminate_; std::thread thread_; std::map tags; public: static bool OpenFrameSocket(int); static bool ValidateFrameSocket(int); Event(Monitor *p_monitor, packetqueue_iterator * p_packetqueue_it, SystemTimePoint p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap); ~Event(); uint64_t Id() const { return id; } const std::string &Cause() const { return cause; } void addNote(const char *cause, const std::string ¬e); int Frames() const { return frames; } int AlarmFrames() const { return alarm_frames; } SystemTimePoint StartTime() const { return start_time; } SystemTimePoint EndTime() const { return end_time.load(); } TimePoint::duration Duration() const { return end_time.load() - start_time; }; void AddPacket_(const std::shared_ptr p); bool WritePacket(const std::shared_ptr p); bool SendFrameImage(const Image *image, bool alarm_frame=false); bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const; void updateNotes(const StringSetMap &stringSetMap); void AddFrame(const std::shared_ptr&packet); void Stop() { terminate_ = true; packetqueue->notify_all(); } bool Stopped() const { return terminate_; } private: void WriteDbFrames(); bool SetPath(Storage *storage); public: static std::string getSubPath(tm time) { std::string subpath = stringtf("%02d/%02d/%02d/%02d/%02d/%02d", time.tm_year - 100, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); return subpath; } static std::string getSubPath(time_t *time) { tm time_tm = {}; localtime_r(time, &time_tm); return Event::getSubPath(time_tm); } const char* getEventFile() const { return video_file.c_str(); } static int PreAlarmCount() { return pre_alarm_count; } static void EmptyPreAlarmFrames() { pre_alarm_count = 0; } static void AddPreAlarmFrame( Image *image, SystemTimePoint timestamp, int score=0, Image *alarm_frame=nullptr ) { pre_alarm_count++; } void SavePreAlarmFrames() { EmptyPreAlarmFrames(); } int MonitorId() const; }; #endif // ZM_EVENT_H