src/zm_monitor.h
Rama master · réplica de lectura del repositorio original.
//
// ZoneMinder Monitor Class 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_MONITOR_H
#define ZM_MONITOR_H
#include "zm_define.h"
#include "zm_camera.h"
#include "zm_analysis_thread.h"
#include "zm_poll_thread.h"
#include "zm_decoder_thread.h"
#include "zm_event.h"
#include "zm_fifo.h"
#include "zm_image.h"
#include "zm_mqtt.h"
#include "zm_packet.h"
#include "zm_packetqueue.h"
#include "zm_utils.h"
#include "zm_audio_detector.h"
#include "zm_zone.h"
#include <atomic>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <sys/time.h>
#include <type_traits>
#include <vector>
#if HAVE_LIBCURL
#include <curl/curl.h>
#endif // HAVE_LIBCURL
#include "zm_monitor_onvif.h"
class Group;
class MonitorLinkExpression;
class StreamSocket;
#define SIGNAL_CAUSE "Signal"
#define MOTION_CAUSE "Motion"
#define AUDIO_CAUSE "Audio"
#define LINKED_CAUSE "Linked"
//
// This is the main class for monitors. Each monitor is associated
// with a camera and is effectively a collector for events.
//
class Monitor : public std::enable_shared_from_this<Monitor> {
friend class MonitorStream;
friend class MonitorLinkExpression;
friend class ONVIF;
public:
typedef enum {
QUERY=0,
CAPTURE,
ANALYSIS
} Purpose;
typedef enum {
CAPTURING_NONE=1,
CAPTURING_ONDEMAND,
CAPTURING_ALWAYS
} CapturingOption;
typedef enum {
ANALYSING_NONE=1,
ANALYSING_ALWAYS
} AnalysingOption;
typedef enum {
ANALYSIS_PRIMARY=1,
ANALYSIS_SECONDARY
} AnalysisSourceOption;
typedef enum {
ANALYSISIMAGE_FULLCOLOUR=1,
ANALYSISIMAGE_YCHANNEL
} AnalysisImageOption;
typedef enum {
RECORDING_NONE=1,
RECORDING_ONMOTION,
RECORDING_ALWAYS
} RecordingOption;
typedef enum {
RECORDING_PRIMARY=1,
RECORDING_SECONDARY,
RECORDING_BOTH
} RecordingSourceOption;
typedef enum {
DECODING_NONE=1,
DECODING_ONDEMAND,
DECODING_KEYFRAMES,
DECODING_KEYFRAMESONDEMAND,
DECODING_ALWAYS
} DecodingOption;
typedef enum {
HLS,
MSE,
WEBRTC
} RTSP2WebOption;
typedef enum {
RESTREAM=1,
CAMERA_DIRECT_PRIMARY,
CAMERA_DIRECT_SECONDARY
} StreamChannelOption;
typedef enum {
LOCAL=1,
REMOTE,
FILE,
FFMPEG,
LIBCURL,
VNC,
} CameraType;
typedef enum {
ROTATE_0=1,
ROTATE_90,
ROTATE_180,
ROTATE_270,
FLIP_HORI,
FLIP_VERT
} Orientation;
typedef enum {
DEINTERLACE_DISABLED = 0x00000000,
DEINTERLACE_FOUR_FIELD_SOFT = 0x00001E04,
DEINTERLACE_FOUR_FIELD_MEDIUM = 0x00001404,
DEINTERLACE_FOUR_FIELD_HARD = 0x00000A04,
DEINTERLACE_DISCARD = 0x00000001,
DEINTERLACE_LINEAR = 0x00000002,
DEINTERLACE_BLEND = 0x00000003,
DEINTERLACE_BLEND_25 = 0x00000205,
DEINTERLACE_V4L2_TOP = 0x02000000,
DEINTERLACE_V4L2_BOTTOM = 0x03000000,
DEINTERLACE_V4L2_ALTERNATE = 0x07000000,
DEINTERLACE_V4L2_PROGRESSIVE = 0x01000000,
DEINTERLACE_V4L2_INTERLACED = 0x04000000
} Deinterlace;
typedef enum {
UNKNOWN = 0,
IDLE,
PREALARM,
ALARM,
ALERT
} State;
typedef enum {
DISABLED,
ENCODE,
PASSTHROUGH,
} VideoWriter;
// An action this monitor performs when it alarms. The device acted on is
// identified by target_monitor_id and is frequently NOT this monitor: the
// point of the feature is that a camera can sound a speaker elsewhere.
struct EventAction {
enum TriggerOn { EVENT_START, EVENT_END, ALARM, ALARM_END, MANUAL };
TriggerOn trigger;
std::string action_type; // zmcontrol command, e.g. audioPlay
unsigned int target_monitor_id;
int audio_file; // -1 when the action takes no file
};
protected:
typedef std::set<Zone *> ZoneSet;
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
typedef enum { CLOSE_UNKNOWN=0, CLOSE_SYSTEM, CLOSE_TIME, CLOSE_DURATION, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
/* A SharedData field. zmc's capture, decoder and analysis threads, zms, zmu
* and the web interface all read and write these while the others run, so
* every access is a relaxed atomic: each load and store is whole and is seen
* by the other threads, but is not ordered against anything else. Plain
* members promised neither (a 64-bit field on i386 is two loads), and
* ThreadSanitizer reported them. The read-modify-write operators are single
* atomic operations, so zmu setting an action bit can't be lost to zmc
* clearing another. It holds only the value, so the layout that Memory.pm
* and Monitor.php read is unchanged. Copying one field to another is deleted
* because it would not be atomic; load() one and assign it. */
template <typename T>
class SharedValue {
public:
SharedValue &operator=(const SharedValue &) = delete;
T load() const {
T value;
__atomic_load(&value_, &value, __ATOMIC_RELAXED);
return value;
}
void store(T value) { __atomic_store(&value_, &value, __ATOMIC_RELAXED); }
operator T() const { return load(); }
T operator=(T value) { store(value); return value; }
T operator++(int) { return __atomic_fetch_add(&value_, 1, __ATOMIC_RELAXED); }
T operator|=(T bits) { return __atomic_or_fetch(&value_, bits, __ATOMIC_RELAXED); }
T operator&=(T bits) { return __atomic_and_fetch(&value_, bits, __ATOMIC_RELAXED); }
private:
// On i386 a double or 64-bit integer is only 4-aligned, and clang then calls
// libatomic for the 8-byte operations. Every 8-byte field already sits on
// an 8-byte offset, so this moves nothing; the offset asserts below check.
alignas(sizeof(T)) T value_;
};
static_assert(sizeof(SharedValue<double>) == sizeof(double), "SharedValue must hold only the value");
static_assert(sizeof(SharedValue<uint8_t>) == sizeof(uint8_t), "SharedValue must hold only the value");
static_assert(std::is_trivially_copyable<SharedValue<double>>::value, "SharedData is memset and mapped raw");
/* sizeof(SharedData) is 888 bytes on both 32bit and 64bit */
typedef struct {
SharedValue<uint32_t> size; /* +0 */
SharedValue<int32_t> last_write_index; /* +4 */
SharedValue<int32_t> last_read_index; /* +8 */
SharedValue<int32_t> image_count; /* +12 */
SharedValue<uint32_t> state; /* +16 */
/* Explicit pad. x86-64 aligns double to 8 and inserts this implicitly, but
* the i386 SysV ABI aligns double to 4 and would not, shifting every later
* member by 4. Making it explicit keeps the layout identical on both. */
uint32_t epadding1; /* +20 */
SharedValue<double> capture_fps; /* +24 Current capturing fps */
SharedValue<double> analysis_fps; /* +32 Current analysis fps */
SharedValue<double> latitude; /* +40 */
SharedValue<double> longitude; /* +48 */
SharedValue<uint64_t> last_event_id; /* +56 */
SharedValue<uint32_t> action; /* +64 */
SharedValue<int32_t> brightness; /* +68 */
SharedValue<int32_t> hue; /* +72 */
SharedValue<int32_t> colour; /* +76 */
SharedValue<int32_t> contrast; /* +80 */
SharedValue<int32_t> alarm_x; /* +84 */
SharedValue<int32_t> alarm_y; /* +88 */
SharedValue<uint8_t> valid; /* +92 */
SharedValue<uint8_t> capturing; /* +93 */
SharedValue<uint8_t> analysing; /* +94 */
SharedValue<uint8_t> recording; /* +95 */
SharedValue<uint8_t> signal; /* +96 */
SharedValue<uint8_t> format; /* +97 */
SharedValue<uint8_t> audio_level; /* +98 0-100, written by the capture thread */
SharedValue<uint8_t> audio_alarm; /* +99 audio_level crossed AudioThreshold */
SharedValue<uint32_t> imagesize; /* +100 */
SharedValue<uint32_t> last_frame_score; /* +104 */
SharedValue<uint32_t> audio_frequency; /* +108 */
SharedValue<uint32_t> audio_channels; /* +112 */
//uint32_t reserved3; /* +0 */
/*
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple
** of 8. Add or delete epadding's to achieve this.
*/
/* Explicit pad, same reasoning as epadding1: the following union is
* 8-aligned on x86-64 but only 4-aligned on i386. */
uint32_t epadding2; /* +116 */
union { /* +120 */
SharedValue<time_t> startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
uint64_t extrapad1;
};
union { /* +128 */
SharedValue<time_t> heartbeat_time; /* Constantly updated by zmc. Used to determine if the process is alive or hung or dead */
uint64_t extrapad2;
};
union { /* +136 */
SharedValue<time_t> last_write_time;
uint64_t extrapad3;
};
union { /* +144 */
SharedValue<time_t> last_read_time;
uint64_t extrapad4;
};
union { /* +152 */
SharedValue<time_t> last_viewed_time;
uint64_t extrapad5;
};
union { /* +160 */
SharedValue<time_t> last_analysis_viewed_time;
uint64_t extrapad6;
};
uint8_t control_state[256]; /* +168 */
char alarm_cause[256]; /* +424 */
/* Absolute path of the per-monitor media stream socket
** (PATH_SOCKS/stream_{monitor_id}.sock), published so consumers discover
** it without hard-coding the convention or reading the producer's
** zm.conf. Empty until the socket is served, or if the path does not fit.
** Reuses the retired video_fifo_path field, same offset and size. */
char stream_socket_path[64]; /* +680 */
/* Formerly audio_fifo_path, retired with video_fifo_path when the media
** FIFOs were replaced by the stream socket. A single socket carries both
** streams, so there is no separate audio path to publish; this slot stays
** reserved. Retiring an shm field means keeping it in place at its
** original offset and size (never removing or reordering it, which would
** shift every field after it for out-of-tree readers): either give it a
** new meaning of the same width, as stream_socket_path above did, or leave
** it reserved like this. Free for a future 64-byte field. */
char reserved_path2[64]; /* +744 */
char janus_pin[64]; /* +808 */
/* Analysis image ring: the annotated/analysis image is published into a
* ring of image_buffer_count slots (reusing the alarm_images SHM region).
* last_analysis_index is the slot most recently written (or
* image_buffer_count as the "nothing written yet" sentinel);
* analysis_image_count is a monotonic counter of analysis images published.
* Appended at the end so no earlier SharedData offset shifts. */
SharedValue<int32_t> last_analysis_index; /* +872 */
SharedValue<int32_t> analysis_image_count; /* +876 */
/* Wall clock second up to which the capture thread should keep measuring
* the audio level even though AudioDetection is off. The monitor editor's
* level meter refreshes this while it is on screen and zmc stops decoding
* audio once it has passed, so a reading can be watched without paying for
* one on every monitor forever. 0 means nobody is asking.
*
* Carved out of analysis_pad rather than appended, so neither the 888-byte
* total nor any existing offset moves. */
SharedValue<uint32_t> audio_level_until; /* +880 */
uint32_t analysis_pad; /* +884 keep 16-byte multiple */
/* 888 total */
} SharedData;
// Cross-process ABI guard: zmc/zma/zms plus the Perl (Memory.pm) and PHP
// (Monitor.php) SHM readers all assume this exact layout. If it changes,
// update those readers in lockstep and bump the size below.
//
// The struct is naturally aligned (NOT packed). Both interior pads are now
// declared explicitly as epadding1/epadding2, so the /* +N */ comments above
// are the real offsets on every supported ABI. Those pads are what keep i386
// (which aligns double and uint64_t to 4, not 8) byte-identical to x86-64;
// without them i386 produced a 880-byte struct that disagreed with the Perl
// and PHP readers, both of which assume 8-byte alignment unconditionally.
static_assert(sizeof(SharedData) == 888, "SharedData layout changed; update Memory.pm and Monitor.php offsets");
// The two members whose alignment differs between x86-64 and i386, asserted
// individually so an ABI divergence points at the culprit rather than only
// reporting a size mismatch. These must match the hardcoded offsets in
// web/includes/Monitor.php and the computed ones in Memory.pm.
static_assert(offsetof(SharedData, capture_fps) == 24, "capture_fps offset changed; update Memory.pm and Monitor.php");
static_assert(offsetof(SharedData, startup_time) == 120, "startup_time offset changed; update Memory.pm and Monitor.php");
static_assert(offsetof(SharedData, control_state) == 168, "control_state offset changed; update Memory.pm and Monitor.php");
static_assert(offsetof(SharedData, audio_level_until) == 880, "audio_level_until offset changed; update Memory.pm and Monitor.php");
enum TriggerState : uint32 {
TRIGGER_CANCEL,
TRIGGER_ON,
TRIGGER_OFF
};
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
typedef struct {
uint32_t size; /* 920 */
TriggerState trigger_state; /* 924 */
uint32_t trigger_score; /* 928 */
uint32_t padding; /* 936 */
char trigger_cause[32]; /* 968 */
char trigger_text[256]; /* 1224 */
char trigger_showtext[256];
} TriggerData;
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
typedef struct {
uint32_t size;
uint64_t current_event;
char event_file[4096];
timeval recording; // used as both bool and a pointer to the timestamp when recording should begin
} VideoStoreData;
public:
class MonitorLink {
protected:
std::shared_ptr<Monitor> monitor;
unsigned int zone_id;
const Zone *zone;
int zone_index; // index into zone_scores for our zone
std::string name;
bool connected;
time_t last_connect_time;
#if ZM_MEM_MAPPED
int map_fd;
std::string mem_file;
#else // ZM_MEM_MAPPED
int shm_id;
#endif // ZM_MEM_MAPPED
off_t mem_size;
unsigned char *mem_ptr;
SharedData *shared_data;
volatile TriggerData *trigger_data;
volatile VideoStoreData *video_store_data;
volatile int * zone_scores;
int last_state;
std::vector<Zone> zones;
public:
MonitorLink(std::shared_ptr<Monitor> p_monitor, unsigned int p_zone_id);
~MonitorLink();
inline unsigned int Id() const { return monitor->Id(); }
inline const char *Name() const { return name.c_str(); }
inline bool isConnected() const { return connected && shared_data->valid; }
inline time_t getLastConnectTime() const { return last_connect_time; }
inline uint32_t lastFrameScore() {
return shared_data->last_frame_score;
}
bool connect();
bool disconnect();
bool isAlarmed();
bool inAlarm();
bool hasAlarmed();
int score();
};
protected:
class AmcrestAPI {
#if HAVE_LIBCURL
private:
Monitor *parent;
bool alarmed;
bool healthy;
std::string last_topic;
std::string last_value;
std::string response;
CURLM *curl_multi = nullptr;
CURL *Amcrest_handle = nullptr;
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
std::unordered_map<std::string, std::string> alarms;
std::mutex alarms_mutex;
std::mutex response_mutex;
void SetNoteSet(Event::StringSet ¬eSet);
public:
explicit AmcrestAPI(Monitor *parent_);
~AmcrestAPI();
int API_Connect();
void WaitForMessage();
int start();
bool isAlarmed() const { return alarmed; };
bool isHealthy() const { return healthy; };
void setNotes(Event::StringSet ¬eSet) { SetNoteSet(noteSet); };
#else
public:
explicit AmcrestAPI(Monitor *) {}
void WaitForMessage() {}
bool isAlarmed() const { return false; }
bool isHealthy() const { return true; }
void setNotes(Event::StringSet &) {}
#endif // HAVE_LIBCURL
};
class RTSP2WebManager {
#if HAVE_LIBCURL
protected:
Monitor *parent;
CURL *curl = nullptr;
//helper class for CURL
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
bool RTSP2Web_Healthy;
bool Use_RTSP_Restream;
bool ssl_verification_failed = false;
std::string RTSP2Web_endpoint;
std::string rtsp_username;
std::string rtsp_password;
std::string rtsp_restream_path;
std::string rtsp_path;
std::string rtsp_second_path;
public:
explicit RTSP2WebManager(Monitor *parent_);
~RTSP2WebManager();
void load_from_monitor();
int add_to_RTSP2Web();
int check_RTSP2Web();
int remove_from_RTSP2Web();
#else
public:
explicit RTSP2WebManager(Monitor *) {}
void load_from_monitor() {}
int add_to_RTSP2Web() { return 0; }
int check_RTSP2Web() { return 1; }
int remove_from_RTSP2Web() { return 0; }
#endif // HAVE_LIBCURL
};
class Go2RTCManager {
#if HAVE_LIBCURL
protected:
Monitor *parent;
// helper class for CURL
struct transfer {
const char *buf;
size_t total;
size_t uploaded;
};
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
static size_t ReadCallback(char *ptr, size_t size, size_t nmemb, void *data);
std::pair<CURLcode, std::string> CURL_PUT(const std::string &endpoint, const std::string &data) const;
bool Go2RTC_Healthy;
bool Use_RTSP_Restream;
mutable bool ssl_verification_failed = false;
std::string Go2RTC_endpoint;
std::string rtsp_restream_path;
std::string rtsp_restream_base_path; // Path without auth parameter
std::string rtsp_username;
std::string rtsp_password;
std::string rtsp_path;
std::string rtsp_second_path;
SystemTimePoint last_auth_refresh; // Track when auth was last refreshed
public:
explicit Go2RTCManager(Monitor *parent_);
~Go2RTCManager();
void load_from_monitor();
int add_to_Go2RTC();
int check_Go2RTC();
int remove_from_Go2RTC();
bool refresh_auth_if_needed(); // Refresh auth_hash if expired
#else
public:
explicit Go2RTCManager(Monitor *) {}
void load_from_monitor() {}
int add_to_Go2RTC() { return 0; }
int check_Go2RTC() { return 1; }
int remove_from_Go2RTC() { return 0; }
bool refresh_auth_if_needed() { return false; }
#endif // HAVE_LIBCURL
};
class JanusManager {
#if HAVE_LIBCURL
protected:
Monitor *parent;
CURL *curl = nullptr;
//helper class for CURL
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
bool Janus_Healthy;
bool Use_RTSP_Restream;
bool ssl_verification_failed = false;
std::string janus_session;
std::string janus_handle;
std::string janus_endpoint;
std::string stream_key;
std::string rtsp_username;
std::string rtsp_password;
TimePoint rtsp_auth_time;
std::string rtsp_path;
std::string profile_override;
std::uint32_t rtsp_session_timeout;
public:
explicit JanusManager(Monitor *parent_);
~JanusManager();
void load_from_monitor();
int add_to_janus();
int check_janus();
int remove_from_janus();
int get_janus_session();
int get_janus_handle();
int get_janus_plugin();
#else
public:
explicit JanusManager(Monitor *) {}
void load_from_monitor() {}
int add_to_janus() { return 0; }
int check_janus() { return 1; }
int remove_from_janus() { return 0; }
int get_janus_session() { return 0; }
int get_janus_handle() { return 0; }
int get_janus_plugin() { return 0; }
#endif // HAVE_LIBCURL
};
// These are read from the DB and thereafter remain unchanged
unsigned int id;
std::string name;
bool deleted;
unsigned int server_id; // Id of the Server object
unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more.
CameraType type;
CapturingOption capturing; // None, OnDemand, Always
AnalysingOption analysing; // None, Always
AnalysisSourceOption analysis_source; // Primary, Secondary
AnalysisImageOption analysis_image; // FullColour, YChannel
RecordingOption recording; // None, OnMotion, Always
RecordingSourceOption recording_source; // Primary, Secondary, Both
DecodingOption decoding; // Whether the monitor will decode h264/h265 packets
bool RTSP2Web_enabled; // Whether we set the h264/h265 stream up on RTSP2Web
int RTSP2Web_type; // Whether we set the h264/h265 stream up on RTSP2Web
StreamChannelOption stream_channel; // Which stream source to use: Restream, CameraDirectPrimary, or CameraDirectSecondary
bool Go2RTC_enabled; // Whether we set the h264/h265 stream up on Go2RTC
bool Go2RTC_disable_backchannel;
bool janus_enabled; // Whether we set the h264/h265 stream up on janus
bool janus_audio_enabled; // Whether we tell Janus to try to include audio.
std::string janus_profile_override; // The Profile-ID to force the stream to use.
bool restream; // Point streaming services (Janus/Go2RTC/RTSP2Web) at the ZM RTSP output, rather than the camera directly.
std::string janus_pin; // For security, we generate a pin required to view the stream.
int rtsp_user; // User Id of a user to use for auth to RTSP_Server
int janus_rtsp_session_timeout; // RTSP session timeout (work around for cameras that dont send ;timeout=<timeout in seconds> but do have a timeout)
std::string protocol;
std::string method;
std::string options;
std::string host;
std::string port;
std::string user;
std::string pass;
std::string path;
std::string second_path;
std::string device_class;
std::string onvif_url;
std::string onvif_events_path;
std::string onvif_username;
std::string onvif_password;
std::string onvif_options;
bool onvif_event_listener;
bool use_Amcrest_API;
std::string device;
int palette;
int channel;
int format;
int camera_width;
int camera_height;
unsigned int width; // Normally the same as the camera, but not if partly rotated
unsigned int height; // Normally the same as the camera, but not if partly rotated
bool v4l_multi_buffer;
unsigned int v4l_captures_per_frame;
Orientation orientation; // Whether the image has to be rotated at all
unsigned int deinterlacing;
unsigned int deinterlacing_value;
std::string decoder_name;
std::string decoder_hwaccel_name;
std::string decoder_hwaccel_device;
// The decoder belongs here rather than to the camera: the camera's job is to
// produce packets, and the decoder thread runs against this context for as
// long as the monitor says so. It used to live in FfmpegCamera, where
// camera->PrimeCapture() freed and rebuilt it underneath a decoder thread
// still holding the pointer.
//
// decoder_hw_pix_fmt must outlive mVideoCodecContext: the context stores its
// address in opaque and get_hw_format reads it back on every format
// negotiation. Being a member of the same object gives it that lifetime.
AVCodecContext *mVideoCodecContext;
AVPixelFormat decoder_hw_pix_fmt;
AVBufferRef *decoder_hw_device_ctx;
bool videoRecording;
int savejpegs;
int colours;
VideoWriter videowriter;
std::string encoderparams;
std::string output_codec;
std::string encoder;
std::string encoder_hwaccel_name;
std::string encoder_hwaccel_device;
// Hardware device contexts used by the encoder (VideoStore), one per
// (hwdevice type, device) and shared by every event's VideoStore rather than
// created and destroyed per event: a per-event create/destroy opened and
// closed /dev/dri/renderD128 each time, and on some VAAPI drivers the
// teardown leaked the fd, so a long-running monitor eventually hit the
// open-file limit (refs #5300). Keyed, and never evicted, so that probing a
// second encoder type (the auto encoder list interleaves VAAPI and QSV)
// cannot drop the context an earlier probe created. Owned here; VideoStore
// borrows a reference through GetEncoderHWDeviceContext.
std::map<std::pair<int, std::string>, AVBufferRef *> encoder_hw_device_ctxs;
std::mutex encoder_hw_device_ctx_mutex;
std::string output_container;
_AVPIXELFORMAT imagePixFormat;
bool record_audio; // Whether to store the audio that we receive
bool audio_detection; // Whether to score on how loud the audio is
int audio_threshold; // 0-100; 0 means detection is off
int audio_alarm_score; // Score contributed while over threshold
AudioDetector audio_detector;
bool wallclock_timestamps; // Whether to use wallclock pts/dts instead of values from ffmpeg
int output_source_stream;
int brightness; // The statically saved brightness of the camera
int contrast; // The statically saved contrast of the camera
int hue; // The statically saved hue of the camera
int colour; // The statically saved colour of the camera
std::string event_prefix; // The prefix applied to event names as they are created
std::string label_format; // The format of the timestamp on the images
Vector2 label_coord; // The coordinates of the timestamp on the images
int label_size; // Size of the timestamp on the images
int32_t image_buffer_count; // Size of circular image buffer, kept in /dev/shm
int32_t max_image_buffer_count; // Max # of video packets to keep in packet queue
int warmup_count; // How many images to process before looking for events
int pre_event_count; // How many images to hold and prepend to an alarm event
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
Seconds section_length; // How long events should last in continuous modes
bool section_length_warn; // Whether to log a warning when a motion event exceeds desired section_length
Seconds min_section_length; // Minimum event length when using event_close_mode == ALARM
bool startstop_on_section_length; // Whether to start/stop events on time % section_length
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
int motion_frame_skip; // How many frames to skip in motion detection
double analysis_fps_limit; // Target framerate for video analysis
Microseconds analysis_update_delay; // How long we wait before updating analysis parameters
Microseconds capture_delay; // How long we wait between capture frames
Microseconds alarm_capture_delay; // How long we wait between capture frames when in alarm state
int alarm_frame_count; // How many alarm frames are required before an event is triggered
int alert_to_alarm_frame_count; // How many alarm frames (consecutive score frames) are required to return alarm from alert
// value for now is the same number configured in alarm_frame_count, maybe getting his own parameter some day
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion
int signal_check_points; // Number of points in the image to check for signal
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not
double latitude;
double longitude;
bool rtsp_server; // Whether to include this monitor as an rtsp server stream
std::string rtsp_streamname; // path in the rtsp url for this monitor
bool soap_wsa_compl; // Whether the camera supports soap_wsa or not.
std::string onvif_alarm_txt; // def onvif_alarm_txt
int importance; // Importance of this monitor, affects Connection logging errors.
int startup_delay; // Seconds to sleep before connecting to camera
unsigned int zone_count;
int capture_max_fps;
Purpose purpose; // What this monitor has been created to do
unsigned int last_camera_bytes;
int event_count;
int last_capture_image_count; // last value of image_count when calculating capture fps
int analysis_image_count; // How many frames have been processed by analysis thread.
// Written by the decoder / analysis thread, read by the others: atomic.
std::atomic<int> decoding_image_count; // How many frames the decoder has written to shm.
std::atomic<int> motion_frame_count; // How many frames have had motion detection performed on them.
int last_motion_frame_count; // last value of motion_frame_count when calculating fps
int ready_count;
int first_alarm_count;
int last_alarm_count;
bool last_signal;
// Capture assumes signal on the first packet after connect() cleared it,
// even when the decoder measures it, so Analyse doesn't see a signal loss
// before the first decoded frame. Capture thread only.
bool assume_signal_on_next_packet;
int buffer_count;
State state;
SystemTimePoint start_time;
SystemTimePoint last_fps_time;
// How often each monitor writes its row of Monitor_Status, and the width of
// the window the writes are staggered across (see Monitor::connect).
static constexpr Seconds kStatusUpdateInterval = Seconds(10);
SystemTimePoint last_status_time;
SystemTimePoint last_analysis_fps_time;
SystemTimePoint auto_resume_time;
unsigned int last_motion_score;
EventCloseMode event_close_mode;
#if ZM_MEM_MAPPED
int map_fd;
std::string mem_file;
#else // ZM_MEM_MAPPED
int shm_id;
#endif // ZM_MEM_MAPPED
off_t mem_size;
unsigned char *mem_ptr;
SharedData *shared_data;
TriggerData *trigger_data;
VideoStoreData *video_store_data;
int *zone_scores;
struct timeval *shared_timestamps;
unsigned char *shared_images;
std::vector<Image *> image_buffer;
AVPixelFormat *image_pixelformats;
// Per-slot cross-process format for the analysis image ring (one entry per
// analysis_image_buffer slot), mirroring image_pixelformats for the capture
// ring. Replaces the former single alarm_image_pixelformat.
AVPixelFormat *analysis_image_pixelformats;
size_t shm_slot_size; // per-slot byte capacity, sized to RGBA upper bound
int video_stream_id; // will be filled in PrimeCapture
int audio_stream_id; // will be filled in PrimeCapture
// Always-on media output; survives camera reconnects, freed in destructor
std::unique_ptr<StreamSocket> stream_socket;
// Current capture health for the stream socket snapshot: 0 = healthy, else
// the last fault event code, with its message. Guarded by stream_event_mutex
// because health events come from the capture thread while state_changed
// events come from the analysis thread.
uint16_t stream_health_code = 0;
// Mirror of the analysis state for the snapshot, written by SetState() under
// stream_event_mutex so the capture thread never reads `state` itself.
State stream_snapshot_state = IDLE;
std::string stream_health_message;
std::mutex stream_event_mutex;
// Rebuild and cache the stream socket snapshot from current state + health.
void RefreshStreamSnapshot();
std::shared_ptr<Camera> camera;
Event *event;
std::mutex event_mutex;
Storage *storage;
VideoStore *videoStore;
PacketQueue packetqueue;
// Stream timestamps kept going forward across a jump back in the camera's
// clock: an offset (microseconds) added to every packet's dts and pts, and
// each stream's last dts with the offset applied and its last step.
int64_t dts_offset_ = 0;
std::map<int, int64_t> last_dts_;
std::map<int, int64_t> last_dts_step_;
// A step back of more than this in a stream's dts is a jump in the camera's
// clock, not reordering jitter.
static constexpr int64_t kDtsStepBack = 1000000;
// Moves the packet onto the continuous timeline; returns the offset applied.
int64_t KeepDtsContinuous(AVPacket *packet, AVRational time_base);
std::unique_ptr<PollThread> Poller;
std::list<ZMPacketLock> decoder_queue;
// The decoder_queue entry a decoded frame with this pts came from; releases
// the entries before it that the codec consumed without returning a frame.
std::list<ZMPacketLock>::iterator MatchDecodedFrame(int64_t frame_pts);
// How many packets decoder_queue may hold, waiting on the codec, before
// Decode() gives up on it and flushes.
static size_t DecoderQueueLimit(int max_keyframe_interval, const AVCodecContext *context);
// Set when Decode() flushed a full decoder_queue: nothing goes to the codec
// until a keyframe, as it has no reference frames to decode the rest against.
bool decoder_needs_keyframe = false;
// Whether a packet must be held back from the codec while it waits for a
// keyframe; clears the wait once the keyframe comes.
bool SkipUntilKeyframe(bool keyframe);
packetqueue_iterator *analysis_it;
std::unique_ptr<AnalysisThread> analysis_thread;
packetqueue_iterator *decoder_it;
std::unique_ptr<DecoderThread> decoder;
SwsContext *convert_context;
std::thread close_event_thread;
// Guards close_event_thread itself. closeEvent() runs on the analysis
// thread and Pause() on the capture thread, and both join and reassign
// it, so the object needs a lock of its own. Not the event lock: that is
// held across closeEvent(), which spawns the thread this guards.
std::mutex close_event_thread_mutex;
std::vector<Zone> zones;
#if MOSQUITTOPP_FOUND
bool mqtt_enabled;
std::vector<std::string> mqtt_subscriptions;
std::unique_ptr<MQTT> mqtt;
#endif
const unsigned char *privacy_bitmask;
std::string linked_monitors_string;
int n_linked_monitors;
MonitorLinkExpression *linked_monitors;
//MonitorLink **linked_monitors;
std::string event_start_command;
std::string event_end_command;
std::vector<EventAction> actions;
// Whether the Alarm actions have run for the alarm currently in progress.
bool alarm_actions_fired;
std::vector<Group *> groups;
Image delta_image;
Image ref_image;
// ref_image is owned by the analysis thread (Analyse/DetectMotion). The
// capture thread (CheckAction) must not touch its buffer directly; on
// suspend-resume it sets this flag instead and the analysis thread drops the
// stale reference itself on its next pass. Prevents a use-after-free/null
// deref race in Image::Delta (refs #4983).
std::atomic<bool> ref_image_reset_{false};
// Set by CheckOndemand() when a linked monitor's alarm wakes this on-demand
// monitor from its pause: steady_clock nanoseconds until which Analyse()
// counts the link as alarmed, 0 for none. The link only reports an alarm while
// it lasts, which can be shorter than reopening the camera takes, so the wake
// has to be remembered until the first packet acts on it.
std::atomic<int64_t> linked_wake_until_{0};
// Analysis image ring: the annotated/analysis image is published into a ring
// of image_buffer_count slots living in the alarm_images SHM region. Readers
// pick up the newest via shared_data->last_analysis_index. Replaces the
// former single alarm_image.
std::vector<Image *> analysis_image_buffer;
Image write_image; // Used when creating snapshot images
std::string diag_path_ref;
std::string diag_path_delta;
RTSP2WebManager *RTSP2Web_Manager;
Go2RTCManager *Go2RTC_Manager;
JanusManager *Janus_Manager;
AmcrestAPI *Amcrest_Manager;
ONVIF *onvif;
// Used in check signal
uint8_t red_val;
uint8_t green_val;
uint8_t blue_val;
uint8_t grayscale_val; /* 8bit grayscale color */
uint8_t luma_val; /* luma of the RGB colour, limited range */
uint8_t luma_full_val; /* luma of the RGB colour, full range */
Rgb colour_val; /* RGB32 color */
int usedsubpixorder;
public:
explicit Monitor();
~Monitor();
void AddPrivacyBitmask();
void LoadCamera();
const std::shared_ptr<Camera> getCamera() { return camera; }
bool connect();
bool disconnect();
inline bool isConnected() const { return mem_ptr != nullptr; }
inline int ShmValid() const {
if (shared_data && shared_data->valid) {
timeval now = {};
gettimeofday(&now, nullptr);
Debug(3, "Shared data is valid, checking heartbeat %" PRIi64 " - %" PRIi64 " = %" PRIi64" < %f",
static_cast<int64>(now.tv_sec),
static_cast<int64>(shared_data->heartbeat_time),
static_cast<int64>(now.tv_sec - shared_data->heartbeat_time),
config.watch_max_delay);
if ((now.tv_sec - shared_data->heartbeat_time) < config.watch_max_delay)
return true;
}
return false;
}
inline unsigned int Id() const { return id; }
inline const char *Name() const { return name.c_str(); }
inline bool Deleted() const { return deleted; }
inline unsigned int ServerId() const { return server_id; }
inline Storage *getStorage() {
if (!storage) {
storage = new Storage(storage_id);
}
return storage;
}
inline CameraType GetType() const { return type; }
CapturingOption Capturing() const { return capturing; }
AnalysingOption Analysing() const { return analysing; }
RecordingOption Recording() const { return recording; }
inline PacketQueue * GetPacketQueue() { return &packetqueue; }
// Called by the decoder thread as it exits. Releases packet locks for
// anything it sent to the codec context but never received as a frame
// (codec context is about to be torn down for Pause/reconnect, so those
// packets will never produce output). Marks them decoded so the analysis
// thread can advance past them.
void flushDecoderQueue();
inline bool Enabled() const {
return shared_data->capturing;
}
DecodingOption Decoding() const {
return decoding;
}
const std::string &DecoderName() const { return decoder_name; }
const std::string &DecoderHWAccelName() const { return decoder_hwaccel_name; }
const std::string &DecoderHWAccelDevice() const { return decoder_hwaccel_device; }
bool JanusEnabled() {
return janus_enabled;
}
bool JanusAudioEnabled() {
return janus_audio_enabled;
}
inline const char* get_stream_key() {
return shared_data->janus_pin;
}
bool has_out_of_order_packets() { return packetqueue.has_out_of_order_packets(); };
int get_max_keyframe_interval() { return packetqueue.get_max_keyframe_interval(); };
bool OnvifEnabled() {
return onvif_event_listener;
}
int check_janus() {
if (Janus_Manager) return Janus_Manager->check_janus();
return -1;
}
bool EventPollerHealthy() const {
if (onvif) {
return onvif->isHealthy();
} else if (Amcrest_Manager) {
return Amcrest_Manager->isHealthy();
}
return false;
}
inline const char *EventPrefix() const { return event_prefix.c_str(); }
inline bool Ready() const {
if (!packetqueue.get_max_keyframe_interval()) {
Debug(4, "Not ready because no keyframe interval.");
return false;
}
if (decoding_image_count > ready_count) {
Debug(4, "Ready because decoding_image_count(%d) > ready_count(%d)", decoding_image_count.load(), ready_count);
return true;
}
Debug(4, "Not ready because decoding_image_count(%d) <= ready_count(%d)", decoding_image_count.load(), ready_count);
return false;
}
inline bool Active() const {
return shared_data->analysing;
}
int64_t getLastViewed() {
if (shared_data && shared_data->valid)
return shared_data->last_viewed_time;
return 0;
}
void setLastViewed() {
setLastViewed(std::chrono::system_clock::now());
}
void setLastViewed(SystemTimePoint new_time) {
if (shared_data && shared_data->valid)
shared_data->last_viewed_time =
static_cast<int64>(std::chrono::duration_cast<Seconds>(new_time.time_since_epoch()).count());
}
// What zmc's capture loop does with an on-demand monitor on this pass.
enum class OndemandStep { CAPTURE, PLAY, PAUSE, SLEEP };
// viewed: watched in the last 10 s. has_image: an image has been written, so
// thumbnails have something to show. primed: the camera is open. busy:
// alarmed, in its post-event frames, or woken by a link that Analyse() hasn't
// acted on yet. link_alarmed is asked only while the camera is closed, when
// the analysis thread that otherwise reads the links is stopped.
static OndemandStep DecideOndemand(bool viewed, bool has_image, bool primed, bool busy,
const std::function<bool()> &link_alarmed);
OndemandStep CheckOndemand();
bool LinkedWakePending() const;
// How long a link wake waits for the camera to reopen and a packet to act on it.
static constexpr std::chrono::seconds kLinkedWakeTimeout{30};
bool hasViewers() {
if (shared_data && shared_data->valid) {
SystemTimePoint now = std::chrono::system_clock::now();
int64 intNow = static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count());
Debug(3, "Last viewed %" PRId64 " seconds ago", intNow - shared_data->last_viewed_time);
return (((!shared_data->last_viewed_time) or ((intNow - shared_data->last_viewed_time)) > 10)) ? false : true;
}
return false;
}
int64_t getLastAnalysisViewed() {
if (shared_data && shared_data->valid)
return shared_data->last_analysis_viewed_time;
return 0;
}
void setLastAnalysisViewed() {
setLastAnalysisViewed(std::chrono::system_clock::now());
}
void setLastAnalysisViewed(SystemTimePoint new_time) {
if (shared_data && shared_data->valid)
shared_data->last_analysis_viewed_time =
static_cast<int64>(std::chrono::duration_cast<Seconds>(new_time.time_since_epoch()).count());
}
bool hasAnalysisViewers() {
if (shared_data && shared_data->valid) {
SystemTimePoint now = std::chrono::system_clock::now();
int64 intNow = static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count());
Debug(3, "Last analysis viewed %" PRId64 " seconds ago", intNow - shared_data->last_analysis_viewed_time);
return (((!shared_data->last_analysis_viewed_time) or ((intNow - shared_data->last_analysis_viewed_time)) > 10)) ? false : true;
}
return false;
}
inline bool Exif() const { return embed_exif; }
inline double Latitude() const { return shared_data ? shared_data->latitude : latitude; }
inline double Longitude() const { return shared_data ? shared_data->longitude : longitude; }
inline bool RTSPServer() const { return rtsp_server; }
inline bool RecordAudio() const { return record_audio; }
inline bool WallClockTimestamps() const { return wallclock_timestamps; }
/*
inline Purpose Purpose() { return purpose };
inline Purpose Purpose( Purpose p ) { purpose = p; };
*/
Orientation getOrientation() const;
unsigned int Width() const { return width; }
unsigned int Height() const { return height; }
unsigned int Colours() const;
unsigned int SubpixelOrder() const;
int GetAudioFrequency() const { return shared_data ? shared_data->audio_frequency : -1; }
int GetAudioChannels() const { return shared_data ? shared_data->audio_channels : -1; }
int GetOptSaveJPEGs() const { return savejpegs; }
VideoWriter GetOptVideoWriter() const { return videowriter; }
const std::string &GetEncoderOptions() const { return encoderparams; }
const std::string &EncoderHWAccelName() const { return encoder_hwaccel_name; }
const std::string &EncoderHWAccelDevice() const { return encoder_hwaccel_device; }
#if HAVE_LIBAVUTIL_HWCONTEXT_H && LIBAVCODEC_VERSION_CHECK(57, 107, 0, 107, 0)
// Return a new reference to the encoder hardware device context for (type,
// device), creating and caching it on first use so every event's VideoStore
// reuses one device instead of opening the render node again (refs #5300).
// The caller owns the returned reference and must unref it. Returns nullptr
// if the device cannot be created.
AVBufferRef *GetEncoderHWDeviceContext(AVHWDeviceType type, const std::string &device);
#endif
const std::string &OutputCodec() const { return output_codec; }
const std::string &Encoder() const { return encoder; }
const std::string &OutputContainer() const { return output_container; }
uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; }
void SetVideoWriterEventId( uint64_t p_event_id ) { video_store_data->current_event = p_event_id; }
SystemTimePoint GetVideoWriterStartTime() const {
return SystemTimePoint(zm::chrono::duration_cast<Microseconds>(video_store_data->recording));
}
void SetVideoWriterStartTime(SystemTimePoint t) {
video_store_data->recording = zm::chrono::duration_cast<timeval>(t.time_since_epoch());
}
unsigned int GetPreEventCount() const { return pre_event_count; };
Seconds GetSectionLength() const { return section_length; };
int32_t GetImageBufferCount() const { return image_buffer_count; };
State GetState() const { return (State)shared_data->state.load(); }
// Set the analysis state, publishing the transition as a stream socket
// state_changed event (and refreshing the snapshot) when it actually changes.
void SetState(State new_state);
// Start the per-monitor media stream socket listener if it is not already
// running. Independent of the camera: the socket carries lifecycle events
// (capture faults) before any media, so zmc starts it before the first
// connect attempt to make startup faults observable. PrimeCapture() calls it
// too, then adds stream parameters. Producer side only (zmc).
void StartStreamSocket();
// Emit a capture-fault lifecycle event on the stream socket and update the
// cached health snapshot. code is one of the kEvent* health codes; a *_failed
// code sets the snapshot's active fault, a *_restored/_resumed code clears it.
void SendStreamHealthEvent(uint16_t code, const std::string &message, int detail = 0);
AVStream *GetAudioStream() const { return camera ? camera->getAudioStream() : nullptr; };
AVStream *GetVideoStream() const { return camera ? camera->getVideoStream() : nullptr; };
AVCodecContext *GetVideoCodecContext() const { return mVideoCodecContext; };
std::string GetSecondPath() const { return second_path; };
std::string GetStreamSocketPath() const { return shared_data ? shared_data->stream_socket_path : ""; };
std::string GetRTSPStreamName() const { return rtsp_streamname; };
const std::string &getONVIF_URL() const { return onvif_url; };
const std::string &getONVIF_Username() const { return onvif_username; };
const std::string &getONVIF_Password() const { return onvif_password; };
const std::string &getONVIF_Options() const { return onvif_options; };
Image *GetAlarmImage();
// Writer-side helper: copies src into alarm_image and publishes its
// AVPixelFormat so reader processes can correctly interpret the SHM bytes.
void WriteAlarmImage(const Image &src);
int GetImage(int32_t index=-1, int scale=100);
std::shared_ptr<ZMPacket> getSnapshot( int index=-1 );
SystemTimePoint GetTimestamp(int index = -1);
void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate();
Microseconds GetAnalysisUpdateDelay() const { return analysis_update_delay; }
unsigned int GetCaptureMaxFPS() const { return capture_max_fps; }
Microseconds GetCaptureDelay() const { return capture_delay; }
Microseconds GetAlarmCaptureDelay() const { return alarm_capture_delay; }
int GetLastReadIndex() const;
int GetLastWriteIndex() const;
uint64_t GetLastEventId() const;
double GetFPS() const;
void UpdateFPS();
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
void ForceAlarmOff();
void CancelForced();
TriggerState GetTriggerState() const { return trigger_data ? trigger_data->trigger_state : TRIGGER_CANCEL; }
// shared_data is null until connect() maps the shm segment, and connect()
// fails with it still null whenever the mmap file cannot be opened, grown or
// mapped -- wrong ownership, or /dev/shm too small for the requested buffers.
// zmc's startup loop calls SetHeartbeatTime() on every failed retry, so
// without these guards a monitor that cannot get its shm takes zmc down with
// SIGSEGV instead of retrying. The accessors around these already guard the
// same way.
SystemTimePoint GetStartupTime() const {
if (!shared_data) return SystemTimePoint();
return std::chrono::system_clock::from_time_t(shared_data->startup_time);
}
void SetStartupTime(SystemTimePoint time) {
if (!shared_data) return;
shared_data->startup_time = std::chrono::system_clock::to_time_t(time);
}
void SetHeartbeatTime(SystemTimePoint time) {
if (!shared_data) return;
shared_data->heartbeat_time = std::chrono::system_clock::to_time_t(time);
}
void get_ref_image();
int LabelSize() const { return label_size; }
void actionReload();
void actionEnable();
void actionDisable();
void actionSuspend();
void actionResume();
int actionBrightness(int p_brightness);
int actionBrightness();
int actionHue(int p_hue);
int actionHue();
int actionColour(int p_colour);
int actionColour();
int actionContrast(int p_contrast);
int actionContrast();
int PrimeCapture();
// Open a decoder for the camera's video stream, and tear it down. Safe to
// call when there is nothing to decode: cameras that hand over images rather
// than packets have no video stream.
int OpenDecoder();
void CloseDecoder();
int PreCapture() const;
int Capture();
int PostCapture() const;
int Pause();
int Play();
int Close();
void CheckAction();
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
unsigned int AnalyseFrame( const Image &frame_image, Event::StringSet &zoneSet );
unsigned int AnalyseFrame( const Image &frame_image, Event::StringSet &zoneSet, Image *analysis_image );
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
bool CheckSignal( const Image *image );
// True when the decoder measures shared_data->signal (SignalCheckPoints)
// for the packets it decodes; Capture then leaves the flag to it, so the
// two threads don't overwrite each other's value. Pure, for tests.
static bool DecoderChecksSignal(DecodingOption decoding, int signal_check_points, bool has_viewers);
bool Analyse();
bool setupConvertContext(const AVFrame *input_frame, const Image *image);
// Write capture_image into image_buffer[index] without conversion and
// record its AVPixelFormat in image_pixelformats[index] so reading
// processes can adopt that format via ReadShmFrame.
void WriteShmFrame(unsigned int index, Image *capture_image);
// Read-side counterpart: ensures image_buffer[index]'s metadata matches
// the format zmc wrote via image_pixelformats[index] before returning
// it. Use this from zms / zma / event paths instead of touching
// image_buffer[index] directly.
Image *ReadShmFrame(unsigned int index);
void applyOrientation(Image *image);
bool applyDeinterlacing(std::shared_ptr<ZMPacket> &packet, Image *capture_image);
bool Decode();
bool Poll();
void DumpImage( Image *dump_image ) const;
std::string Substitute(const std::string &format, SystemTimePoint ts_time) const;
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
Event *openEvent(
ZMPacketLock *packet_lock,
const std::string &cause,
const Event::StringSetMap ¬eSetMap);
// Join a close that is still in flight, so whatever it holds -- notably a
// hardware encoder session belonging to the previous event -- is released.
// Returns true if there was one to wait for.
bool WaitForEventClose();
void closeEvent();
void Reload();
void ReloadZones();
void ReloadLinkedMonitors();
void LoadActions();
// Fires the AlarmEnd actions once per alarm, and only when the Alarm actions
// actually ran, so the two stay paired.
void EndAlarmActions();
void RunActions(EventAction::TriggerOn trigger);
// Pure helpers, separated from RunActions so they can be tested without a
// database, a socket or a device. ActionCommandName maps the DB enum onto a
// zmcontrol method rather than passing the stored string through, so nothing
// from the database is interpolated into the message uninspected.
// Builds the Ffmpeg-equivalent URL for a Remote/rtsp monitor, so the
// deprecation warning can name the exact Source Path to switch to. Pure, so
// it is testable without a camera; pass the result through
// remove_authentication() before logging it.
static std::string RtspUrlFromRemote(const std::string &host, const std::string &port,
const std::string &path, const std::string &user,
const std::string &pass);
static const char *ActionCommandName(const std::string &action_type);
static std::string ActionMessage(const EventAction &action);
static const char *ActionTriggerName(EventAction::TriggerOn trigger);
bool DumpSettings( char *output, bool verbose );
void DumpZoneImage( const char *zone_string=0 );
std::vector<Group *> Groups();
StringVector GroupNames();
static std::vector<std::shared_ptr<Monitor>> LoadMonitors(const std::string &sql, Purpose purpose); // Returns # of Monitors loaded, 0 on failure.
#if ZM_HAS_V4L2
static std::vector<std::shared_ptr<Monitor>> LoadLocalMonitors(const char *device, Purpose purpose);
#endif // ZM_HAS_V4L2
static std::vector<std::shared_ptr<Monitor>> LoadRemoteMonitors(const char *protocol, const char *host, const char*port, const char*path, Purpose purpose);
static std::vector<std::shared_ptr<Monitor>> LoadFileMonitors(const char *file, Purpose purpose);
static std::vector<std::shared_ptr<Monitor>> LoadFfmpegMonitors(const char *file, Purpose purpose);
static std::shared_ptr<Monitor> Load(unsigned int id, bool load_zones, Purpose purpose);
void Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose);
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
double get_capture_fps( ) const {
return shared_data ? shared_data->capture_fps : 0.0;
}
double get_analysis_fps( ) const {
return shared_data ? shared_data->analysis_fps : 0.0;
}
int Importance() const { return importance; }
int StartupDelay() const { return startup_delay; }
// Whether anything wants the audio level right now: either the monitor
// scores on it, or the editor's meter has asked for a reading and its
// request has not expired. Decoding audio is not free, so a monitor nobody
// is asking about does not do it.
bool AudioLevelWanted(SystemTimePoint now) const;
// Peak audio level since the last call, for the Frames row about to be
// written. Clears on read, so each row covers its own interval. Always 0
// when the monitor has AudioDetection off, because nothing runs the
// detector then, and that is what tells the event view there is no audio
// series to draw.
int TakeAudioPeak() { return audio_detector.TakePeak(); }
private:
// True after a keyframe is sent until the decoder outputs the first frame.
// Used by keyframe-based decoding modes to feed any required follow-up packets.
// A future improvement could eliminate mode-specific checks by relying solely
// on this flag to track the decoder state.
bool decoder_requires_next_packet = false;
};
#define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit))
#endif // ZM_MONITOR_H