2003-03-26 19:57:29 +08:00
//
// ZoneMinder Monitor Class Implementation, $Date$, $Revision$
2008-07-25 17:33:23 +08:00
// Copyright (C) 2001-2008 Philip Coombes
2018-03-09 20:23:40 +08:00
//
2003-03-26 19:57:29 +08:00
// 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.
2018-03-09 20:23:40 +08:00
//
2003-03-26 19:57:29 +08:00
// 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.
2018-03-09 20:23:40 +08:00
//
2003-03-26 19:57:29 +08:00
// 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.
2018-03-09 20:23:40 +08:00
//
2003-03-26 19:57:29 +08:00
# include "zm_monitor.h"
2021-02-04 11:47:28 +08:00
# include "zm_group.h"
2017-06-23 05:58:32 +08:00
# include "zm_eventstream.h"
2021-02-04 11:47:28 +08:00
# include "zm_fifo.h"
# include "zm_file_camera.h"
2003-03-26 19:57:29 +08:00
# include "zm_remote_camera.h"
2008-07-16 16:35:59 +08:00
# include "zm_remote_camera_http.h"
2017-10-04 04:28:56 +08:00
# include "zm_remote_camera_nvsocket.h"
2021-02-04 11:47:28 +08:00
# include "zm_signal.h"
# include "zm_time.h"
2021-02-11 06:18:21 +08:00
# include "zm_utils.h"
2021-02-04 11:47:28 +08:00
# include "zm_zone.h"
# if ZM_HAS_V4L
# include "zm_local_camera.h"
# endif // ZM_HAS_V4L
2008-10-17 00:11:49 +08:00
# if HAVE_LIBAVFORMAT
2008-07-16 16:35:59 +08:00
# include "zm_remote_camera_rtsp.h"
2008-10-17 00:11:49 +08:00
# endif // HAVE_LIBAVFORMAT
2021-02-04 11:47:28 +08:00
2009-01-20 23:01:32 +08:00
# if HAVE_LIBAVFORMAT
# include "zm_ffmpeg_camera.h"
# endif // HAVE_LIBAVFORMAT
2021-02-04 11:47:28 +08:00
2013-12-13 01:45:29 +08:00
# if HAVE_LIBVLC
# include "zm_libvlc_camera.h"
# endif // HAVE_LIBVLC
2021-02-04 11:47:28 +08:00
2013-11-04 23:10:07 +08:00
# if HAVE_LIBCURL
# include "zm_curl_camera.h"
# endif // HAVE_LIBCURL
2021-02-04 11:47:28 +08:00
2020-03-26 15:08:00 +08:00
# if HAVE_LIBVNC
2020-03-26 14:21:34 +08:00
# include "zm_libvnc_camera.h"
2020-03-26 15:08:00 +08:00
# endif // HAVE_LIBVNC
2003-03-26 19:57:29 +08:00
2021-02-11 06:29:51 +08:00
# include <algorithm>
2021-02-04 11:47:28 +08:00
# include <sys/types.h>
# include <sys/stat.h>
2008-07-14 23:21:16 +08:00
# if ZM_MEM_MAPPED
# include <sys/mman.h>
# include <fcntl.h>
# else // ZM_MEM_MAPPED
# include <sys/ipc.h>
# include <sys/shm.h>
# endif // ZM_MEM_MAPPED
2015-05-18 08:18:54 +08:00
// SOLARIS - we don't have MAP_LOCKED on openSolaris/illumos
# ifndef MAP_LOCKED
# define MAP_LOCKED 0
# endif
2018-03-09 20:23:40 +08:00
// This is the official SQL (and ordering of the fields) to load a Monitor.
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
std : : string load_monitor_sql =
2020-12-11 03:05:46 +08:00
" SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `DecodingEnabled`, "
" `LinkedMonitors`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`, "
2019-08-15 04:18:21 +08:00
" `Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
" `Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
" `DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
" `SaveJPEGs`, `VideoWriter`, `EncoderParameters`, "
2019-08-23 00:57:00 +08:00
" `OutputCodec`, `Encoder`, `OutputContainer`, "
2019-08-15 04:18:21 +08:00
" `RecordAudio`, "
" `Brightness`, `Contrast`, `Hue`, `Colour`, "
" `EventPrefix`, `LabelFormat`, `LabelX`, `LabelY`, `LabelSize`, "
" `ImageBufferCount`, `WarmupCount`, `PreEventCount`, `PostEventCount`, `StreamReplayBuffer`, `AlarmFrameCount`, "
" `SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, "
2021-02-03 03:21:15 +08:00
" `FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`, `RTSPServer`, `SignalCheckPoints`, `SignalCheckColour` FROM `Monitors` " ;
2018-03-09 20:23:40 +08:00
std : : string CameraType_Strings [ ] = {
" Local " ,
" Remote " ,
" File " ,
" Ffmpeg " ,
" LibVLC " ,
2018-04-14 23:03:08 +08:00
" NVSOCKET " ,
2018-03-09 20:23:40 +08:00
" CURL " ,
2020-03-26 14:21:34 +08:00
" VNC " ,
2018-03-09 20:23:40 +08:00
} ;
2018-04-12 22:28:22 +08:00
2020-07-24 07:31:12 +08:00
Monitor : : MonitorLink : : MonitorLink ( unsigned int p_id , const char * p_name ) :
id ( p_id ) ,
2020-08-26 07:45:48 +08:00
shared_data ( nullptr ) ,
trigger_data ( nullptr ) ,
video_store_data ( nullptr )
2017-11-28 22:50:09 +08:00
{
2020-07-24 07:31:12 +08:00
strncpy ( name , p_name , sizeof ( name ) - 1 ) ;
2006-01-23 02:31:55 +08:00
2008-07-14 23:21:16 +08:00
# if ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
map_fd = - 1 ;
2020-07-24 07:31:12 +08:00
snprintf ( mem_file , sizeof ( mem_file ) , " %s/zm.mmap.%d " , staticConfig . PATH_MAP . c_str ( ) , id ) ;
2008-07-14 23:21:16 +08:00
# else // ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
shm_id = 0 ;
2008-07-14 23:21:16 +08:00
# endif // ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
mem_size = 0 ;
2020-08-26 07:45:48 +08:00
mem_ptr = nullptr ;
2006-01-23 02:31:55 +08:00
2017-10-23 21:51:41 +08:00
last_event_id = 0 ;
2016-04-29 20:57:31 +08:00
last_state = IDLE ;
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
last_connect_time = 0 ;
connected = false ;
2006-01-23 02:31:55 +08:00
}
2016-04-01 00:54:56 +08:00
Monitor : : MonitorLink : : ~ MonitorLink ( ) {
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2006-01-23 02:31:55 +08:00
}
2016-04-01 00:54:56 +08:00
bool Monitor : : MonitorLink : : connect ( ) {
2020-08-26 07:45:48 +08:00
if ( ! last_connect_time | | ( time ( nullptr ) - last_connect_time ) > 60 ) {
last_connect_time = time ( nullptr ) ;
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
mem_size = sizeof ( SharedData ) + sizeof ( TriggerData ) ;
2008-07-14 23:21:16 +08:00
2020-06-22 22:39:29 +08:00
Debug ( 1 , " link.mem.size=%d " , mem_size ) ;
2008-07-14 23:21:16 +08:00
# if ZM_MEM_MAPPED
2020-06-22 22:39:29 +08:00
map_fd = open ( mem_file , O_RDWR , ( mode_t ) 0600 ) ;
2016-04-29 20:57:31 +08:00
if ( map_fd < 0 ) {
2020-06-22 22:39:29 +08:00
Debug ( 3 , " Can't open linked memory map file %s: %s " , mem_file , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
while ( map_fd < = 2 ) {
int new_map_fd = dup ( map_fd ) ;
2020-06-22 22:39:29 +08:00
Warning ( " Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d " , map_fd , new_map_fd ) ;
2016-04-29 20:57:31 +08:00
close ( map_fd ) ;
map_fd = new_map_fd ;
}
struct stat map_stat ;
2020-06-22 22:39:29 +08:00
if ( fstat ( map_fd , & map_stat ) < 0 ) {
Error ( " Can't stat linked memory map file %s: %s " , mem_file , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
if ( map_stat . st_size = = 0 ) {
2020-06-22 22:39:29 +08:00
Error ( " Linked memory map file %s is empty: %s " , mem_file , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
} else if ( map_stat . st_size < mem_size ) {
2020-06-22 22:39:29 +08:00
Error ( " Got unexpected memory map file size %ld, expected %d " , map_stat . st_size , mem_size ) ;
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
2020-08-26 07:45:48 +08:00
mem_ptr = ( unsigned char * ) mmap ( nullptr , mem_size , PROT_READ | PROT_WRITE , MAP_SHARED , map_fd , 0 ) ;
2016-04-29 20:57:31 +08:00
if ( mem_ptr = = MAP_FAILED ) {
2020-06-22 22:39:29 +08:00
Error ( " Can't map file %s (%d bytes) to memory: %s " , mem_file , mem_size , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
2008-07-14 23:21:16 +08:00
# else // ZM_MEM_MAPPED
2021-02-06 00:04:02 +08:00
shm_id = shmget ( ( config . shm_key & 0xffff0000 ) | id , mem_size , 0700 ) ;
2016-04-29 20:57:31 +08:00
if ( shm_id < 0 ) {
2021-02-06 00:04:02 +08:00
Debug ( 3 , " Can't shmget link memory: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
connected = false ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
2020-06-22 22:39:29 +08:00
mem_ptr = ( unsigned char * ) shmat ( shm_id , 0 , 0 ) ;
2017-05-11 01:16:08 +08:00
if ( mem_ptr < ( void * ) 0 ) {
2020-06-22 22:39:29 +08:00
Debug ( 3 , " Can't shmat link memory: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
connected = false ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
2008-07-14 23:21:16 +08:00
# endif // ZM_MEM_MAPPED
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
shared_data = ( SharedData * ) mem_ptr ;
trigger_data = ( TriggerData * ) ( ( char * ) shared_data + sizeof ( SharedData ) ) ;
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
if ( ! shared_data - > valid ) {
2020-06-22 22:39:29 +08:00
Debug ( 3 , " Linked memory not initialised by capture daemon " ) ;
2016-04-29 20:57:31 +08:00
disconnect ( ) ;
2020-06-22 22:39:29 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
last_state = shared_data - > state ;
2017-10-23 21:51:41 +08:00
last_event_id = shared_data - > last_event_id ;
2016-04-29 20:57:31 +08:00
connected = true ;
2006-01-23 02:31:55 +08:00
2020-06-22 22:39:29 +08:00
return true ;
2016-04-29 20:57:31 +08:00
}
2020-06-22 22:39:29 +08:00
return false ;
2018-04-14 23:03:08 +08:00
} // end bool Monitor::MonitorLink::connect()
2006-01-23 02:31:55 +08:00
2016-04-01 00:54:56 +08:00
bool Monitor : : MonitorLink : : disconnect ( ) {
2016-04-29 20:57:31 +08:00
if ( connected ) {
connected = false ;
2006-01-23 02:31:55 +08:00
2008-07-14 23:21:16 +08:00
# if ZM_MEM_MAPPED
2017-02-03 01:43:13 +08:00
if ( mem_ptr > ( void * ) 0 ) {
2016-04-29 20:57:31 +08:00
msync ( mem_ptr , mem_size , MS_ASYNC ) ;
munmap ( mem_ptr , mem_size ) ;
}
if ( map_fd > = 0 )
close ( map_fd ) ;
map_fd = - 1 ;
2008-07-14 23:21:16 +08:00
# else // ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
struct shmid_ds shm_data ;
if ( shmctl ( shm_id , IPC_STAT , & shm_data ) < 0 ) {
Debug ( 3 , " Can't shmctl: %s " , strerror ( errno ) ) ;
return ( false ) ;
}
shm_id = 0 ;
if ( shm_data . shm_nattch < = 1 ) {
if ( shmctl ( shm_id , IPC_RMID , 0 ) < 0 ) {
Debug ( 3 , " Can't shmctl: %s " , strerror ( errno ) ) ;
return ( false ) ;
}
}
if ( shmdt ( mem_ptr ) < 0 ) {
Debug ( 3 , " Can't shmdt: %s " , strerror ( errno ) ) ;
return ( false ) ;
}
2006-01-23 02:31:55 +08:00
2008-07-14 23:21:16 +08:00
# endif // ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
mem_size = 0 ;
2020-08-26 07:45:48 +08:00
mem_ptr = nullptr ;
2016-04-29 20:57:31 +08:00
}
return ( true ) ;
2006-01-23 02:31:55 +08:00
}
2016-04-01 00:54:56 +08:00
bool Monitor : : MonitorLink : : isAlarmed ( ) {
2016-04-29 20:57:31 +08:00
if ( ! connected ) {
return ( false ) ;
}
return ( shared_data - > state = = ALARM ) ;
2006-01-23 02:31:55 +08:00
}
2016-04-01 00:54:56 +08:00
bool Monitor : : MonitorLink : : inAlarm ( ) {
2016-04-29 20:57:31 +08:00
if ( ! connected ) {
return ( false ) ;
}
return ( shared_data - > state = = ALARM | | shared_data - > state = = ALERT ) ;
2006-01-23 02:31:55 +08:00
}
2016-12-09 00:49:54 +08:00
bool Monitor : : MonitorLink : : hasAlarmed ( ) {
2016-04-29 20:57:31 +08:00
if ( shared_data - > state = = ALARM ) {
2018-04-13 04:40:11 +08:00
return true ;
2016-04-29 20:57:31 +08:00
}
2018-04-14 23:03:08 +08:00
last_event_id = shared_data - > last_event_id ;
2018-04-13 04:40:11 +08:00
return false ;
2006-01-23 02:31:55 +08:00
}
2018-04-14 23:03:08 +08:00
Monitor : : Monitor ( )
: id ( 0 ) ,
name ( " " ) ,
server_id ( 0 ) ,
storage_id ( 0 ) ,
type ( LOCAL ) ,
function ( NONE ) ,
enabled ( 0 ) ,
2020-12-15 23:14:19 +08:00
decoding_enabled ( 0 ) ,
2018-04-14 23:03:08 +08:00
//protocol
//method
//options
//host
//port
//user
//pass
//path
2020-10-21 04:20:29 +08:00
//device
2020-07-23 01:14:40 +08:00
palette ( 0 ) ,
2018-04-14 23:03:08 +08:00
channel ( 0 ) ,
format ( 0 ) ,
width ( 0 ) ,
height ( 0 ) ,
//v4l_multi_buffer
//v4l_captures_per_frame
orientation ( ROTATE_0 ) ,
deinterlacing ( 0 ) ,
deinterlacing_value ( 0 ) ,
2020-05-03 06:03:42 +08:00
decoder_hwaccel_name ( " " ) ,
decoder_hwaccel_device ( " " ) ,
2018-04-14 23:03:08 +08:00
videoRecording ( 0 ) ,
rtsp_describe ( 0 ) ,
savejpegs ( 0 ) ,
colours ( 0 ) ,
videowriter ( DISABLED ) ,
encoderparams ( " " ) ,
2018-04-15 02:58:25 +08:00
output_codec ( 0 ) ,
2021-01-07 00:31:31 +08:00
encoder ( " " ) ,
2018-04-14 23:03:08 +08:00
output_container ( " " ) ,
2021-01-07 00:31:31 +08:00
imagePixFormat ( AV_PIX_FMT_NONE ) ,
subpixelorder ( 0 ) ,
2018-04-14 23:03:08 +08:00
record_audio ( 0 ) ,
//event_prefix
//label_format
label_coord ( Coord ( 0 , 0 ) ) ,
label_size ( 0 ) ,
image_buffer_count ( 0 ) ,
warmup_count ( 0 ) ,
pre_event_count ( 0 ) ,
post_event_count ( 0 ) ,
stream_replay_buffer ( 0 ) ,
section_length ( 0 ) ,
2019-08-23 00:57:00 +08:00
min_section_length ( 0 ) ,
2021-01-07 00:31:31 +08:00
adaptive_skip ( false ) ,
2018-04-14 23:03:08 +08:00
frame_skip ( 0 ) ,
motion_frame_skip ( 0 ) ,
analysis_fps_limit ( 0 ) ,
analysis_update_delay ( 0 ) ,
capture_delay ( 0 ) ,
alarm_capture_delay ( 0 ) ,
alarm_frame_count ( 0 ) ,
fps_report_interval ( 0 ) ,
ref_blend_perc ( 0 ) ,
alarm_ref_blend_perc ( 0 ) ,
track_motion ( 0 ) ,
2018-05-06 00:25:11 +08:00
signal_check_points ( 0 ) ,
2018-04-14 23:03:08 +08:00
signal_check_colour ( 0 ) ,
embed_exif ( 0 ) ,
2021-02-03 03:21:15 +08:00
rtsp_server ( 0 ) ,
2021-01-07 00:31:31 +08:00
capture_max_fps ( 0 ) ,
2018-04-14 23:03:08 +08:00
purpose ( QUERY ) ,
2020-12-24 07:50:07 +08:00
last_camera_bytes ( 0 ) ,
event_count ( 0 ) ,
image_count ( 0 ) ,
2021-02-05 00:50:01 +08:00
last_capture_image_count ( 0 ) ,
2020-12-24 07:50:07 +08:00
analysis_image_count ( 0 ) ,
motion_frame_count ( 0 ) ,
2021-02-05 00:50:01 +08:00
last_motion_frame_count ( 0 ) ,
2021-01-07 00:31:31 +08:00
ready_count ( 0 ) ,
first_alarm_count ( 0 ) ,
last_alarm_count ( 0 ) ,
last_signal ( false ) ,
last_section_mod ( 0 ) ,
buffer_count ( 0 ) ,
state ( IDLE ) ,
start_time ( 0 ) ,
last_fps_time ( 0 ) ,
last_analysis_fps_time ( 0 ) ,
2018-04-14 23:03:08 +08:00
auto_resume_time ( 0 ) ,
2016-04-29 20:57:31 +08:00
last_motion_score ( 0 ) ,
2020-12-17 23:16:54 +08:00
camera ( nullptr ) ,
2020-12-16 07:25:17 +08:00
event ( nullptr ) ,
2021-01-16 03:42:26 +08:00
storage ( nullptr ) ,
videoStore ( nullptr ) ,
analysis_it ( nullptr ) ,
2018-04-14 23:03:08 +08:00
n_zones ( 0 ) ,
2020-09-29 23:02:40 +08:00
zones ( nullptr ) ,
2020-12-08 05:26:26 +08:00
privacy_bitmask ( nullptr ) ,
2020-12-06 08:36:44 +08:00
n_linked_monitors ( 0 ) ,
linked_monitors ( nullptr )
2003-03-26 19:57:29 +08:00
{
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
if ( strcmp ( config . event_close_mode , " time " ) = = 0 )
event_close_mode = CLOSE_TIME ;
else if ( strcmp ( config . event_close_mode , " alarm " ) = = 0 )
event_close_mode = CLOSE_ALARM ;
else
event_close_mode = CLOSE_IDLE ;
2020-12-24 07:50:07 +08:00
2018-04-14 23:03:08 +08:00
event = 0 ;
last_section_mod = 0 ;
adaptive_skip = true ;
2020-09-29 23:02:40 +08:00
videoStore = nullptr ;
2021-02-01 10:30:12 +08:00
packetqueue . setMaxVideoPackets ( pre_event_count ) ;
2021-02-03 03:49:17 +08:00
} // Monitor::Monitor
2018-04-14 23:03:08 +08:00
/*
std : : string load_monitor_sql =
2020-12-15 23:14:19 +08:00
" SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, DecodingEnabled, LinkedMonitors, "
2018-04-14 23:03:08 +08:00
" AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, "
" Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
" Protocol, Method, Options, User, Pass, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, "
" SaveJPEGs, VideoWriter, EncoderParameters,
2021-01-19 22:44:53 +08:00
" OutputCodec, Encoder, OutputContainer, "
" RecordAudio, "
2018-04-14 23:03:08 +08:00
" Brightness, Contrast, Hue, Colour, "
" EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, "
" ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, "
2020-12-13 07:52:57 +08:00
" SectionLength, MinSectionLength, FrameSkip, MotionFrameSkip, "
2021-02-03 03:21:15 +08:00
" FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif, `RTSPServer`, SignalCheckPoints, SignalCheckColour FROM Monitors " ;
2018-04-14 23:03:08 +08:00
*/
void Monitor : : Load ( MYSQL_ROW dbrow , bool load_zones = true , Purpose p = QUERY ) {
purpose = p ;
int col = 0 ;
id = atoi ( dbrow [ col ] ) ; col + + ;
strncpy ( name , dbrow [ col ] , sizeof ( name ) - 1 ) ; col + + ;
server_id = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
storage_id = atoi ( dbrow [ col ] ) ; col + + ;
2021-01-19 22:44:53 +08:00
if ( storage )
delete storage ;
2018-04-14 23:03:08 +08:00
storage = new Storage ( storage_id ) ;
if ( ! strcmp ( dbrow [ col ] , " Local " ) ) {
type = LOCAL ;
} else if ( ! strcmp ( dbrow [ col ] , " Ffmpeg " ) ) {
type = FFMPEG ;
} else if ( ! strcmp ( dbrow [ col ] , " Remote " ) ) {
type = REMOTE ;
} else if ( ! strcmp ( dbrow [ col ] , " File " ) ) {
type = FILE ;
} else if ( ! strcmp ( dbrow [ col ] , " NVSocket " ) ) {
type = NVSOCKET ;
} else if ( ! strcmp ( dbrow [ col ] , " Libvlc " ) ) {
type = LIBVLC ;
} else if ( ! strcmp ( dbrow [ col ] , " cURL " ) ) {
type = CURL ;
} else {
Fatal ( " Bogus monitor type '%s' for monitor %d " , dbrow [ col ] , id ) ;
}
2021-01-19 22:44:53 +08:00
Debug ( 1 , " Have camera type %s " , CameraType_Strings [ type ] . c_str ( ) ) ;
2018-04-14 23:03:08 +08:00
col + + ;
function = ( Function ) atoi ( dbrow [ col ] ) ; col + + ;
enabled = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
2020-12-15 23:14:19 +08:00
decoding_enabled = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
2018-04-14 23:03:08 +08:00
ReloadLinkedMonitors ( dbrow [ col ] ) ; col + + ;
2021-02-02 05:01:09 +08:00
analysis_fps_limit = dbrow [ col ] ? strtod ( dbrow [ col ] , nullptr ) : 0.0 ; col + + ;
analysis_update_delay = strtoul ( dbrow [ col + + ] , nullptr , 0 ) ;
2021-01-19 22:44:53 +08:00
capture_delay = ( dbrow [ col ] & & atof ( dbrow [ col ] ) > 0.0 ) ? int ( DT_PREC_3 / atof ( dbrow [ col ] ) ) : 0 ; col + + ;
alarm_capture_delay = ( dbrow [ col ] & & atof ( dbrow [ col ] ) > 0.0 ) ? int ( DT_PREC_3 / atof ( dbrow [ col ] ) ) : 0 ; col + + ;
2018-04-14 23:03:08 +08:00
if ( dbrow [ col ] )
strncpy ( device , dbrow [ col ] , sizeof ( device ) - 1 ) ;
else
device [ 0 ] = 0 ;
col + + ;
channel = atoi ( dbrow [ col ] ) ; col + + ;
format = atoi ( dbrow [ col ] ) ; col + + ;
v4l_multi_buffer = config . v4l_multi_buffer ;
if ( dbrow [ col ] ) {
if ( * dbrow [ col ] = = ' 0 ' ) {
v4l_multi_buffer = false ;
} else if ( * dbrow [ col ] = = ' 1 ' ) {
v4l_multi_buffer = true ;
}
}
col + + ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
v4l_captures_per_frame = 0 ;
if ( dbrow [ col ] ) {
v4l_captures_per_frame = atoi ( dbrow [ col ] ) ;
} else {
v4l_captures_per_frame = config . captures_per_frame ;
}
col + + ;
protocol = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
method = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
options = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
user = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
pass = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
host = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
port = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
path = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
camera_width = atoi ( dbrow [ col ] ) ; col + + ;
camera_height = atoi ( dbrow [ col ] ) ; col + + ;
colours = atoi ( dbrow [ col ] ) ; col + + ;
palette = atoi ( dbrow [ col ] ) ; col + + ;
orientation = ( Orientation ) atoi ( dbrow [ col ] ) ; col + + ;
width = ( orientation = = ROTATE_90 | | orientation = = ROTATE_270 ) ? camera_height : camera_width ;
height = ( orientation = = ROTATE_90 | | orientation = = ROTATE_270 ) ? camera_width : camera_height ;
deinterlacing = atoi ( dbrow [ col ] ) ; col + + ;
deinterlacing_value = deinterlacing & 0xff ;
2019-08-23 00:57:00 +08:00
decoder_hwaccel_name = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
decoder_hwaccel_device = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2018-04-14 23:03:08 +08:00
rtsp_describe = ( dbrow [ col ] & & * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
savejpegs = atoi ( dbrow [ col ] ) ; col + + ;
videowriter = ( VideoWriter ) atoi ( dbrow [ col ] ) ; col + + ;
encoderparams = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
/* Parse encoder parameters */
ParseEncoderParameters ( encoderparams . c_str ( ) , & encoderparamsvec ) ;
2018-04-15 02:58:25 +08:00
output_codec = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
encoder = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2018-04-14 23:03:08 +08:00
output_container = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
record_audio = ( * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
brightness = atoi ( dbrow [ col ] ) ; col + + ;
contrast = atoi ( dbrow [ col ] ) ; col + + ;
hue = atoi ( dbrow [ col ] ) ; col + + ;
colour = atoi ( dbrow [ col ] ) ; col + + ;
if ( dbrow [ col ] )
strncpy ( event_prefix , dbrow [ col ] , sizeof ( event_prefix ) - 1 ) ;
else
event_prefix [ 0 ] = 0 ;
col + + ;
if ( dbrow [ col ] )
strncpy ( label_format , dbrow [ col ] , sizeof ( label_format ) - 1 ) ;
else
label_format [ 0 ] = 0 ;
col + + ;
2016-04-29 20:57:31 +08:00
// Change \n to actual line feeds
char * token_ptr = label_format ;
const char * token_string = " \n " ;
2019-04-16 01:26:55 +08:00
while ( ( token_ptr = strstr ( token_ptr , token_string ) ) ) {
2016-12-09 00:49:54 +08:00
if ( * ( token_ptr + 1 ) ) {
2016-04-29 20:57:31 +08:00
* token_ptr = ' \n ' ;
token_ptr + + ;
2019-04-16 01:26:55 +08:00
strcpy ( token_ptr , token_ptr + 1 ) ;
2016-12-09 00:49:54 +08:00
} else {
2016-04-29 20:57:31 +08:00
* token_ptr = ' \0 ' ;
break ;
}
}
2018-04-14 23:03:08 +08:00
label_coord = Coord ( atoi ( dbrow [ col ] ) , atoi ( dbrow [ col + 1 ] ) ) ; col + = 2 ;
label_size = atoi ( dbrow [ col ] ) ; col + + ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
image_buffer_count = atoi ( dbrow [ col ] ) ; col + + ;
warmup_count = atoi ( dbrow [ col ] ) ; col + + ;
pre_event_count = atoi ( dbrow [ col ] ) ; col + + ;
post_event_count = atoi ( dbrow [ col ] ) ; col + + ;
stream_replay_buffer = atoi ( dbrow [ col ] ) ; col + + ;
alarm_frame_count = atoi ( dbrow [ col ] ) ; col + + ;
2016-04-29 20:57:31 +08:00
if ( alarm_frame_count < 1 )
alarm_frame_count = 1 ;
else if ( alarm_frame_count > MAX_PRE_ALARM_FRAMES )
alarm_frame_count = MAX_PRE_ALARM_FRAMES ;
2018-04-14 23:03:08 +08:00
pre_event_buffer_count = pre_event_count + alarm_frame_count + warmup_count - 1 ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
section_length = atoi ( dbrow [ col ] ) ; col + + ;
2020-12-13 07:52:57 +08:00
min_section_length = atoi ( dbrow [ col ] ) ; col + + ;
2018-04-14 23:03:08 +08:00
frame_skip = atoi ( dbrow [ col ] ) ; col + + ;
motion_frame_skip = atoi ( dbrow [ col ] ) ; col + + ;
fps_report_interval = atoi ( dbrow [ col ] ) ; col + + ;
ref_blend_perc = atoi ( dbrow [ col ] ) ; col + + ;
alarm_ref_blend_perc = atoi ( dbrow [ col ] ) ; col + + ;
track_motion = atoi ( dbrow [ col ] ) ; col + + ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
embed_exif = ( * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
2021-02-03 03:21:15 +08:00
rtsp_server = ( * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
2021-02-03 03:49:17 +08:00
signal_check_points = atoi ( dbrow [ col ] ) ; col + + ;
signal_check_colour = strtol ( dbrow [ col ] [ 0 ] = = ' # ' ? dbrow [ col ] + 1 : dbrow [ col ] , 0 , 16 ) ; col + + ;
2017-10-28 11:36:49 +08:00
2017-12-02 03:33:51 +08:00
// How many frames we need to have before we start analysing
2021-02-11 02:54:13 +08:00
ready_count = std : : max ( warmup_count , pre_event_count ) ;
2016-04-29 20:57:31 +08:00
last_alarm_count = 0 ;
state = IDLE ;
2021-01-07 00:31:31 +08:00
last_signal = true ; // Defaulting to having signal so that we don't get a signal change on the first frame.
// Instead initial failure to capture will cause a loss of signal change which I think makes more sense.
2020-05-03 06:03:42 +08:00
uint64_t image_size = width * height * colours ;
2016-04-29 20:57:31 +08:00
2020-04-27 06:19:30 +08:00
if ( strcmp ( config . event_close_mode , " time " ) = = 0 )
2016-04-29 20:57:31 +08:00
event_close_mode = CLOSE_TIME ;
2020-04-27 06:19:30 +08:00
else if ( strcmp ( config . event_close_mode , " alarm " ) = = 0 )
2016-04-29 20:57:31 +08:00
event_close_mode = CLOSE_ALARM ;
else
event_close_mode = CLOSE_IDLE ;
mem_size = sizeof ( SharedData )
+ sizeof ( TriggerData )
+ sizeof ( VideoStoreData ) //Information to pass back to the capture process
2018-04-14 23:03:08 +08:00
+ ( image_buffer_count * sizeof ( struct timeval ) )
+ ( image_buffer_count * width * height * colours )
2017-04-16 15:57:37 +08:00
+ 64 ; /* Padding used to permit aligning the images buffer to 64 byte boundary */
2016-04-29 20:57:31 +08:00
2019-08-12 08:21:37 +08:00
Debug ( 1 , " mem.size(%d) SharedData=%d TriggerData=%d VideoStoreData=%d timestamps=%d images=%dx%d = % " PRId64 " total=% " PRId64 ,
sizeof ( mem_size ) ,
sizeof ( SharedData ) , sizeof ( TriggerData ) , sizeof ( VideoStoreData ) ,
( image_buffer_count * sizeof ( struct timeval ) ) ,
2020-05-03 06:03:42 +08:00
image_buffer_count , image_size , ( image_buffer_count * image_size ) ,
2019-08-12 08:21:37 +08:00
mem_size ) ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
Zone * * zones = 0 ;
int n_zones = Zone : : Load ( this , zones ) ;
this - > AddZones ( n_zones , zones ) ;
this - > AddPrivacyBitmask ( zones ) ;
2020-11-14 01:27:51 +08:00
2017-01-18 22:31:08 +08:00
// Should maybe store this for later use
2020-07-23 01:14:40 +08:00
std : : string monitor_dir = stringtf ( " %s/%d " , storage - > Path ( ) , id ) ;
2021-02-11 06:18:21 +08:00
LoadCamera ( ) ;
2016-05-07 00:39:10 +08:00
2021-01-07 22:40:24 +08:00
if ( purpose ! = QUERY ) {
2020-07-23 01:14:40 +08:00
if ( mkdir ( monitor_dir . c_str ( ) , 0755 ) & & ( errno ! = EEXIST ) ) {
Error ( " Can't mkdir %s: %s " , monitor_dir . c_str ( ) , strerror ( errno ) ) ;
2017-08-12 00:03:37 +08:00
}
2018-03-09 20:23:40 +08:00
2020-12-11 05:23:08 +08:00
// Do this here to save a few cycles with all the comparisons
decoding_enabled = ! (
( function = = RECORD or function = = NODECT )
and
( savejpegs = = 0 )
and
2021-01-07 00:31:31 +08:00
( videowriter = = PASSTHROUGH )
2020-12-11 05:23:08 +08:00
and
! decoding_enabled
) ;
Debug ( 1 , " Decoding enabled: %d " , decoding_enabled ) ;
2020-11-14 01:27:51 +08:00
if ( config . record_diag_images ) {
if ( config . record_diag_images_fifo ) {
2020-11-14 06:05:03 +08:00
diag_path_ref = stringtf ( " %s/diagpipe-r-%d.jpg " , staticConfig . PATH_SOCKS . c_str ( ) , id ) ;
diag_path_delta = stringtf ( " %s/diagpipe-d-%d.jpg " , staticConfig . PATH_SOCKS . c_str ( ) , id ) ;
2020-11-14 01:27:51 +08:00
FifoStream : : fifo_create_if_missing ( diag_path_ref . c_str ( ) ) ;
FifoStream : : fifo_create_if_missing ( diag_path_delta . c_str ( ) ) ;
} else {
diag_path_ref = stringtf ( " %s/%d/diag-r.jpg " , storage - > Path ( ) , id ) ;
diag_path_delta = stringtf ( " %s/%d/diag-d.jpg " , storage - > Path ( ) , id ) ;
}
2020-04-27 06:19:30 +08:00
}
2020-12-10 04:00:11 +08:00
} // end if purpose
2016-11-28 09:10:12 +08:00
2018-04-14 23:03:08 +08:00
Debug ( 1 , " Loaded monitor %d(%s), %d zones " , id , name , n_zones ) ;
} // Monitor::Load
2016-05-07 00:39:10 +08:00
2021-02-11 06:18:21 +08:00
void Monitor : : LoadCamera ( ) {
2021-02-12 03:02:08 +08:00
if ( camera )
2021-02-11 06:18:21 +08:00
return ;
2016-04-29 20:57:31 +08:00
2021-02-12 03:02:08 +08:00
switch ( type ) {
case LOCAL : {
int extras = ( deinterlacing > > 24 ) & 0xff ;
camera = ZM : : make_unique < LocalCamera > ( this ,
device ,
channel ,
format ,
v4l_multi_buffer ,
v4l_captures_per_frame ,
method ,
camera_width ,
camera_height ,
colours ,
palette ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio ,
extras
2018-04-14 23:03:08 +08:00
) ;
2021-02-12 03:02:08 +08:00
break ;
2016-04-29 20:57:31 +08:00
}
2021-02-12 03:02:08 +08:00
case REMOTE : {
if ( protocol = = " http " ) {
camera = ZM : : make_unique < RemoteCameraHttp > ( this ,
method ,
host ,
port ,
path ,
camera_width ,
camera_height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
}
2018-04-14 23:03:08 +08:00
# if HAVE_LIBAVFORMAT
2021-02-12 03:02:08 +08:00
else if ( protocol = = " rtsp " ) {
camera = ZM : : make_unique < RemoteCameraRtsp > ( this ,
method ,
host , // Host
port , // Port
path , // Path
camera_width ,
camera_height ,
rtsp_describe ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
}
2018-04-14 23:03:08 +08:00
# endif // HAVE_LIBAVFORMAT
2021-02-12 03:02:08 +08:00
else {
Error ( " Unexpected remote camera protocol '%s' " , protocol . c_str ( ) ) ;
}
break ;
}
case FILE : {
camera = ZM : : make_unique < FileCamera > ( this ,
path . c_str ( ) ,
camera_width ,
camera_height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
break ;
2018-04-14 23:03:08 +08:00
}
# if HAVE_LIBAVFORMAT
2021-02-12 03:02:08 +08:00
case FFMPEG : {
camera = ZM : : make_unique < FfmpegCamera > ( this ,
path ,
method ,
options ,
camera_width ,
camera_height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio ,
decoder_hwaccel_name ,
decoder_hwaccel_device
) ;
break ;
}
2018-04-14 23:03:08 +08:00
# endif // HAVE_LIBAVFORMAT
2021-02-12 03:02:08 +08:00
case NVSOCKET : {
camera = ZM : : make_unique < RemoteCameraNVSocket > ( this ,
host . c_str ( ) ,
port . c_str ( ) ,
path . c_str ( ) ,
camera_width ,
camera_height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
2017-10-23 21:51:41 +08:00
) ;
2021-02-12 03:02:08 +08:00
break ;
}
case LIBVLC : {
2018-04-14 23:03:08 +08:00
# if HAVE_LIBVLC
2021-02-12 03:02:08 +08:00
camera = ZM : : make_unique < LibvlcCamera > ( this ,
path . c_str ( ) ,
method ,
options ,
camera_width ,
camera_height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2018-04-14 23:03:08 +08:00
# else // HAVE_LIBVLC
2021-02-12 03:02:08 +08:00
Error ( " You must have vlc libraries installed to use vlc cameras for monitor %d " , id ) ;
2018-04-14 23:03:08 +08:00
# endif // HAVE_LIBVLC
2021-02-12 03:02:08 +08:00
break ;
}
case CURL : {
2018-04-14 23:03:08 +08:00
# if HAVE_LIBCURL
2021-02-12 03:02:08 +08:00
camera = ZM : : make_unique < cURLCamera > ( this ,
path . c_str ( ) ,
user . c_str ( ) ,
pass . c_str ( ) ,
camera_width ,
camera_height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2018-04-14 23:03:08 +08:00
# else // HAVE_LIBCURL
2021-02-12 03:02:08 +08:00
Error ( " You must have libcurl installed to use ffmpeg cameras for monitor %d " , id ) ;
2018-04-14 23:03:08 +08:00
# endif // HAVE_LIBCURL
2021-02-12 03:02:08 +08:00
break ;
}
case VNC : {
2020-12-08 05:26:26 +08:00
# if HAVE_LIBVNC
2021-02-12 03:02:08 +08:00
camera = ZM : : make_unique < VncCamera > ( this ,
host . c_str ( ) ,
port . c_str ( ) ,
user . c_str ( ) ,
pass . c_str ( ) ,
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2020-12-08 05:26:26 +08:00
# else // HAVE_LIBVNC
2021-02-12 03:02:08 +08:00
Fatal ( " You must have libvnc installed to use VNC cameras for monitor id %d " , id ) ;
2020-12-08 05:26:26 +08:00
# endif // HAVE_LIBVNC
2021-02-12 03:02:08 +08:00
break ;
}
default : {
Fatal ( " Tried to load unsupported camera type %d for monitor %u " , int ( type ) , id ) ;
break ;
}
}
2021-02-11 06:18:21 +08:00
}
2016-04-29 20:57:31 +08:00
2021-02-08 02:12:39 +08:00
std : : shared_ptr < Monitor > Monitor : : Load ( unsigned int p_id , bool load_zones , Purpose purpose ) {
2018-04-14 23:03:08 +08:00
std : : string sql = load_monitor_sql + stringtf ( " WHERE Id=%d " , p_id ) ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
zmDbRow dbrow ;
if ( ! dbrow . fetch ( sql . c_str ( ) ) ) {
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
2021-02-02 05:01:09 +08:00
return nullptr ;
2018-04-14 23:03:08 +08:00
}
2021-02-08 02:12:39 +08:00
std : : shared_ptr < Monitor > monitor = std : : make_shared < Monitor > ( ) ;
2019-02-26 22:45:20 +08:00
monitor - > Load ( dbrow . mysql_row ( ) , load_zones , purpose ) ;
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
return monitor ;
2021-02-08 02:12:39 +08:00
}
2003-03-26 19:57:29 +08:00
2014-06-27 02:54:47 +08:00
bool Monitor : : connect ( ) {
2020-05-03 06:03:42 +08:00
Debug ( 3 , " Connecting to monitor. Purpose is %d " , purpose ) ;
2014-06-27 02:54:47 +08:00
# if ZM_MEM_MAPPED
2018-12-28 00:50:43 +08:00
snprintf ( mem_file , sizeof ( mem_file ) , " %s/zm.mmap.%d " , staticConfig . PATH_MAP . c_str ( ) , id ) ;
2021-01-23 01:11:19 +08:00
if ( purpose ! = CAPTURE ) {
map_fd = open ( mem_file , O_RDWR ) ;
} else {
map_fd = open ( mem_file , O_RDWR | O_CREAT , ( mode_t ) 0660 ) ;
}
2017-08-12 00:03:37 +08:00
if ( map_fd < 0 ) {
2020-05-15 00:01:24 +08:00
Error ( " Can't open memory map file %s, probably not enough space free: %s " , mem_file , strerror ( errno ) ) ;
return false ;
2017-08-12 00:03:37 +08:00
} else {
2018-12-28 00:50:43 +08:00
Debug ( 3 , " Success opening mmap file at (%s) " , mem_file ) ;
2017-08-12 00:03:37 +08:00
}
2016-04-29 20:57:31 +08:00
struct stat map_stat ;
2020-05-15 00:01:24 +08:00
if ( fstat ( map_fd , & map_stat ) < 0 ) {
Error ( " Can't stat memory map file %s: %s, is the zmc process for this monitor running? " , mem_file , strerror ( errno ) ) ;
close ( map_fd ) ;
return false ;
}
2017-08-12 00:03:37 +08:00
if ( map_stat . st_size ! = mem_size ) {
if ( purpose = = CAPTURE ) {
// Allocate the size
2018-12-28 00:50:43 +08:00
if ( ftruncate ( map_fd , mem_size ) < 0 ) {
Fatal ( " Can't extend memory map file %s to %d bytes: %s " , mem_file , mem_size , strerror ( errno ) ) ;
2017-08-12 00:03:37 +08:00
}
} else if ( map_stat . st_size = = 0 ) {
2018-12-28 00:50:43 +08:00
Error ( " Got empty memory map file size %ld, is the zmc process for this monitor running? " , map_stat . st_size , mem_size ) ;
2018-12-28 00:53:21 +08:00
close ( map_fd ) ;
map_fd = - 1 ;
2017-08-12 00:03:37 +08:00
return false ;
} else {
2018-12-28 00:50:43 +08:00
Error ( " Got unexpected memory map file size %ld, expected %d " , map_stat . st_size , mem_size ) ;
2018-12-28 00:53:21 +08:00
close ( map_fd ) ;
map_fd = - 1 ;
2017-08-12 00:03:37 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
2017-08-12 00:03:37 +08:00
}
2018-12-28 00:50:43 +08:00
Debug ( 3 , " MMap file size is %ld " , map_stat . st_size ) ;
2015-04-21 00:53:02 +08:00
# ifdef MAP_LOCKED
2020-08-26 07:45:48 +08:00
mem_ptr = ( unsigned char * ) mmap ( nullptr , mem_size , PROT_READ | PROT_WRITE , MAP_SHARED | MAP_LOCKED , map_fd , 0 ) ;
2017-08-12 00:03:37 +08:00
if ( mem_ptr = = MAP_FAILED ) {
if ( errno = = EAGAIN ) {
2018-12-28 00:50:43 +08:00
Debug ( 1 , " Unable to map file %s (%d bytes) to locked memory, trying unlocked " , mem_file , mem_size ) ;
2015-04-21 00:53:02 +08:00
# endif
2020-08-26 07:45:48 +08:00
mem_ptr = ( unsigned char * ) mmap ( nullptr , mem_size , PROT_READ | PROT_WRITE , MAP_SHARED , map_fd , 0 ) ;
2018-12-28 00:50:43 +08:00
Debug ( 1 , " Mapped file %s (%d bytes) to unlocked memory " , mem_file , mem_size ) ;
2015-04-21 00:53:02 +08:00
# ifdef MAP_LOCKED
2017-08-12 00:03:37 +08:00
} else {
2018-12-28 00:50:43 +08:00
Error ( " Unable to map file %s (%d bytes) to locked memory (%s) " , mem_file , mem_size , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
}
2017-08-12 00:03:37 +08:00
}
2015-04-21 00:53:02 +08:00
# endif
2020-11-14 01:27:51 +08:00
if ( ( mem_ptr = = MAP_FAILED ) or ( mem_ptr = = nullptr ) ) {
Error ( " Can't map file %s (%d bytes) to memory: %s(%d) " , mem_file , mem_size , strerror ( errno ) , errno ) ;
close ( map_fd ) ;
map_fd = - 1 ;
mem_ptr = nullptr ;
return false ;
2016-04-29 20:57:31 +08:00
}
2014-06-27 02:54:47 +08:00
# else // ZM_MEM_MAPPED
2018-12-28 00:50:43 +08:00
shm_id = shmget ( ( config . shm_key & 0xffff0000 ) | id , mem_size , IPC_CREAT | 0700 ) ;
2016-04-29 20:57:31 +08:00
if ( shm_id < 0 ) {
2018-12-28 00:50:43 +08:00
Fatal ( " Can't shmget, probably not enough shared memory space free: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
}
2019-08-12 08:21:37 +08:00
mem_ptr = ( unsigned char * ) shmat ( shm_id , 0 , 0 ) ;
2017-05-11 01:16:08 +08:00
if ( mem_ptr < ( void * ) 0 ) {
2018-12-28 00:50:43 +08:00
Fatal ( " Can't shmat: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
}
2014-06-27 02:54:47 +08:00
# endif // ZM_MEM_MAPPED
2021-01-09 04:49:09 +08:00
2016-04-29 20:57:31 +08:00
shared_data = ( SharedData * ) mem_ptr ;
trigger_data = ( TriggerData * ) ( ( char * ) shared_data + sizeof ( SharedData ) ) ;
video_store_data = ( VideoStoreData * ) ( ( char * ) trigger_data + sizeof ( TriggerData ) ) ;
2017-11-22 08:55:40 +08:00
shared_timestamps = ( struct timeval * ) ( ( char * ) video_store_data + sizeof ( VideoStoreData ) ) ;
shared_images = ( unsigned char * ) ( ( char * ) shared_timestamps + ( image_buffer_count * sizeof ( struct timeval ) ) ) ;
2021-02-01 23:24:58 +08:00
#if 0
if ( analysis_it ) {
packetqueue . free_it ( analysis_it ) ;
analysis_it = nullptr ;
}
# endif
2017-08-12 00:03:37 +08:00
if ( ( ( unsigned long ) shared_images % 64 ) ! = 0 ) {
2017-04-16 15:57:37 +08:00
/* Align images buffer to nearest 64 byte boundary */
Debug ( 3 , " Aligning shared memory images to the next 64 byte boundary " ) ;
shared_images = ( uint8_t * ) ( ( unsigned long ) shared_images + ( 64 - ( ( unsigned long ) shared_images % 64 ) ) ) ;
2016-04-29 20:57:31 +08:00
}
2021-01-09 04:49:09 +08:00
if ( ! camera )
2021-02-11 06:18:21 +08:00
LoadCamera ( ) ;
2017-10-07 23:30:41 +08:00
2018-12-28 00:50:43 +08:00
Debug ( 3 , " Allocating %d image buffers " , image_buffer_count ) ;
2017-11-14 01:14:57 +08:00
image_buffer = new ZMPacket [ image_buffer_count ] ;
2018-02-24 08:01:42 +08:00
for ( int i = 0 ; i < image_buffer_count ; i + + ) {
2017-11-22 08:55:40 +08:00
image_buffer [ i ] . image_index = i ;
2018-02-24 08:01:42 +08:00
image_buffer [ i ] . timestamp = & ( shared_timestamps [ i ] ) ;
2020-08-06 23:57:35 +08:00
image_buffer [ i ] . image = new Image ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) , & ( shared_images [ i * camera - > ImageSize ( ) ] ) ) ;
2018-02-24 08:01:42 +08:00
image_buffer [ i ] . image - > HoldBuffer ( true ) ; /* Don't release the internal buffer or replace it with another */
}
2017-12-06 05:16:52 +08:00
if ( deinterlacing_value = = 4 ) {
2018-02-24 08:01:42 +08:00
/* Four field motion adaptive deinterlacing in use */
/* Allocate a buffer for the next image */
2017-10-07 23:30:41 +08:00
next_buffer . image = new Image ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ) ;
}
2017-11-22 08:55:40 +08:00
2018-04-14 23:03:08 +08:00
if ( purpose = = CAPTURE ) {
2018-08-18 04:05:02 +08:00
memset ( mem_ptr , 0 , mem_size ) ;
2018-04-14 23:03:08 +08:00
shared_data - > size = sizeof ( SharedData ) ;
Debug ( 1 , " shared.size=%d " , shared_data - > size ) ;
shared_data - > active = enabled ;
shared_data - > signal = false ;
2020-12-10 04:00:11 +08:00
shared_data - > capture_fps = 0.0 ;
shared_data - > analysis_fps = 0.0 ;
2018-04-14 23:03:08 +08:00
shared_data - > state = IDLE ;
shared_data - > last_write_index = image_buffer_count ;
shared_data - > last_read_index = image_buffer_count ;
shared_data - > last_write_time = 0 ;
shared_data - > last_event_id = 0 ;
shared_data - > action = ( Action ) 0 ;
shared_data - > brightness = - 1 ;
shared_data - > hue = - 1 ;
shared_data - > colour = - 1 ;
shared_data - > contrast = - 1 ;
shared_data - > alarm_x = - 1 ;
shared_data - > alarm_y = - 1 ;
shared_data - > format = camera - > SubpixelOrder ( ) ;
shared_data - > imagesize = camera - > ImageSize ( ) ;
shared_data - > alarm_cause [ 0 ] = 0 ;
2020-12-30 01:18:26 +08:00
shared_data - > last_frame_score = 0 ;
2018-04-14 23:03:08 +08:00
trigger_data - > size = sizeof ( TriggerData ) ;
trigger_data - > trigger_state = TRIGGER_CANCEL ;
trigger_data - > trigger_score = 0 ;
trigger_data - > trigger_cause [ 0 ] = 0 ;
trigger_data - > trigger_text [ 0 ] = 0 ;
trigger_data - > trigger_showtext [ 0 ] = 0 ;
shared_data - > valid = true ;
video_store_data - > recording = ( struct timeval ) { 0 } ;
2021-02-02 05:01:09 +08:00
// Uh, why nothing? Why not nullptr?
2018-04-14 23:03:08 +08:00
snprintf ( video_store_data - > event_file , sizeof ( video_store_data - > event_file ) , " nothing " ) ;
video_store_data - > size = sizeof ( VideoStoreData ) ;
//video_store_data->frameNumber = 0;
}
2021-02-02 05:01:09 +08:00
if ( ( ! mem_ptr ) or ! shared_data - > valid ) {
2018-04-14 23:03:08 +08:00
if ( purpose ! = QUERY ) {
Error ( " Shared data not initialised by capture daemon for monitor %s " , name ) ;
exit ( - 1 ) ;
2018-03-09 20:50:47 +08:00
}
2018-02-24 08:01:42 +08:00
}
2020-12-24 07:50:07 +08:00
// We set these here because otherwise the first fps calc is meaningless
struct timeval now ;
2021-02-02 05:01:09 +08:00
gettimeofday ( & now , nullptr ) ;
2020-12-24 07:50:07 +08:00
double now_double = ( double ) now . tv_sec + ( 0.000001f * now . tv_usec ) ;
last_fps_time = now_double ;
last_analysis_fps_time = now_double ;
2020-11-18 22:56:13 +08:00
Debug ( 3 , " Success connecting " ) ;
2016-04-29 20:57:31 +08:00
return true ;
2017-10-07 23:30:41 +08:00
} // Monitor::connect
2014-06-27 02:54:47 +08:00
2020-11-14 01:27:51 +08:00
bool Monitor : : disconnect ( ) {
if ( ! mem_ptr )
return true ;
# if ZM_MEM_MAPPED
if ( mem_ptr > ( void * ) 0 ) {
msync ( mem_ptr , mem_size , MS_ASYNC ) ;
munmap ( mem_ptr , mem_size ) ;
}
if ( map_fd > = 0 )
close ( map_fd ) ;
map_fd = - 1 ;
2021-02-09 02:47:40 +08:00
mem_ptr = nullptr ;
shared_data = nullptr ;
2020-11-14 01:27:51 +08:00
if ( purpose = = CAPTURE ) {
if ( unlink ( mem_file ) < 0 ) {
Warning ( " Can't unlink '%s': %s " , mem_file , strerror ( errno ) ) ;
}
}
# else // ZM_MEM_MAPPED
struct shmid_ds shm_data ;
if ( shmctl ( shm_id , IPC_STAT , & shm_data ) < 0 ) {
Debug ( 3 , " Can't shmctl: %s " , strerror ( errno ) ) ;
return false ;
}
shm_id = 0 ;
if ( shm_data . shm_nattch < = 1 ) {
if ( shmctl ( shm_id , IPC_RMID , 0 ) < 0 ) {
Debug ( 3 , " Can't shmctl: %s " , strerror ( errno ) ) ;
return false ;
}
}
if ( shmdt ( mem_ptr ) < 0 ) {
Debug ( 3 , " Can't shmdt: %s " , strerror ( errno ) ) ;
return false ;
}
# endif // ZM_MEM_MAPPED
2021-02-04 06:04:40 +08:00
if ( event ) {
Info ( " %s: image_count:%d - Closing event % " PRIu64 " , shutting down " , name , image_count , event - > Id ( ) ) ;
closeEvent ( ) ;
}
2020-11-19 23:43:02 +08:00
if ( image_buffer ) {
for ( int i = 0 ; i < image_buffer_count ; i + + ) {
2021-01-09 04:49:09 +08:00
// We delete the image because it is an object pointing to space that won't be free'd.
2020-11-19 23:43:02 +08:00
delete image_buffer [ i ] . image ;
image_buffer [ i ] . image = nullptr ;
2021-01-09 04:49:09 +08:00
// We don't delete the timestamp because it is just a pointer to shared mem.
image_buffer [ i ] . timestamp = nullptr ;
2020-11-19 23:43:02 +08:00
}
delete [ ] image_buffer ;
image_buffer = nullptr ;
}
2021-02-09 02:47:40 +08:00
2020-11-14 01:27:51 +08:00
return true ;
} // end bool Monitor::disconnect()
2016-06-22 02:02:36 +08:00
Monitor : : ~ Monitor ( ) {
2021-02-11 05:21:47 +08:00
Close ( ) ;
2016-04-29 20:57:31 +08:00
if ( mem_ptr ) {
2016-09-13 21:36:45 +08:00
if ( event ) {
2018-04-18 01:51:20 +08:00
Info ( " %s: image_count:%d - Closing event % " PRIu64 " , shutting down " , name , image_count , event - > Id ( ) ) ;
2016-09-13 21:36:45 +08:00
closeEvent ( ) ;
}
2016-06-22 02:02:36 +08:00
if ( purpose = = ANALYSIS ) {
2016-04-29 20:57:31 +08:00
shared_data - > state = state = IDLE ;
2017-11-18 05:49:01 +08:00
// I think we set it to the count so that it is technically 1 behind capture, which starts at 0
2016-04-29 20:57:31 +08:00
shared_data - > last_read_index = image_buffer_count ;
shared_data - > last_read_time = 0 ;
2020-07-21 09:09:07 +08:00
if ( Event : : PreAlarmCount ( ) )
Event : : EmptyPreAlarmFrames ( ) ;
2016-06-22 02:02:36 +08:00
} else if ( purpose = = CAPTURE ) {
2016-04-29 20:57:31 +08:00
shared_data - > valid = false ;
2020-07-21 09:09:07 +08:00
memset ( mem_ptr , 0 , mem_size ) ;
2020-11-20 05:39:14 +08:00
if ( ( deinterlacing & 0xff ) = = 4 ) {
delete next_buffer . image ;
delete next_buffer . timestamp ;
}
2016-04-29 20:57:31 +08:00
}
2020-11-14 01:27:51 +08:00
disconnect ( ) ;
} // end if mem_ptr
2006-01-23 02:31:55 +08:00
2021-01-20 02:43:25 +08:00
if ( analysis_it ) {
2021-02-01 23:24:58 +08:00
packetqueue . free_it ( analysis_it ) ;
2021-01-20 02:43:25 +08:00
analysis_it = nullptr ;
}
2018-04-15 02:58:25 +08:00
2020-11-14 01:27:51 +08:00
for ( int i = 0 ; i < n_zones ; i + + ) {
delete zones [ i ] ;
}
delete [ ] zones ;
2016-11-14 06:40:54 +08:00
2020-11-14 01:27:51 +08:00
delete storage ;
2021-02-01 00:57:43 +08:00
if ( n_linked_monitors ) {
for ( int i = 0 ; i < n_linked_monitors ; i + + ) {
delete linked_monitors [ i ] ;
}
delete [ ] linked_monitors ;
linked_monitors = nullptr ;
}
2020-11-14 01:27:51 +08:00
} // end Monitor::~Monitor()
2006-01-23 02:31:55 +08:00
2018-03-04 05:28:01 +08:00
void Monitor : : AddZones ( int p_n_zones , Zone * p_zones [ ] ) {
for ( int i = 0 ; i < n_zones ; i + + )
2016-04-29 20:57:31 +08:00
delete zones [ i ] ;
delete [ ] zones ;
n_zones = p_n_zones ;
zones = p_zones ;
2003-03-26 19:57:29 +08:00
}
2018-03-04 05:28:01 +08:00
void Monitor : : AddPrivacyBitmask ( Zone * p_zones [ ] ) {
2017-12-13 01:52:30 +08:00
if ( privacy_bitmask ) {
2016-04-29 20:57:31 +08:00
delete [ ] privacy_bitmask ;
2020-08-26 07:45:48 +08:00
privacy_bitmask = nullptr ;
2017-12-13 01:52:30 +08:00
}
2020-08-26 07:45:48 +08:00
Image * privacy_image = nullptr ;
2016-04-29 20:57:31 +08:00
2018-03-04 05:28:01 +08:00
for ( int i = 0 ; i < n_zones ; i + + ) {
2016-06-22 02:02:36 +08:00
if ( p_zones [ i ] - > IsPrivacy ( ) ) {
if ( ! privacy_image ) {
2018-03-04 05:28:01 +08:00
privacy_image = new Image ( width , height , 1 , ZM_SUBPIX_ORDER_NONE ) ;
2016-04-29 20:57:31 +08:00
privacy_image - > Clear ( ) ;
}
2018-03-04 05:28:01 +08:00
privacy_image - > Fill ( 0xff , p_zones [ i ] - > GetPolygon ( ) ) ;
privacy_image - > Outline ( 0xff , p_zones [ i ] - > GetPolygon ( ) ) ;
2016-04-29 20:57:31 +08:00
}
} // end foreach zone
if ( privacy_image )
privacy_bitmask = privacy_image - > Buffer ( ) ;
2015-08-20 20:20:41 +08:00
}
2020-07-23 01:14:40 +08:00
int Monitor : : GetImage ( int index , int scale ) {
2016-06-22 02:02:36 +08:00
if ( index < 0 | | index > image_buffer_count ) {
2016-04-29 20:57:31 +08:00
index = shared_data - > last_write_index ;
}
2016-06-22 02:02:36 +08:00
if ( index ! = image_buffer_count ) {
2016-04-29 20:57:31 +08:00
Image * image ;
// If we are going to be modifying the snapshot before writing, then we need to copy it
if ( ( scale ! = ZM_SCALE_BASE ) | | ( ! config . timestamp_on_capture ) ) {
2017-11-14 01:14:57 +08:00
ZMPacket * snap = & image_buffer [ index ] ;
2020-12-10 04:00:11 +08:00
alarm_image . Assign ( * snap - > image ) ;
2016-04-29 20:57:31 +08:00
if ( scale ! = ZM_SCALE_BASE ) {
2019-05-07 00:16:06 +08:00
alarm_image . Scale ( scale ) ;
2016-04-29 20:57:31 +08:00
}
if ( ! config . timestamp_on_capture ) {
2019-05-07 00:16:06 +08:00
TimestampImage ( & alarm_image , snap - > timestamp ) ;
2016-04-29 20:57:31 +08:00
}
image = & alarm_image ;
} else {
image = image_buffer [ index ] . image ;
}
static char filename [ PATH_MAX ] ;
2019-05-07 00:16:06 +08:00
snprintf ( filename , sizeof ( filename ) , " Monitor%d.jpg " , id ) ;
image - > WriteJpeg ( filename ) ;
2016-06-22 02:02:36 +08:00
} else {
2019-05-07 00:16:06 +08:00
Error ( " Unable to generate image, no images in buffer " ) ;
2016-04-29 20:57:31 +08:00
}
2019-05-07 00:16:06 +08:00
return 0 ;
2003-03-26 19:57:29 +08:00
}
2018-04-14 23:03:08 +08:00
ZMPacket * Monitor : : getSnapshot ( int index ) const {
2017-11-22 08:55:40 +08:00
2018-04-14 23:03:08 +08:00
if ( ( index < 0 ) | | ( index > image_buffer_count ) ) {
2016-04-29 20:57:31 +08:00
index = shared_data - > last_write_index ;
}
2017-11-22 08:55:40 +08:00
return & image_buffer [ index ] ;
2016-04-29 20:57:31 +08:00
2021-02-02 05:01:09 +08:00
return nullptr ;
2017-11-22 08:55:40 +08:00
}
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
struct timeval Monitor : : GetTimestamp ( int index ) const {
ZMPacket * packet = getSnapshot ( index ) ;
2017-11-22 08:55:40 +08:00
if ( packet )
return * packet - > timestamp ;
2016-04-29 20:57:31 +08:00
2017-11-22 08:55:40 +08:00
static struct timeval null_tv = { 0 , 0 } ;
return null_tv ;
2003-03-26 19:57:29 +08:00
}
2016-06-22 02:02:36 +08:00
unsigned int Monitor : : GetLastReadIndex ( ) const {
2016-04-29 20:57:31 +08:00
return ( shared_data - > last_read_index ! = ( unsigned int ) image_buffer_count ? shared_data - > last_read_index : - 1 ) ;
2003-03-26 19:57:29 +08:00
}
2016-06-22 02:02:36 +08:00
unsigned int Monitor : : GetLastWriteIndex ( ) const {
2016-04-29 20:57:31 +08:00
return ( shared_data - > last_write_index ! = ( unsigned int ) image_buffer_count ? shared_data - > last_write_index : - 1 ) ;
2003-03-26 19:57:29 +08:00
}
2018-04-13 04:40:11 +08:00
uint64_t Monitor : : GetLastEventId ( ) const {
2018-04-14 23:03:08 +08:00
return shared_data - > last_event_id ;
2003-03-26 19:57:29 +08:00
}
2017-11-25 04:37:50 +08:00
// This function is crap.
2016-06-22 02:02:36 +08:00
double Monitor : : GetFPS ( ) const {
2020-12-10 04:00:11 +08:00
return get_capture_fps ( ) ;
2018-01-19 00:39:33 +08:00
// last_write_index is the last capture index. It starts as == image_buffer_count so that the first asignment % image_buffer_count = 0;
2016-04-29 20:57:31 +08:00
int index1 = shared_data - > last_write_index ;
2017-12-01 05:10:30 +08:00
if ( index1 > = image_buffer_count ) {
2017-11-25 04:37:50 +08:00
// last_write_index only has this value on startup before capturing anything.
return 0.0 ;
2016-04-29 20:57:31 +08:00
}
2017-12-01 05:10:30 +08:00
Debug ( 2 , " index1(%d) " , index1 ) ;
2017-11-14 01:14:57 +08:00
ZMPacket * snap1 = & image_buffer [ index1 ] ;
2017-11-22 08:55:40 +08:00
if ( ! snap1 - > timestamp - > tv_sec ) {
2017-11-25 04:37:50 +08:00
// This should be impossible
2018-02-24 08:01:42 +08:00
Warning ( " Impossible situation. No timestamp on captured image index was %d, image-buffer_count was (%d) " , index1 , image_buffer_count ) ;
2017-11-25 04:37:50 +08:00
return 0.0 ;
2016-04-29 20:57:31 +08:00
}
struct timeval time1 = * snap1 - > timestamp ;
2017-12-02 03:33:51 +08:00
int fps_image_count = image_buffer_count ;
2017-11-18 03:30:38 +08:00
2016-04-29 20:57:31 +08:00
int index2 = ( index1 + 1 ) % image_buffer_count ;
2017-12-01 05:10:30 +08:00
Debug ( 2 , " index2(%d) " , index2 ) ;
2017-11-28 00:28:36 +08:00
ZMPacket * snap2 = & image_buffer [ index2 ] ;
2017-11-25 04:37:50 +08:00
// the timestamp pointers are initialized on connection, so that's redundant
// tv_sec is probably only zero during the first loop of capturing, so this basically just counts the unused images.
2018-01-19 00:39:33 +08:00
// The problem is that there is no locking, and we set the timestamp before we set last_write_index,
// so there is a small window where the next image can have a timestamp in the future
2018-01-19 02:44:26 +08:00
while ( ! snap2 - > timestamp - > tv_sec | | tvDiffSec ( * snap2 - > timestamp , * snap1 - > timestamp ) < 0 ) {
2016-06-22 02:02:36 +08:00
if ( index1 = = index2 ) {
2018-01-19 00:39:33 +08:00
// All images are uncaptured
2017-11-25 04:37:50 +08:00
return 0.0 ;
2016-04-29 20:57:31 +08:00
}
index2 = ( index2 + 1 ) % image_buffer_count ;
2017-11-22 08:55:40 +08:00
snap2 = & image_buffer [ index2 ] ;
2017-12-02 03:33:51 +08:00
fps_image_count - - ;
2016-04-29 20:57:31 +08:00
}
struct timeval time2 = * snap2 - > timestamp ;
double time_diff = tvDiffSec ( time2 , time1 ) ;
2019-01-22 02:00:10 +08:00
if ( ! time_diff ) {
2019-07-31 23:23:02 +08:00
Error ( " No diff between time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d " ,
time_diff , index2 , time2 . tv_sec , time2 . tv_usec , index1 , time1 . tv_sec , time1 . tv_usec , image_buffer_count ) ;
2019-01-22 02:00:10 +08:00
return 0.0 ;
}
2017-12-02 03:33:51 +08:00
double curr_fps = fps_image_count / time_diff ;
2016-04-29 20:57:31 +08:00
2016-06-22 02:02:36 +08:00
if ( curr_fps < 0.0 ) {
2019-07-31 23:23:02 +08:00
Error ( " Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d " ,
curr_fps , time_diff , index2 , time2 . tv_sec , time2 . tv_usec , index1 , time1 . tv_sec , time1 . tv_usec , image_buffer_count ) ;
2017-11-25 04:37:50 +08:00
return 0.0 ;
2017-12-13 03:37:02 +08:00
} else {
2019-07-31 23:23:02 +08:00
Debug ( 2 , " GetFPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d " ,
curr_fps , time_diff , index2 , time2 . tv_sec , time2 . tv_usec , index1 , time1 . tv_sec , time1 . tv_usec , image_buffer_count ) ;
2016-04-29 20:57:31 +08:00
}
2017-11-25 04:37:50 +08:00
return curr_fps ;
2003-03-26 19:57:29 +08:00
}
2018-02-13 18:26:30 +08:00
/* I think this returns the # of micro seconds that we should sleep in order to maintain the desired analysis rate */
2016-06-22 02:02:36 +08:00
useconds_t Monitor : : GetAnalysisRate ( ) {
2020-12-10 04:00:11 +08:00
double capture_fps = get_capture_fps ( ) ;
2017-11-22 08:55:40 +08:00
if ( ! analysis_fps_limit ) {
2019-04-16 00:55:28 +08:00
return 0 ;
2017-11-22 08:55:40 +08:00
} else if ( analysis_fps_limit > capture_fps ) {
2021-01-16 00:54:36 +08:00
if ( last_fps_time ! = last_analysis_fps_time ) {
// At startup they are equal, should never be equal again
Warning ( " Analysis fps (%.2f) is greater than capturing fps (%.2f) " , analysis_fps_limit , capture_fps ) ;
}
2019-04-16 00:55:28 +08:00
return 0 ;
2016-06-22 02:02:36 +08:00
} else {
2017-11-22 08:55:40 +08:00
return ( ( 1000000 / analysis_fps_limit ) - ( 1000000 / capture_fps ) ) ;
2016-04-29 20:57:31 +08:00
}
2015-07-24 04:36:30 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : UpdateAdaptiveSkip ( ) {
if ( config . opt_adaptive_skip ) {
2020-12-10 04:00:11 +08:00
double capturing_fps = get_capture_fps ( ) ;
double analysis_fps = get_analysis_fps ( ) ;
2016-06-22 02:02:36 +08:00
if ( adaptive_skip & & analysis_fps & & ( analysis_fps < capturing_fps ) ) {
2019-04-16 00:55:28 +08:00
Info ( " Analysis fps (%.2f) is lower than capturing fps (%.2f), disabling adaptive skip feature " , analysis_fps , capturing_fps ) ;
2016-04-29 20:57:31 +08:00
adaptive_skip = false ;
2016-06-22 02:02:36 +08:00
} else if ( ! adaptive_skip & & ( ! analysis_fps | | ( analysis_fps > = capturing_fps ) ) ) {
2019-04-16 00:55:28 +08:00
Info ( " Enabling adaptive skip feature " ) ;
2016-04-29 20:57:31 +08:00
adaptive_skip = true ;
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
adaptive_skip = false ;
}
2015-07-24 04:36:30 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : ForceAlarmOn ( int force_score , const char * force_cause , const char * force_text ) {
2016-04-29 20:57:31 +08:00
trigger_data - > trigger_state = TRIGGER_ON ;
trigger_data - > trigger_score = force_score ;
2019-04-16 00:55:28 +08:00
strncpy ( trigger_data - > trigger_cause , force_cause , sizeof ( trigger_data - > trigger_cause ) - 1 ) ;
strncpy ( trigger_data - > trigger_text , force_text , sizeof ( trigger_data - > trigger_text ) - 1 ) ;
2003-03-26 19:57:29 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : ForceAlarmOff ( ) {
2016-04-29 20:57:31 +08:00
trigger_data - > trigger_state = TRIGGER_OFF ;
2003-03-26 21:18:26 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : CancelForced ( ) {
2016-04-29 20:57:31 +08:00
trigger_data - > trigger_state = TRIGGER_CANCEL ;
2003-03-26 19:57:29 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : actionReload ( ) {
2016-04-29 20:57:31 +08:00
shared_data - > action | = RELOAD ;
2005-12-23 00:46:25 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : actionEnable ( ) {
2016-04-29 20:57:31 +08:00
shared_data - > action | = RELOAD ;
2018-04-24 23:41:54 +08:00
db_mutex . lock ( ) ;
2016-04-29 20:57:31 +08:00
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
2019-08-18 02:36:52 +08:00
snprintf ( sql , sizeof ( sql ) , " UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %d " , id ) ;
2018-04-05 23:30:14 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
2016-04-29 20:57:31 +08:00
}
2018-04-05 23:30:14 +08:00
db_mutex . unlock ( ) ;
2005-12-23 00:46:25 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : actionDisable ( ) {
2016-04-29 20:57:31 +08:00
shared_data - > action | = RELOAD ;
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
2019-08-19 09:32:19 +08:00
snprintf ( sql , sizeof ( sql ) , " UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %d " , id ) ;
2018-04-05 23:30:14 +08:00
db_mutex . lock ( ) ;
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
2016-04-29 20:57:31 +08:00
}
2018-04-05 23:30:14 +08:00
db_mutex . unlock ( ) ;
2005-12-23 00:46:25 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : actionSuspend ( ) {
2016-04-29 20:57:31 +08:00
shared_data - > action | = SUSPEND ;
2005-02-24 18:43:29 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : actionResume ( ) {
2016-04-29 20:57:31 +08:00
shared_data - > action | = RESUME ;
2005-02-24 18:43:29 +08:00
}
2019-04-16 00:55:28 +08:00
int Monitor : : actionBrightness ( int p_brightness ) {
2016-06-22 02:02:36 +08:00
if ( purpose ! = CAPTURE ) {
if ( p_brightness > = 0 ) {
2016-04-29 20:57:31 +08:00
shared_data - > brightness = p_brightness ;
shared_data - > action | = SET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & SET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to set brightness " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > action | = GET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & GET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to get brightness " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
}
2019-04-16 00:55:28 +08:00
return shared_data - > brightness ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
return camera - > Brightness ( p_brightness ) ;
} // end int Monitor::actionBrightness(int p_brightness)
2003-06-25 17:47:09 +08:00
2019-04-16 00:55:28 +08:00
int Monitor : : actionContrast ( int p_contrast ) {
2016-06-22 02:02:36 +08:00
if ( purpose ! = CAPTURE ) {
if ( p_contrast > = 0 ) {
2016-04-29 20:57:31 +08:00
shared_data - > contrast = p_contrast ;
shared_data - > action | = SET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & SET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to set contrast " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > action | = GET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & GET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to get contrast " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
}
2019-04-16 00:55:28 +08:00
return shared_data - > contrast ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
return camera - > Contrast ( p_contrast ) ;
} // end int Monitor::actionContrast(int p_contrast)
2003-06-25 17:47:09 +08:00
2019-04-16 00:55:28 +08:00
int Monitor : : actionHue ( int p_hue ) {
2016-06-22 02:02:36 +08:00
if ( purpose ! = CAPTURE ) {
if ( p_hue > = 0 ) {
2016-04-29 20:57:31 +08:00
shared_data - > hue = p_hue ;
shared_data - > action | = SET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & SET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to set hue " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > action | = GET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & GET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to get hue " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
}
2019-04-16 00:55:28 +08:00
return shared_data - > hue ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
return camera - > Hue ( p_hue ) ;
} // end int Monitor::actionHue(int p_hue)
2003-06-25 17:47:09 +08:00
2019-04-16 00:55:28 +08:00
int Monitor : : actionColour ( int p_colour ) {
2016-06-22 02:02:36 +08:00
if ( purpose ! = CAPTURE ) {
if ( p_colour > = 0 ) {
2016-04-29 20:57:31 +08:00
shared_data - > colour = p_colour ;
shared_data - > action | = SET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & SET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to set colour " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > action | = GET_SETTINGS ;
int wait_loops = 10 ;
2016-06-22 02:02:36 +08:00
while ( shared_data - > action & GET_SETTINGS ) {
2016-12-09 00:49:54 +08:00
if ( wait_loops - - ) {
2019-04-16 00:55:28 +08:00
usleep ( 100000 ) ;
2016-12-09 00:49:54 +08:00
} else {
2019-04-16 00:55:28 +08:00
Warning ( " Timed out waiting to get colour " ) ;
return - 1 ;
2016-04-29 20:57:31 +08:00
}
}
}
2019-04-16 00:55:28 +08:00
return shared_data - > colour ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
return camera - > Colour ( p_colour ) ;
} // end int Monitor::actionColour(int p_colour)
2003-06-25 17:47:09 +08:00
2019-04-16 00:55:28 +08:00
void Monitor : : DumpZoneImage ( const char * zone_string ) {
2016-04-29 20:57:31 +08:00
int exclude_id = 0 ;
int extra_colour = 0 ;
Polygon extra_zone ;
2016-06-22 02:02:36 +08:00
if ( zone_string ) {
2018-04-12 22:28:22 +08:00
if ( ! Zone : : ParseZoneString ( zone_string , exclude_id , extra_colour , extra_zone ) ) {
Error ( " Failed to parse zone string, ignoring " ) ;
2016-04-29 20:57:31 +08:00
}
}
2020-08-26 07:45:48 +08:00
Image * zone_image = nullptr ;
2016-04-29 20:57:31 +08:00
if ( ( ( ! staticConfig . SERVER_ID ) | | ( staticConfig . SERVER_ID = = server_id ) ) & & mem_ptr ) {
Debug ( 3 , " Trying to load from local zmc " ) ;
int index = shared_data - > last_write_index ;
2017-11-22 08:55:40 +08:00
ZMPacket * snap = getSnapshot ( index ) ;
2018-04-12 22:28:22 +08:00
zone_image = new Image ( * snap - > image ) ;
2016-04-29 20:57:31 +08:00
} else {
Debug ( 3 , " Trying to load from event " ) ;
// Grab the most revent event image
2019-08-15 04:18:21 +08:00
std : : string sql = stringtf ( " SELECT MAX(`Id`) FROM `Events` WHERE `MonitorId`=%d AND `Frames` > 0 " , id ) ;
2016-04-29 20:57:31 +08:00
zmDbRow eventid_row ;
2018-04-12 22:28:22 +08:00
if ( eventid_row . fetch ( sql . c_str ( ) ) ) {
2018-04-13 04:40:11 +08:00
uint64_t event_id = atoll ( eventid_row [ 0 ] ) ;
2016-04-29 20:57:31 +08:00
2018-04-18 01:51:20 +08:00
Debug ( 3 , " Got event % " PRIu64 , event_id ) ;
2016-04-29 20:57:31 +08:00
EventStream * stream = new EventStream ( ) ;
2018-04-12 22:28:22 +08:00
stream - > setStreamStart ( event_id , ( unsigned int ) 1 ) ;
2016-04-29 20:57:31 +08:00
zone_image = stream - > getImage ( ) ;
2018-04-12 22:26:18 +08:00
delete stream ;
2020-08-26 07:45:48 +08:00
stream = nullptr ;
2016-04-29 20:57:31 +08:00
} else {
2018-04-12 22:28:22 +08:00
Error ( " Unable to load an event for monitor %d " , id ) ;
2016-04-29 20:57:31 +08:00
return ;
}
}
2018-04-12 22:28:22 +08:00
if ( zone_image - > Colours ( ) = = ZM_COLOUR_GRAY8 ) {
zone_image - > Colourise ( ZM_COLOUR_RGB24 , ZM_SUBPIX_ORDER_RGB ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
2019-04-16 00:55:28 +08:00
for ( int i = 0 ; i < n_zones ; i + + ) {
2016-04-29 20:57:31 +08:00
if ( exclude_id & & ( ! extra_colour | | extra_zone . getNumCoords ( ) ) & & zones [ i ] - > Id ( ) = = exclude_id )
continue ;
Rgb colour ;
2016-06-22 02:02:36 +08:00
if ( exclude_id & & ! extra_zone . getNumCoords ( ) & & zones [ i ] - > Id ( ) = = exclude_id ) {
2016-04-29 20:57:31 +08:00
colour = extra_colour ;
2016-06-22 02:02:36 +08:00
} else {
if ( zones [ i ] - > IsActive ( ) ) {
2016-04-29 20:57:31 +08:00
colour = RGB_RED ;
2016-06-22 02:02:36 +08:00
} else if ( zones [ i ] - > IsInclusive ( ) ) {
2016-04-29 20:57:31 +08:00
colour = RGB_ORANGE ;
2016-06-22 02:02:36 +08:00
} else if ( zones [ i ] - > IsExclusive ( ) ) {
2016-04-29 20:57:31 +08:00
colour = RGB_PURPLE ;
2016-06-22 02:02:36 +08:00
} else if ( zones [ i ] - > IsPreclusive ( ) ) {
2016-04-29 20:57:31 +08:00
colour = RGB_BLUE ;
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
colour = RGB_WHITE ;
}
}
2019-04-16 00:55:28 +08:00
zone_image - > Fill ( colour , 2 , zones [ i ] - > GetPolygon ( ) ) ;
zone_image - > Outline ( colour , zones [ i ] - > GetPolygon ( ) ) ;
2016-04-29 20:57:31 +08:00
}
2016-06-22 02:02:36 +08:00
if ( extra_zone . getNumCoords ( ) ) {
2019-04-16 00:55:28 +08:00
zone_image - > Fill ( extra_colour , 2 , extra_zone ) ;
zone_image - > Outline ( extra_colour , extra_zone ) ;
2016-04-29 20:57:31 +08:00
}
static char filename [ PATH_MAX ] ;
2019-04-16 00:55:28 +08:00
snprintf ( filename , sizeof ( filename ) , " Zones%d.jpg " , id ) ;
zone_image - > WriteJpeg ( filename ) ;
2016-04-29 20:57:31 +08:00
delete zone_image ;
2019-04-16 00:55:28 +08:00
} // end void Monitor::DumpZoneImage(const char *zone_string)
2003-03-26 19:57:29 +08:00
2019-04-16 00:55:28 +08:00
void Monitor : : DumpImage ( Image * dump_image ) const {
2016-06-22 02:02:36 +08:00
if ( image_count & & ! ( image_count % 10 ) ) {
2016-04-29 20:57:31 +08:00
static char filename [ PATH_MAX ] ;
static char new_filename [ PATH_MAX ] ;
2019-04-16 00:55:28 +08:00
snprintf ( filename , sizeof ( filename ) , " Monitor%d.jpg " , id ) ;
snprintf ( new_filename , sizeof ( new_filename ) , " Monitor%d-new.jpg " , id ) ;
if ( dump_image - > WriteJpeg ( new_filename ) )
rename ( new_filename , filename ) ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
} // end void Monitor::DumpImage(Image *dump_image)
2003-03-26 19:57:29 +08:00
2019-04-16 00:55:28 +08:00
bool Monitor : : CheckSignal ( const Image * image ) {
2016-04-29 20:57:31 +08:00
static bool static_undef = true ;
/* RGB24 colors */
static uint8_t red_val ;
static uint8_t green_val ;
static uint8_t blue_val ;
2018-03-09 20:23:40 +08:00
static uint8_t grayscale_val ; /* 8bit grayscale color */
2016-04-29 20:57:31 +08:00
static Rgb colour_val ; /* RGB32 color */
static int usedsubpixorder ;
2018-04-19 22:10:14 +08:00
if ( signal_check_points > 0 ) {
2016-06-22 02:02:36 +08:00
if ( static_undef ) {
2016-04-29 20:57:31 +08:00
static_undef = false ;
usedsubpixorder = camera - > SubpixelOrder ( ) ;
colour_val = rgb_convert ( signal_check_colour , ZM_SUBPIX_ORDER_BGR ) ; /* HTML colour code is actually BGR in memory, we want RGB */
colour_val = rgb_convert ( colour_val , usedsubpixorder ) ;
red_val = RED_VAL_BGRA ( signal_check_colour ) ;
green_val = GREEN_VAL_BGRA ( signal_check_colour ) ;
blue_val = BLUE_VAL_BGRA ( signal_check_colour ) ;
grayscale_val = signal_check_colour & 0xff ; /* Clear all bytes but lowest byte */
}
const uint8_t * buffer = image - > Buffer ( ) ;
int pixels = image - > Pixels ( ) ;
int width = image - > Width ( ) ;
int colours = image - > Colours ( ) ;
int index = 0 ;
2018-04-19 22:10:14 +08:00
for ( int i = 0 ; i < signal_check_points ; i + + ) {
2016-06-22 02:02:36 +08:00
while ( true ) {
2018-04-19 21:34:14 +08:00
// Why the casting to long long? also note that on a 64bit cpu, long long is 128bits
2016-04-29 20:57:31 +08:00
index = ( int ) ( ( ( long long ) rand ( ) * ( long long ) ( pixels - 1 ) ) / RAND_MAX ) ;
if ( ! config . timestamp_on_capture | | ! label_format [ 0 ] )
break ;
// Avoid sampling the rows with timestamp in
if ( index < ( label_coord . Y ( ) * width ) | | index > = ( label_coord . Y ( ) + Image : : LINE_HEIGHT ) * width )
break ;
}
2018-03-09 20:23:40 +08:00
2018-03-27 23:04:40 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
2016-06-22 02:02:36 +08:00
if ( * ( buffer + index ) ! = grayscale_val )
2016-04-29 20:57:31 +08:00
return true ;
2016-06-22 02:02:36 +08:00
2018-03-27 23:04:40 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
2016-06-22 02:02:36 +08:00
const uint8_t * ptr = buffer + ( index * colours ) ;
2018-03-27 23:04:40 +08:00
if ( usedsubpixorder = = ZM_SUBPIX_ORDER_BGR ) {
2016-06-22 02:02:36 +08:00
if ( ( RED_PTR_BGRA ( ptr ) ! = red_val ) | | ( GREEN_PTR_BGRA ( ptr ) ! = green_val ) | | ( BLUE_PTR_BGRA ( ptr ) ! = blue_val ) )
return true ;
} else {
/* Assume RGB */
if ( ( RED_PTR_RGBA ( ptr ) ! = red_val ) | | ( GREEN_PTR_RGBA ( ptr ) ! = green_val ) | | ( BLUE_PTR_RGBA ( ptr ) ! = blue_val ) )
return true ;
}
2018-03-27 23:04:40 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
if ( usedsubpixorder = = ZM_SUBPIX_ORDER_ARGB | | usedsubpixorder = = ZM_SUBPIX_ORDER_ABGR ) {
2016-06-22 02:02:36 +08:00
if ( ARGB_ABGR_ZEROALPHA ( * ( ( ( const Rgb * ) buffer ) + index ) ) ! = ARGB_ABGR_ZEROALPHA ( colour_val ) )
return true ;
} else {
/* Assume RGBA or BGRA */
if ( RGBA_BGRA_ZEROALPHA ( * ( ( ( const Rgb * ) buffer ) + index ) ) ! = RGBA_BGRA_ZEROALPHA ( colour_val ) )
return true ;
}
2016-04-29 20:57:31 +08:00
}
2018-03-27 23:04:40 +08:00
} // end for < signal_check_points
2019-04-16 00:55:28 +08:00
Debug ( 1 , " SignalCheck: %d points, colour_val(%d) " , signal_check_points , colour_val ) ;
2018-03-27 23:04:40 +08:00
return false ;
} // end if signal_check_points
return true ;
2019-04-16 00:55:28 +08:00
} // end bool Monitor::CheckSignal(const Image *image)
2006-01-15 06:47:02 +08:00
2017-11-21 00:48:56 +08:00
void Monitor : : CheckAction ( ) {
2016-04-29 20:57:31 +08:00
struct timeval now ;
2020-08-26 07:45:48 +08:00
gettimeofday ( & now , nullptr ) ;
2016-04-29 20:57:31 +08:00
2016-06-22 02:02:36 +08:00
if ( shared_data - > action ) {
2016-06-22 00:21:18 +08:00
// Can there be more than 1 bit set in the action? Shouldn't these be elseifs?
2016-06-22 02:02:36 +08:00
if ( shared_data - > action & RELOAD ) {
2018-05-27 01:01:30 +08:00
Info ( " Received reload indication at count %d " , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > action & = ~ RELOAD ;
Reload ( ) ;
}
2016-06-22 02:02:36 +08:00
if ( shared_data - > action & SUSPEND ) {
if ( Active ( ) ) {
2018-05-27 01:01:30 +08:00
Info ( " Received suspend indication at count %d " , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > active = false ;
//closeEvent();
2016-06-22 00:21:18 +08:00
} else {
2018-05-27 01:01:30 +08:00
Info ( " Received suspend indication at count %d, but wasn't active " , image_count ) ;
2016-04-29 20:57:31 +08:00
}
2016-06-22 02:02:36 +08:00
if ( config . max_suspend_time ) {
2016-04-29 20:57:31 +08:00
auto_resume_time = now . tv_sec + config . max_suspend_time ;
}
shared_data - > action & = ~ SUSPEND ;
2017-11-21 00:48:56 +08:00
} else if ( shared_data - > action & RESUME ) {
2016-06-22 02:02:36 +08:00
if ( Enabled ( ) & & ! Active ( ) ) {
2018-05-27 01:01:30 +08:00
Info ( " Received resume indication at count %d " , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > active = true ;
2017-11-21 00:48:56 +08:00
ref_image = * ( image_buffer [ shared_data - > last_write_index ] . image ) ;
2021-02-11 02:54:13 +08:00
ready_count = std : : max ( warmup_count , pre_event_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > alarm_x = shared_data - > alarm_y = - 1 ;
}
shared_data - > action & = ~ RESUME ;
}
2017-05-20 21:03:51 +08:00
} // end if shared_data->action
2016-05-06 02:51:26 +08:00
2016-06-22 02:02:36 +08:00
if ( auto_resume_time & & ( now . tv_sec > = auto_resume_time ) ) {
2018-05-27 01:01:30 +08:00
Info ( " Auto resuming at count %d " , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > active = true ;
2020-08-08 05:52:43 +08:00
ref_image . Assign ( * ( image_buffer [ shared_data - > last_write_index ] . image ) ) ;
2021-02-11 02:54:13 +08:00
ready_count = std : : max ( warmup_count , pre_event_count ) ;
2016-04-29 20:57:31 +08:00
auto_resume_time = 0 ;
}
2017-11-22 08:55:40 +08:00
}
2017-11-21 00:48:56 +08:00
2020-12-24 07:50:07 +08:00
void Monitor : : UpdateCaptureFPS ( ) {
2021-02-05 00:50:01 +08:00
if ( fps_report_interval and
(
! ( image_count % fps_report_interval )
or
( ( image_count < fps_report_interval ) and ! ( image_count % 10 ) )
)
) {
2018-02-17 23:54:53 +08:00
struct timeval now ;
2021-02-02 05:01:09 +08:00
gettimeofday ( & now , nullptr ) ;
2020-12-24 07:50:07 +08:00
double now_double = ( double ) now . tv_sec + ( 0.000001f * now . tv_usec ) ;
2021-02-05 00:50:01 +08:00
double elapsed = now_double - last_fps_time ;
2020-12-24 07:50:07 +08:00
// If we are too fast, we get div by zero. This seems to happen in the case of audio packets.
2021-02-05 00:50:01 +08:00
// Also only do the update at most 1/sec
if ( elapsed > 1.0 ) {
2020-12-24 07:50:07 +08:00
// # of images per interval / the amount of time it took
2021-02-05 00:50:01 +08:00
double new_capture_fps = double ( ( image_count - last_capture_image_count ) / elapsed ) ;
2020-12-24 07:50:07 +08:00
unsigned int new_camera_bytes = camera - > Bytes ( ) ;
2021-02-05 00:50:01 +08:00
unsigned int new_capture_bandwidth = ( new_camera_bytes - last_camera_bytes ) / elapsed ;
2020-12-24 07:50:07 +08:00
last_camera_bytes = new_camera_bytes ;
2021-02-05 00:50:01 +08:00
Debug ( 4 , " %s: %d - now:%lf, last %lf, elapsed %lf = %lffps " , " Capturing " , image_count ,
now_double , last_analysis_fps_time ,
elapsed , new_capture_fps
) ;
2021-01-16 00:54:36 +08:00
Info ( " %s: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec " ,
name , image_count , new_capture_fps , new_capture_bandwidth ) ;
2020-12-24 07:50:07 +08:00
shared_data - > capture_fps = new_capture_fps ;
last_fps_time = now_double ;
2021-02-05 00:50:01 +08:00
last_capture_image_count = image_count ;
2018-02-16 04:54:13 +08:00
db_mutex . lock ( ) ;
2020-12-24 07:50:07 +08:00
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
// The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost,
// and nothing else will update the status until zmc restarts. Since we are successfully capturing we can
// assume that we are connected
snprintf ( sql , sizeof ( sql ) ,
" INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth,Status) "
" VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE "
" CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected' " ,
id , new_capture_fps , new_capture_bandwidth , new_capture_fps , new_capture_bandwidth ) ;
2019-02-26 22:45:20 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
2018-02-06 23:34:17 +08:00
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
}
2018-02-16 04:54:13 +08:00
db_mutex . unlock ( ) ;
2020-12-24 07:50:07 +08:00
} // now != last_fps_time
} // end if report fps
2021-02-05 00:50:01 +08:00
} // void Monitor::UpdateCaptureFPS()
2016-04-29 20:57:31 +08:00
2020-12-24 07:50:07 +08:00
void Monitor : : UpdateAnalysisFPS ( ) {
Debug ( 1 , " analysis_image_count(%d) motion_count(%d) fps_report_interval(%d) mod%d " ,
analysis_image_count , motion_frame_count , fps_report_interval ,
( ( analysis_image_count & & fps_report_interval ) ? ! ( analysis_image_count % fps_report_interval ) : - 1 ) ) ;
2021-02-02 05:01:09 +08:00
if (
2021-02-05 00:50:01 +08:00
( analysis_image_count and fps_report_interval and ! ( analysis_image_count % fps_report_interval ) )
2021-02-02 05:01:09 +08:00
or
2021-02-05 00:50:01 +08:00
// In startup do faster updates
( ( analysis_image_count < fps_report_interval ) and ! ( analysis_image_count % 10 ) )
2021-02-02 05:01:09 +08:00
) {
2020-12-24 07:50:07 +08:00
//if ( analysis_image_count && fps_report_interval && !(analysis_image_count%fps_report_interval) ) {
struct timeval now ;
2021-02-02 05:01:09 +08:00
gettimeofday ( & now , nullptr ) ;
2020-12-24 07:50:07 +08:00
double now_double = ( double ) now . tv_sec + ( 0.000001f * now . tv_usec ) ;
2021-02-05 00:50:01 +08:00
double elapsed = now_double - last_analysis_fps_time ;
2020-12-24 07:50:07 +08:00
Debug ( 4 , " %s: %d - now:%d.%d = %lf, last %lf, diff %lf " , name , analysis_image_count ,
now . tv_sec , now . tv_usec , now_double , last_analysis_fps_time ,
2021-02-05 00:50:01 +08:00
elapsed
2020-12-24 07:50:07 +08:00
) ;
2021-02-05 00:50:01 +08:00
if ( elapsed > 1.0 ) {
double new_analysis_fps = double ( motion_frame_count - last_motion_frame_count ) / elapsed ;
Info ( " %s: %d - Analysing at %.2lf fps from %d - %d=%d / %lf - %lf = %lf " ,
2020-12-24 07:50:07 +08:00
name , analysis_image_count , new_analysis_fps ,
2021-02-05 00:50:01 +08:00
motion_frame_count , last_motion_frame_count , ( motion_frame_count - last_motion_frame_count ) ,
now_double , last_analysis_fps_time , elapsed ) ;
2020-12-24 07:50:07 +08:00
if ( new_analysis_fps ! = shared_data - > analysis_fps ) {
shared_data - > analysis_fps = new_analysis_fps ;
char sql [ ZM_SQL_SML_BUFSIZ ] ;
snprintf ( sql , sizeof ( sql ) ,
" INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf) "
" ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf " ,
id , new_analysis_fps , new_analysis_fps ) ;
db_mutex . lock ( ) ;
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
}
db_mutex . unlock ( ) ;
last_analysis_fps_time = now_double ;
2021-02-05 00:50:01 +08:00
last_motion_frame_count = motion_frame_count ;
2020-12-24 07:50:07 +08:00
} else {
Debug ( 4 , " No change in fps " ) ;
} // end if change in fps
} // end if at least 1 second has passed since last update
} // end if time to do an update
} // end void Monitor::UpdateAnalysisFPS
2017-11-22 08:55:40 +08:00
// Would be nice if this JUST did analysis
// This idea is that we should be analysing as close to the capture frame as possible.
// This function should process as much as possible before returning
//
// If there is an event, the we should do our best to empty the queue.
// If there isn't then we keep pre-event + alarm frames. = pre_event_count
bool Monitor : : Analyse ( ) {
2019-08-23 00:57:00 +08:00
2018-08-11 22:08:30 +08:00
if ( ! Enabled ( ) ) {
2020-09-26 04:19:52 +08:00
Warning ( " Shouldn't be doing Analyse when not Enabled " ) ;
2017-11-18 03:30:38 +08:00
return false ;
}
2021-01-07 00:31:31 +08:00
if ( ! analysis_it )
2021-02-01 10:30:12 +08:00
analysis_it = packetqueue . get_video_it ( true ) ;
2016-04-29 20:57:31 +08:00
2021-02-12 23:38:21 +08:00
// if have event, send frames until we find a video packet, at which point do analysis. Adaptive skip should only affect which frames we do analysis on.
2016-04-29 20:57:31 +08:00
2021-02-12 23:38:21 +08:00
// get_analysis_packet will lock the packet and may wait if analysis_it is at the end
ZMPacket * snap = packetqueue . get_packet ( analysis_it ) ;
if ( ! snap ) return false ;
2017-12-01 03:37:36 +08:00
2021-02-12 23:38:21 +08:00
// Is it possible for snap->score to be ! -1 ? Not if everything is working correctly
if ( snap - > score ! = - 1 ) {
snap - > unlock ( ) ;
packetqueue . increment_it ( analysis_it ) ;
Error ( " skipping because score was %d " , snap - > score ) ;
return false ;
}
packetqueue_iterator snap_it = * analysis_it ;
packetqueue . increment_it ( analysis_it ) ;
// signal is set by capture
bool signal = shared_data - > signal ;
bool signal_change = ( signal ! = last_signal ) ;
Debug ( 3 , " Motion detection is enabled signal(%d) signal_change(%d) trigger state(%d) image index %d " ,
signal , signal_change , trigger_data - > trigger_state , snap - > image_index ) ;
// Need to guard around event creation/deletion from Reload()
std : : unique_lock < std : : mutex > lck ( event_mutex ) ;
// if we have been told to be OFF, then we are off and don't do any processing.
if ( trigger_data - > trigger_state ! = TRIGGER_OFF ) {
Debug ( 4 , " Trigger not OFF state is (%d) " , trigger_data - > trigger_state ) ;
int score = 0 ;
// Ready means that we have captured the warmup # of frames
if ( ! Ready ( ) ) {
Debug ( 3 , " Not ready? " ) ;
2020-12-10 04:00:11 +08:00
snap - > unlock ( ) ;
return false ;
}
2018-04-15 05:03:45 +08:00
2021-02-12 23:38:21 +08:00
Debug ( 4 , " Ready " ) ;
std : : string cause ;
Event : : StringSetMap noteSetMap ;
2017-11-28 03:57:24 +08:00
2021-02-12 23:38:21 +08:00
// Specifically told to be on. Setting the score here will trigger the alarm.
if ( trigger_data - > trigger_state = = TRIGGER_ON ) {
score + = trigger_data - > trigger_score ;
Debug ( 1 , " Triggered on score += %d => %d " , trigger_data - > trigger_score , score ) ;
if ( ! event ) {
cause + = trigger_data - > trigger_cause ;
}
Event : : StringSet noteSet ;
noteSet . insert ( trigger_data - > trigger_text ) ;
noteSetMap [ trigger_data - > trigger_cause ] = noteSet ;
} // end if trigger_on
if ( signal_change ) {
Debug ( 2 , " Signal change " ) ;
const char * signalText = " Unknown " ;
if ( ! signal ) {
signalText = " Lost " ;
if ( event ) {
Info ( " %s: %03d - Closing event % " PRIu64 " , signal loss " , name , analysis_image_count , event - > Id ( ) ) ;
closeEvent ( ) ;
last_section_mod = 0 ;
}
} else {
signalText = " Reacquired " ;
score + = 100 ;
}
if ( ! event ) {
if ( cause . length ( ) )
cause + = " , " ;
cause + = SIGNAL_CAUSE ;
}
Event : : StringSet noteSet ;
noteSet . insert ( signalText ) ;
noteSetMap [ SIGNAL_CAUSE ] = noteSet ;
shared_data - > state = state = IDLE ;
shared_data - > active = signal ;
if ( ( function = = MODECT or function = = MOCORD ) and snap - > image )
ref_image . Assign ( * ( snap - > image ) ) ;
} // else
if ( signal ) {
if ( snap - > image or ( snap - > packet . stream_index = = video_stream_id ) ) {
struct timeval * timestamp = snap - > timestamp ;
if ( Active ( ) and ( function = = MODECT or function = = MOCORD ) and snap - > image ) {
Debug ( 3 , " signal and active and modect " ) ;
Event : : StringSet zoneSet ;
int motion_score = last_motion_score ;
if ( analysis_fps_limit ) {
double capture_fps = get_capture_fps ( ) ;
motion_frame_skip = capture_fps / analysis_fps_limit ;
Debug ( 1 , " Recalculating motion_frame_skip (%d) = capture_fps(%f) / analysis_fps(%f) " ,
motion_frame_skip , capture_fps , analysis_fps_limit ) ;
2016-04-29 20:57:31 +08:00
}
2021-02-12 23:38:21 +08:00
if ( ! ( analysis_image_count % ( motion_frame_skip + 1 ) ) ) {
if ( snap - > image ) {
// Get new score.
motion_score = DetectMotion ( * ( snap - > image ) , zoneSet ) ;
Debug ( 3 , " After motion detection, score:%d last_motion_score(%d), new motion score(%d) " ,
score , last_motion_score , motion_score ) ;
} else {
Warning ( " No image in snap " ) ;
2016-04-29 20:57:31 +08:00
}
2021-02-12 23:38:21 +08:00
// Why are we updating the last_motion_score too?
last_motion_score = motion_score ;
motion_frame_count + = 1 ;
2017-05-17 00:04:56 +08:00
} else {
2021-02-12 23:38:21 +08:00
Debug ( 1 , " Skipped motion detection " ) ;
2016-04-29 20:57:31 +08:00
}
2021-02-12 23:38:21 +08:00
if ( motion_score ) {
score + = motion_score ;
2016-04-29 20:57:31 +08:00
if ( cause . length ( ) )
cause + = " , " ;
2021-02-12 23:38:21 +08:00
cause + = MOTION_CAUSE ;
noteSetMap [ MOTION_CAUSE ] = zoneSet ;
} // end if motion_score
} // end if active and doing motion detection
// Check to see if linked monitors are triggering.
if ( n_linked_monitors > 0 ) {
Debug ( 4 , " Checking linked monitors " ) ;
// FIXME improve logic here
bool first_link = true ;
2016-04-29 20:57:31 +08:00
Event : : StringSet noteSet ;
2021-02-12 23:38:21 +08:00
for ( int i = 0 ; i < n_linked_monitors ; i + + ) {
// TODO: Shouldn't we try to connect?
if ( linked_monitors [ i ] - > isConnected ( ) ) {
Debug ( 4 , " Linked monitor %d %s is connected " ,
linked_monitors [ i ] - > Id ( ) , linked_monitors [ i ] - > Name ( ) ) ;
if ( linked_monitors [ i ] - > hasAlarmed ( ) ) {
Debug ( 4 , " Linked monitor %d %s is alarmed " ,
linked_monitors [ i ] - > Id ( ) , linked_monitors [ i ] - > Name ( ) ) ;
if ( ! event ) {
if ( first_link ) {
if ( cause . length ( ) )
cause + = " , " ;
cause + = LINKED_CAUSE ;
first_link = false ;
}
}
noteSet . insert ( linked_monitors [ i ] - > Name ( ) ) ;
score + = linked_monitors [ i ] - > lastFrameScore ( ) ; // 50;
} else {
Debug ( 4 , " Linked monitor %d %s is not alarmed " ,
linked_monitors [ i ] - > Id ( ) , linked_monitors [ i ] - > Name ( ) ) ;
2020-12-28 01:00:18 +08:00
}
2021-02-12 23:38:21 +08:00
} else {
Debug ( 1 , " Linked monitor %d %d is not connected. Connecting. " , i , linked_monitors [ i ] - > Id ( ) ) ;
linked_monitors [ i ] - > connect ( ) ;
}
} // end foreach linked_monitor
if ( noteSet . size ( ) > 0 )
noteSetMap [ LINKED_CAUSE ] = noteSet ;
} // end if linked_monitors
if ( function = = RECORD | | function = = MOCORD ) {
// If doing record, check to see if we need to close the event or not.
if ( event ) {
Debug ( 2 , " Have event in mocord " ) ;
if ( section_length
& & ( ( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) > = section_length )
& & ( ( function = = MOCORD & & ( event_close_mode ! = CLOSE_TIME ) ) | | ! ( timestamp - > tv_sec % section_length ) )
) {
Info ( " %s: %03d - Closing event % " PRIu64 " , section end forced %d - %d = %d >= %d " ,
name , image_count , event - > Id ( ) ,
timestamp - > tv_sec , video_store_data - > recording . tv_sec ,
timestamp - > tv_sec - video_store_data - > recording . tv_sec ,
section_length
) ;
closeEvent ( ) ;
} // end if section_length
} // end if event
2020-12-28 01:00:18 +08:00
2021-02-12 23:38:21 +08:00
if ( ! event ) {
Debug ( 2 , " Creating continuous event " ) ;
if ( ! snap - > keyframe and ( videowriter = = PASSTHROUGH ) ) {
// Must start on a keyframe so rewind. Only for passthrough though I guess.
// FIXME this iterator is not protected from invalidation
packetqueue_iterator * start_it = packetqueue . get_event_start_packet_it (
snap_it , 0 /* pre_event_count */
) ;
// This gets a lock on the starting packet
ZMPacket * starting_packet = packetqueue . get_packet ( start_it ) ;
event = new Event ( this , * ( starting_packet - > timestamp ) , " Continuous " , noteSetMap ) ;
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while ( starting_packet and ( * start_it ) ! = snap_it ) {
event - > AddPacket ( starting_packet ) ;
// Have added the packet, don't want to unlock it until we have locked the next
packetqueue . increment_it ( start_it ) ;
if ( ( * start_it ) = = snap_it ) {
starting_packet - > unlock ( ) ;
break ;
2020-12-28 01:00:18 +08:00
}
2021-02-12 23:38:21 +08:00
ZMPacket * p = packetqueue . get_packet ( start_it ) ;
starting_packet - > unlock ( ) ;
starting_packet = p ;
2019-02-26 22:45:20 +08:00
}
2021-02-12 23:38:21 +08:00
packetqueue . free_it ( start_it ) ;
delete start_it ;
start_it = nullptr ;
} else {
// Create event from current snap
event = new Event ( this , * timestamp , " Continuous " , noteSetMap ) ;
}
shared_data - > last_event_id = event - > Id ( ) ;
// lets construct alarm cause. It will contain cause + names of zones alarmed
std : : string alarm_cause = " Continuous " ;
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( zones [ i ] - > Alarmed ( ) ) {
alarm_cause + = std : : string ( zones [ i ] - > Label ( ) ) ;
if ( i < n_zones - 1 ) {
alarm_cause + = " , " ;
}
}
}
alarm_cause = cause + " " + alarm_cause ;
strncpy ( shared_data - > alarm_cause , alarm_cause . c_str ( ) , sizeof ( shared_data - > alarm_cause ) - 1 ) ;
video_store_data - > recording = event - > StartTime ( ) ;
Info ( " %s: %03d - Opened new event % " PRIu64 " , section start " ,
name , analysis_image_count , event - > Id ( ) ) ;
/* To prevent cancelling out an existing alert\prealarm\alarm state */
if ( state = = IDLE ) {
shared_data - > state = state = TAPE ;
}
} // end if ! event
} // end if RECORDING
if ( score ) {
if ( ( state = = IDLE ) | | ( state = = TAPE ) | | ( state = = PREALARM ) ) {
// If we should end then previous continuous event and start a new non-continuous event
if ( event & & event - > Frames ( )
& & ( ! event - > AlarmFrames ( ) )
& & ( event_close_mode = = CLOSE_ALARM )
& & ( ( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) > = min_section_length )
& & ( ( ! pre_event_count ) | | ( Event : : PreAlarmCount ( ) > = alarm_frame_count - 1 ) )
) {
Info ( " %s: %03d - Closing event % " PRIu64 " , continuous end, alarm begins " ,
name , image_count , event - > Id ( ) ) ;
closeEvent ( ) ;
} else if ( event ) {
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
Debug ( 3 , " pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d min " ,
Event : : PreAlarmCount ( ) , event - > Frames ( ) , event - > AlarmFrames ( ) ,
( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) , min_section_length
) ;
}
if ( ( ! pre_event_count ) | | ( Event : : PreAlarmCount ( ) > = alarm_frame_count - 1 ) ) {
// lets construct alarm cause. It will contain cause + names of zones alarmed
std : : string alarm_cause = " " ;
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( zones [ i ] - > Alarmed ( ) ) {
alarm_cause = alarm_cause + " , " + std : : string ( zones [ i ] - > Label ( ) ) ;
2019-04-25 01:55:57 +08:00
}
2021-02-12 23:38:21 +08:00
}
if ( ! alarm_cause . empty ( ) ) alarm_cause [ 0 ] = ' ' ;
alarm_cause = cause + alarm_cause ;
strncpy ( shared_data - > alarm_cause , alarm_cause . c_str ( ) , sizeof ( shared_data - > alarm_cause ) - 1 ) ;
Info ( " %s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s " ,
name , image_count , Event : : PreAlarmCount ( ) , alarm_frame_count , shared_data - > alarm_cause ) ;
2019-06-25 01:13:46 +08:00
if ( ! event ) {
2021-02-12 23:38:21 +08:00
packetqueue_iterator * start_it = packetqueue . get_event_start_packet_it (
snap_it ,
( pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count )
) ;
ZMPacket * starting_packet = * ( * start_it ) ;
event = new Event ( this , * ( starting_packet - > timestamp ) , cause , noteSetMap ) ;
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while ( * start_it ! = snap_it ) {
event - > AddPacket ( starting_packet ) ;
packetqueue . increment_it ( start_it ) ;
if ( ( * start_it ) = = snap_it ) {
2021-02-07 09:11:27 +08:00
starting_packet - > unlock ( ) ;
2021-02-12 23:38:21 +08:00
break ;
2016-04-29 20:57:31 +08:00
}
2021-02-12 23:38:21 +08:00
ZMPacket * p = packetqueue . get_packet ( start_it ) ;
starting_packet - > unlock ( ) ;
starting_packet = p ;
2021-01-16 00:54:36 +08:00
}
2021-02-12 23:38:21 +08:00
packetqueue . free_it ( start_it ) ;
delete start_it ;
start_it = nullptr ;
2017-11-22 08:55:40 +08:00
2020-12-28 01:00:18 +08:00
shared_data - > last_event_id = event - > Id ( ) ;
2019-05-06 22:50:12 +08:00
//set up video store data
snprintf ( video_store_data - > event_file , sizeof ( video_store_data - > event_file ) , " %s " , event - > getEventFile ( ) ) ;
video_store_data - > recording = event - > StartTime ( ) ;
2021-02-12 23:38:21 +08:00
shared_data - > state = state = ALARM ;
2020-12-28 01:00:18 +08:00
2021-02-12 23:38:21 +08:00
Info ( " %s: %03d - Opening new event % " PRIu64 " , alarm start " , name , image_count , event - > Id ( ) ) ;
} // end if no event, so start it
if ( alarm_frame_count ) {
Debug ( 1 , " alarm frame count so SavePreAlarmFrames " ) ;
event - > SavePreAlarmFrames ( ) ;
}
} else if ( state ! = PREALARM ) {
Info ( " %s: %03d - Gone into prealarm state " , name , analysis_image_count ) ;
shared_data - > state = state = PREALARM ;
2016-04-29 20:57:31 +08:00
}
2021-02-12 23:38:21 +08:00
} else if ( state = = ALERT ) {
Info ( " %s: %03d - Gone back into alarm state " , name , analysis_image_count ) ;
shared_data - > state = state = ALARM ;
}
last_alarm_count = analysis_image_count ;
} else { // no score?
if ( state = = ALARM ) {
Info ( " %s: %03d - Gone into alert state " , name , analysis_image_count ) ;
shared_data - > state = state = ALERT ;
} else if ( state = = ALERT ) {
if (
( analysis_image_count - last_alarm_count > post_event_count )
& &
( ( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) > = min_section_length )
) {
Info ( " %s: %03d - Left alarm state (% " PRIu64 " ) - %d(%d) images " ,
name , analysis_image_count , event - > Id ( ) , event - > Frames ( ) , event - > AlarmFrames ( ) ) ;
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
if ( ( function ! = RECORD & & function ! = MOCORD ) | | event_close_mode = = CLOSE_ALARM ) {
shared_data - > state = state = IDLE ;
Info ( " %s: %03d - Closing event % " PRIu64 " , alarm end%s " ,
name , analysis_image_count , event - > Id ( ) , ( function = = MOCORD ) ? " , section truncated " : " " ) ;
closeEvent ( ) ;
} else {
shared_data - > state = state = TAPE ;
}
}
} else if ( state = = PREALARM ) {
// Back to IDLE
shared_data - > state = state = ( ( function ! = MOCORD ) ? IDLE : TAPE ) ;
} else {
Debug ( 1 , " State %d beacuse image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%d) - recording.tv_src(%d) >= min_section_length(%d) " ,
state , analysis_image_count , last_alarm_count , post_event_count ,
timestamp - > tv_sec , video_store_data - > recording . tv_sec , min_section_length ) ;
}
if ( Event : : PreAlarmCount ( ) )
Event : : EmptyPreAlarmFrames ( ) ;
} // end if score or not
snap - > score = score ;
if ( state = = PREALARM ) {
// Generate analysis images if necessary
if ( ( savejpegs > 1 ) and snap - > image ) {
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( zones [ i ] - > Alarmed ( ) ) {
if ( zones [ i ] - > AlarmImage ( ) ) {
if ( ! snap - > analysis_image )
snap - > analysis_image = new Image ( * ( snap - > image ) ) ;
snap - > analysis_image - > Overlay ( * ( zones [ i ] - > AlarmImage ( ) ) ) ;
}
} // end if zone is alarmed
} // end foreach zone
} // end if savejpegs
// incremement pre alarm image count
//have_pre_alarmed_frames ++;
Event : : AddPreAlarmFrame ( snap - > image , * timestamp , score , nullptr ) ;
} else if ( state = = ALARM ) {
if ( ( savejpegs > 1 ) and snap - > image ) {
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( zones [ i ] - > Alarmed ( ) ) {
if ( zones [ i ] - > AlarmImage ( ) ) {
if ( ! snap - > analysis_image )
snap - > analysis_image = new Image ( * ( snap - > image ) ) ;
snap - > analysis_image - > Overlay ( * ( zones [ i ] - > AlarmImage ( ) ) ) ;
}
if ( config . record_event_stats )
zones [ i ] - > RecordStats ( event ) ;
} // end if zone is alarmed
} // end foreach zone
}
if ( noteSetMap . size ( ) > 0 )
event - > updateNotes ( noteSetMap ) ;
if ( section_length
& & ( ( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) > = section_length )
& & ! ( image_count % fps_report_interval )
) {
Warning ( " %s: %03d - event % " PRIu64 " , has exceeded desired section length. %d - %d = %d >= %d " ,
name , image_count , event - > Id ( ) ,
timestamp - > tv_sec , video_store_data - > recording . tv_sec ,
timestamp - > tv_sec - video_store_data - > recording . tv_sec ,
section_length
) ;
closeEvent ( ) ;
event = new Event ( this , * timestamp , cause , noteSetMap ) ;
shared_data - > last_event_id = event - > Id ( ) ;
//set up video store data
snprintf ( video_store_data - > event_file , sizeof ( video_store_data - > event_file ) , " %s " , event - > getEventFile ( ) ) ;
video_store_data - > recording = event - > StartTime ( ) ;
}
2017-04-06 04:10:21 +08:00
2021-02-12 23:38:21 +08:00
} else if ( state = = ALERT ) {
// Alert means this frame has no motion, but we were alarmed and are still recording.
if ( noteSetMap . size ( ) > 0 )
event - > updateNotes ( noteSetMap ) ;
} else if ( state = = TAPE ) {
// bulk frame code moved to event.
} // end if state machine
2021-02-11 06:07:58 +08:00
2021-02-12 23:38:21 +08:00
if ( ( function = = MODECT or function = = MOCORD ) and snap - > image ) {
ref_image . Blend ( * ( snap - > image ) , ( state = = ALARM ? alarm_ref_blend_perc : ref_blend_perc ) ) ;
}
last_signal = signal ;
} // end if videostream
} // end if signal
shared_data - > last_frame_score = score ;
} else {
Debug ( 3 , " trigger == off " ) ;
if ( event ) {
Info ( " %s: %03d - Closing event % " PRIu64 " , trigger off " , name , analysis_image_count , event - > Id ( ) ) ;
closeEvent ( ) ;
2021-02-11 06:07:58 +08:00
}
2021-02-12 23:38:21 +08:00
shared_data - > state = state = IDLE ;
trigger_data - > trigger_state = TRIGGER_CANCEL ;
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
2016-04-29 20:57:31 +08:00
2021-02-12 23:38:21 +08:00
if ( event ) event - > AddPacket ( snap ) ;
if ( videowriter = = PASSTHROUGH and ! savejpegs ) {
// Don't need raw images anymore
delete snap - > image ;
snap - > image = nullptr ;
}
// popPacket will have placed a second lock on snap, so release it here.
snap - > unlock ( ) ;
shared_data - > last_read_index = snap - > image_index ;
shared_data - > last_read_time = time ( nullptr ) ;
analysis_image_count + + ;
UpdateAnalysisFPS ( ) ;
return true ;
2020-12-06 08:36:44 +08:00
} // end Monitor::Analyse
2016-04-01 00:54:56 +08:00
2016-06-22 02:02:36 +08:00
void Monitor : : Reload ( ) {
2019-04-16 00:55:28 +08:00
Debug ( 1 , " Reloading monitor %s " , name ) ;
2016-04-29 20:57:31 +08:00
2021-02-11 02:54:13 +08:00
// Access to the event needs to be protected. Either thread could call Reload. Either thread could close the event.
// Need a mutex on it I guess. FIXME
2021-02-12 23:14:25 +08:00
// Need to guard around event creation/deletion This will prevent event creation until new settings are loaded
std : : unique_lock < std : : mutex > lck ( event_mutex ) ;
2018-03-27 22:04:19 +08:00
if ( event ) {
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Closing event % " PRIu64 " , reloading " , name , image_count , event - > Id ( ) ) ;
2018-03-27 22:04:19 +08:00
closeEvent ( ) ;
}
2016-04-29 20:57:31 +08:00
2018-04-14 23:03:08 +08:00
std : : string sql = load_monitor_sql + stringtf ( " WHERE Id=%d " , id ) ;
zmDbRow * row = zmDbFetchOne ( sql . c_str ( ) ) ;
2018-03-27 22:04:19 +08:00
if ( ! row ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
} else if ( MYSQL_ROW dbrow = row - > mysql_row ( ) ) {
2019-08-23 00:57:00 +08:00
Load ( dbrow , 1 , purpose ) ;
2016-04-29 20:57:31 +08:00
shared_data - > state = state = IDLE ;
shared_data - > alarm_x = shared_data - > alarm_y = - 1 ;
if ( enabled )
shared_data - > active = true ;
2021-02-11 02:54:13 +08:00
ready_count = std : : max ( warmup_count , pre_event_count ) ;
2016-04-29 20:57:31 +08:00
2018-04-12 23:29:35 +08:00
delete row ;
2020-07-22 05:38:32 +08:00
} // end if row
2016-04-29 20:57:31 +08:00
ReloadZones ( ) ;
2020-04-17 05:54:20 +08:00
} // end void Monitor::Reload()
2005-12-23 00:46:25 +08:00
2016-06-22 02:02:36 +08:00
void Monitor : : ReloadZones ( ) {
2019-04-16 00:55:28 +08:00
Debug ( 1 , " Reloading zones for monitor %s " , name ) ;
2016-06-22 02:02:36 +08:00
for ( int i = 0 ; i < n_zones ; i + + ) {
2016-04-29 20:57:31 +08:00
delete zones [ i ] ;
}
delete [ ] zones ;
2020-08-26 07:45:48 +08:00
zones = nullptr ;
2019-04-16 00:55:28 +08:00
n_zones = Zone : : Load ( this , zones ) ;
2016-04-29 20:57:31 +08:00
//DumpZoneImage();
2019-04-16 00:55:28 +08:00
} // end void Monitor::ReloadZones()
2003-03-26 19:57:29 +08:00
2018-04-24 04:24:15 +08:00
void Monitor : : ReloadLinkedMonitors ( const char * p_linked_monitors ) {
Debug ( 1 , " Reloading linked monitors for monitor %s, '%s' " , name , p_linked_monitors ) ;
2016-06-22 02:02:36 +08:00
if ( n_linked_monitors ) {
2020-12-15 23:14:19 +08:00
for ( int i = 0 ; i < n_linked_monitors ; i + + ) {
2016-04-29 20:57:31 +08:00
delete linked_monitors [ i ] ;
}
delete [ ] linked_monitors ;
2020-08-26 07:45:48 +08:00
linked_monitors = nullptr ;
2016-04-29 20:57:31 +08:00
}
n_linked_monitors = 0 ;
2016-06-22 02:02:36 +08:00
if ( p_linked_monitors ) {
2016-04-29 20:57:31 +08:00
int n_link_ids = 0 ;
unsigned int link_ids [ 256 ] ;
2018-03-04 05:28:01 +08:00
// This nasty code picks out strings of digits from p_linked_monitors and tries to load them.
2016-04-29 20:57:31 +08:00
char link_id_str [ 8 ] ;
char * dest_ptr = link_id_str ;
const char * src_ptr = p_linked_monitors ;
2019-04-16 00:55:28 +08:00
while ( 1 ) {
2016-04-29 20:57:31 +08:00
dest_ptr = link_id_str ;
2019-04-16 00:55:28 +08:00
while ( * src_ptr > = ' 0 ' & & * src_ptr < = ' 9 ' ) {
2016-06-22 02:02:36 +08:00
if ( ( dest_ptr - link_id_str ) < ( unsigned int ) ( sizeof ( link_id_str ) - 1 ) ) {
2016-04-29 20:57:31 +08:00
* dest_ptr + + = * src_ptr + + ;
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
break ;
}
}
// Add the link monitor
2016-06-22 02:02:36 +08:00
if ( dest_ptr ! = link_id_str ) {
2016-04-29 20:57:31 +08:00
* dest_ptr = ' \0 ' ;
unsigned int link_id = atoi ( link_id_str ) ;
2018-04-24 04:24:15 +08:00
if ( link_id > 0 & & link_id ! = id ) {
Debug ( 3 , " Found linked monitor id %d " , link_id ) ;
2016-04-29 20:57:31 +08:00
int j ;
2016-06-22 02:02:36 +08:00
for ( j = 0 ; j < n_link_ids ; j + + ) {
2016-04-29 20:57:31 +08:00
if ( link_ids [ j ] = = link_id )
break ;
}
2017-05-17 00:04:56 +08:00
if ( j = = n_link_ids ) {
// Not already found
2016-04-29 20:57:31 +08:00
link_ids [ n_link_ids + + ] = link_id ;
}
}
}
if ( ! * src_ptr )
break ;
while ( * src_ptr & & ( * src_ptr < ' 0 ' | | * src_ptr > ' 9 ' ) )
src_ptr + + ;
if ( ! * src_ptr )
break ;
}
2016-06-22 02:02:36 +08:00
if ( n_link_ids > 0 ) {
2018-04-24 04:24:15 +08:00
Debug ( 1 , " Linking to %d monitors " , n_link_ids ) ;
2016-04-29 20:57:31 +08:00
linked_monitors = new MonitorLink * [ n_link_ids ] ;
int count = 0 ;
2016-06-22 02:02:36 +08:00
for ( int i = 0 ; i < n_link_ids ; i + + ) {
2018-04-24 04:24:15 +08:00
Debug ( 1 , " Checking linked monitor %d " , link_ids [ i ] ) ;
2016-04-29 20:57:31 +08:00
2018-04-24 04:24:15 +08:00
db_mutex . lock ( ) ;
2016-04-29 20:57:31 +08:00
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
2019-08-18 02:36:52 +08:00
snprintf ( sql , sizeof ( sql ) ,
" SELECT `Id`, `Name` FROM `Monitors` "
" WHERE `Id` = %d "
" AND `Function` != 'None' "
" AND `Function` != 'Monitor' "
" AND `Enabled`=1 " ,
2019-12-01 03:59:39 +08:00
link_ids [ i ] ) ;
2018-04-24 04:24:15 +08:00
if ( mysql_query ( & dbconn , sql ) ) {
db_mutex . unlock ( ) ;
2018-12-21 02:41:57 +08:00
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
2018-04-24 04:24:15 +08:00
continue ;
2016-04-29 20:57:31 +08:00
}
2018-04-24 04:24:15 +08:00
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
2018-02-16 04:54:13 +08:00
db_mutex . unlock ( ) ;
2016-06-22 02:02:36 +08:00
if ( ! result ) {
2018-12-21 02:41:57 +08:00
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
2018-04-24 04:24:15 +08:00
continue ;
2016-04-29 20:57:31 +08:00
}
2018-04-24 04:24:15 +08:00
int n_monitors = mysql_num_rows ( result ) ;
2016-06-22 02:02:36 +08:00
if ( n_monitors = = 1 ) {
2018-04-24 04:24:15 +08:00
MYSQL_ROW dbrow = mysql_fetch_row ( result ) ;
2020-12-06 08:36:44 +08:00
Debug ( 1 , " Linking to monitor %d %s " , atoi ( dbrow [ 0 ] ) , dbrow [ 1 ] ) ;
2018-04-24 04:24:15 +08:00
linked_monitors [ count + + ] = new MonitorLink ( link_ids [ i ] , dbrow [ 1 ] ) ;
2016-06-22 02:02:36 +08:00
} else {
2018-04-24 04:24:15 +08:00
Warning ( " Can't link to monitor %d, invalid id, function or not enabled " , link_ids [ i ] ) ;
2016-04-29 20:57:31 +08:00
}
2018-04-24 04:24:15 +08:00
mysql_free_result ( result ) ;
} // end foreach link_id
2016-04-29 20:57:31 +08:00
n_linked_monitors = count ;
2018-04-24 04:24:15 +08:00
} // end if has link_ids
} // end if p_linked_monitors
} // end void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors)
2006-01-23 02:31:55 +08:00
2021-02-08 02:12:39 +08:00
std : : vector < std : : shared_ptr < Monitor > > Monitor : : LoadMonitors ( std : : string sql , Purpose purpose ) {
2018-03-09 20:23:40 +08:00
Debug ( 1 , " Loading Monitors with %s " , sql . c_str ( ) ) ;
2016-04-29 20:57:31 +08:00
2018-03-09 20:23:40 +08:00
MYSQL_RES * result = zmDbFetch ( sql . c_str ( ) ) ;
2021-02-08 02:12:39 +08:00
if ( ! result ) {
2018-03-09 20:23:40 +08:00
Error ( " Can't load local monitors: %s " , mysql_error ( & dbconn ) ) ;
2021-02-08 02:12:39 +08:00
return { } ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
int n_monitors = mysql_num_rows ( result ) ;
2019-04-16 00:55:28 +08:00
Debug ( 1 , " Got %d monitors " , n_monitors ) ;
2021-02-08 02:12:39 +08:00
std : : vector < std : : shared_ptr < Monitor > > monitors ;
monitors . reserve ( n_monitors ) ;
for ( int i = 0 ; MYSQL_ROW dbrow = mysql_fetch_row ( result ) ; i + + ) {
2021-02-08 19:25:18 +08:00
monitors . emplace_back ( std : : make_shared < Monitor > ( ) ) ;
2021-02-08 02:12:39 +08:00
monitors . back ( ) - > Load ( dbrow , true , purpose ) ;
}
if ( mysql_errno ( & dbconn ) ) {
2018-03-09 20:23:40 +08:00
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
2020-11-19 23:43:02 +08:00
mysql_free_result ( result ) ;
2021-02-08 02:12:39 +08:00
return { } ;
2018-03-09 20:23:40 +08:00
}
mysql_free_result ( result ) ;
2016-04-29 20:57:31 +08:00
2021-02-08 02:12:39 +08:00
return monitors ;
}
2016-04-29 20:57:31 +08:00
2018-03-09 20:23:40 +08:00
# if ZM_HAS_V4L
2021-02-08 02:12:39 +08:00
std : : vector < std : : shared_ptr < Monitor > > Monitor : : LoadLocalMonitors ( const char * device , Purpose purpose ) {
2016-04-29 20:57:31 +08:00
2019-08-18 02:36:52 +08:00
std : : string sql = load_monitor_sql + " WHERE `Function` != 'None' AND `Type` = 'Local' " ;
2016-04-29 20:57:31 +08:00
2018-03-09 20:23:40 +08:00
if ( device [ 0 ] )
2019-08-18 02:36:52 +08:00
sql + = " AND `Device`=' " + std : : string ( device ) + " ' " ;
2018-03-09 20:23:40 +08:00
if ( staticConfig . SERVER_ID )
2019-08-18 02:36:52 +08:00
sql + = stringtf ( " AND `ServerId`=%d " , staticConfig . SERVER_ID ) ;
2021-02-08 02:12:39 +08:00
return LoadMonitors ( sql , purpose ) ;
}
2018-03-09 20:23:40 +08:00
# endif // ZM_HAS_V4L
2018-03-09 20:50:47 +08:00
2021-02-08 02:12:39 +08:00
std : : vector < std : : shared_ptr < Monitor > > Monitor : : LoadRemoteMonitors ( const char * protocol , const char * host , const char * port , const char * path , Purpose purpose ) {
2019-08-18 02:36:52 +08:00
std : : string sql = load_monitor_sql + " WHERE `Function` != 'None' AND `Type` = 'Remote' " ;
2018-03-09 20:23:40 +08:00
if ( staticConfig . SERVER_ID )
2019-08-18 02:36:52 +08:00
sql + = stringtf ( " AND `ServerId`=%d " , staticConfig . SERVER_ID ) ;
2018-03-09 20:50:47 +08:00
2018-03-09 20:23:40 +08:00
if ( protocol )
2019-08-18 02:36:52 +08:00
sql + = stringtf ( " AND `Protocol` = '%s' AND `Host` = '%s' AND `Port` = '%s' AND `Path` = '%s' " , protocol , host , port , path ) ;
2021-02-08 02:12:39 +08:00
return LoadMonitors ( sql , purpose ) ;
}
2018-03-09 20:50:47 +08:00
2021-02-08 02:12:39 +08:00
std : : vector < std : : shared_ptr < Monitor > > Monitor : : LoadFileMonitors ( const char * file , Purpose purpose ) {
2019-08-18 02:36:52 +08:00
std : : string sql = load_monitor_sql + " WHERE `Function` != 'None' AND `Type` = 'File' " ;
2018-03-09 20:23:40 +08:00
if ( file [ 0 ] )
2019-08-18 02:36:52 +08:00
sql + = " AND `Path`=' " + std : : string ( file ) + " ' " ;
2016-04-29 20:57:31 +08:00
if ( staticConfig . SERVER_ID ) {
2019-08-18 02:36:52 +08:00
sql + = stringtf ( " AND `ServerId`=%d " , staticConfig . SERVER_ID ) ;
2020-10-09 04:46:30 +08:00
}
2021-02-08 02:12:39 +08:00
return LoadMonitors ( sql , purpose ) ;
}
2003-03-26 19:57:29 +08:00
2011-02-16 05:59:06 +08:00
# if HAVE_LIBAVFORMAT
2021-02-08 02:12:39 +08:00
std : : vector < std : : shared_ptr < Monitor > > Monitor : : LoadFfmpegMonitors ( const char * file , Purpose purpose ) {
2019-08-18 02:36:52 +08:00
std : : string sql = load_monitor_sql + " WHERE `Function` != 'None' AND `Type` = 'Ffmpeg' " ;
2018-03-09 20:23:40 +08:00
if ( file [ 0 ] )
2019-08-18 02:36:52 +08:00
sql + = " AND `Path` = ' " + std : : string ( file ) + " ' " ;
2018-03-09 20:23:40 +08:00
if ( staticConfig . SERVER_ID ) {
2019-08-18 02:36:52 +08:00
sql + = stringtf ( " AND `ServerId`=%d " , staticConfig . SERVER_ID ) ;
2018-03-09 20:23:40 +08:00
}
2021-02-08 02:12:39 +08:00
return LoadMonitors ( sql , purpose ) ;
}
2009-01-28 23:02:33 +08:00
# endif // HAVE_LIBAVFORMAT
2003-03-26 19:57:29 +08:00
2016-09-13 09:35:14 +08:00
/* Returns 0 on success, even if no new images are available (transient error)
* Returns - 1 on failure .
*/
int Monitor : : Capture ( ) {
static int FirstCapture = 1 ; // Used in de-interlacing to indicate whether this is the even or odd image
2016-04-29 20:57:31 +08:00
2020-07-23 01:41:27 +08:00
unsigned int index = image_count % image_buffer_count ;
2016-04-29 20:57:31 +08:00
2020-12-28 01:00:18 +08:00
ZMPacket * packet = new ZMPacket ( ) ;
packet - > timestamp = new struct timeval ;
2021-01-07 00:31:31 +08:00
packet - > image_index = image_count ;
2020-12-31 10:23:29 +08:00
gettimeofday ( packet - > timestamp , nullptr ) ;
2019-02-26 22:45:20 +08:00
2020-12-28 01:00:18 +08:00
Image * capture_image = image_buffer [ index ] . image ;
2017-10-23 21:51:41 +08:00
int captureResult = 0 ;
2016-09-13 09:35:14 +08:00
2016-09-26 23:01:47 +08:00
if ( deinterlacing_value = = 4 ) {
2016-04-29 20:57:31 +08:00
if ( FirstCapture ! = 1 ) {
/* Copy the next image into the shared memory */
2020-12-28 01:00:18 +08:00
//capture_image->CopyBuffer(*(next_buffer.image));
2016-04-29 20:57:31 +08:00
}
/* Capture a new next image */
2017-11-14 01:14:57 +08:00
captureResult = camera - > Capture ( * packet ) ;
2021-02-10 00:54:38 +08:00
// How about set shared_data->current_timestamp
2021-02-02 05:01:09 +08:00
gettimeofday ( packet - > timestamp , nullptr ) ;
2016-04-29 20:57:31 +08:00
if ( FirstCapture ) {
2016-09-13 09:35:14 +08:00
FirstCapture = 0 ;
return 0 ;
}
2016-04-29 20:57:31 +08:00
} else {
2018-02-28 09:19:39 +08:00
Debug ( 4 , " Capturing " ) ;
2017-11-14 01:14:57 +08:00
captureResult = camera - > Capture ( * packet ) ;
2021-01-12 02:35:48 +08:00
Debug ( 4 , " Back from capture result=%d " , captureResult ) ;
2020-12-28 01:00:18 +08:00
2017-11-13 00:42:34 +08:00
if ( captureResult < 0 ) {
2020-07-23 01:14:40 +08:00
Debug ( 2 , " failed capture " ) ;
2021-01-12 02:35:48 +08:00
// Unable to capture image
2017-10-23 21:51:41 +08:00
// Fake a signal loss image
2021-01-12 02:35:48 +08:00
// Not sure what to do here. We will close monitor and kill analysis_thread but what about rtsp server?
2017-10-23 21:51:41 +08:00
Rgb signalcolor ;
2017-11-13 00:42:34 +08:00
/* HTML colour code is actually BGR in memory, we want RGB */
signalcolor = rgb_convert ( signal_check_colour , ZM_SUBPIX_ORDER_BGR ) ;
2021-01-12 02:35:48 +08:00
capture_image = new Image ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ) ;
2017-10-23 21:51:41 +08:00
capture_image - > Fill ( signalcolor ) ;
2017-11-13 00:42:34 +08:00
shared_data - > signal = false ;
2018-04-14 23:03:08 +08:00
shared_data - > last_write_index = index ;
shared_data - > last_write_time = image_buffer [ index ] . timestamp - > tv_sec ;
2021-01-12 02:35:48 +08:00
image_buffer [ index ] . image - > Assign ( * capture_image ) ;
* ( image_buffer [ index ] . timestamp ) = * ( packet - > timestamp ) ;
delete capture_image ;
2018-04-14 23:03:08 +08:00
image_count + + ;
2021-01-07 00:31:31 +08:00
delete packet ;
2018-04-14 23:03:08 +08:00
// What about timestamping it?
// Don't want to do analysis on it, but we won't due to signal
2018-02-03 05:07:13 +08:00
return - 1 ;
2017-11-15 05:16:07 +08:00
} else if ( captureResult > 0 ) {
2020-12-16 04:59:46 +08:00
Debug ( 2 , " Have packet stream_index:%d ?= videostream_id:(%d) q.vpktcount(%d) event?(%d) " ,
2021-02-01 10:30:12 +08:00
packet - > packet . stream_index , video_stream_id , packetqueue . packet_count ( video_stream_id ) , ( event ? 1 : 0 ) ) ;
2017-10-23 21:51:41 +08:00
2021-01-31 09:35:10 +08:00
if ( ( packet - > packet . stream_index ! = video_stream_id ) and ! packet - > image ) {
2019-02-25 23:21:43 +08:00
// Only queue if we have some video packets in there. Should push this logic into packetqueue
2021-02-10 00:54:38 +08:00
if ( record_audio and ( packetqueue . packet_count ( video_stream_id ) or event ) ) {
packet - > image_index = - 1 ;
2020-09-26 04:19:52 +08:00
Debug ( 2 , " Queueing audio packet " ) ;
2021-02-01 10:30:12 +08:00
packetqueue . queuePacket ( packet ) ;
2021-01-07 00:31:31 +08:00
} else {
delete packet ;
2017-11-22 08:55:40 +08:00
}
2017-11-22 00:58:15 +08:00
// Don't update last_write_index because that is used for live streaming
2017-11-23 21:40:49 +08:00
//shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
2017-11-22 00:58:15 +08:00
return 1 ;
2020-12-28 01:00:18 +08:00
} // end if audio
2016-04-29 20:57:31 +08:00
2020-12-28 01:00:18 +08:00
if ( ! packet - > image ) {
if ( packet - > packet . size and ! packet - > in_frame ) {
if ( ! decoding_enabled ) {
Debug ( 1 , " Not decoding " ) ;
} else {
Debug ( 2 , " About to decode %p " , packet ) ;
2021-01-12 02:35:48 +08:00
// Allocate the image first so that it can be used by hwaccel
// We don't actually care about camera colours, pixel order etc. We care about the desired settings
//
2021-01-31 01:34:29 +08:00
//capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
2021-01-12 02:35:48 +08:00
int ret = packet - > decode ( camera - > get_VideoCodecContext ( ) ) ;
if ( ret < 0 ) {
2020-12-28 01:00:18 +08:00
Error ( " decode failed " ) ;
2021-01-12 02:35:48 +08:00
} else if ( ret = = 0 ) {
delete packet ;
return 0 ;
2020-12-28 01:00:18 +08:00
} // end if decode
} // end if decoding
2020-12-13 07:52:57 +08:00
} else {
2020-12-28 01:00:18 +08:00
Debug ( 1 , " No packet.size(%d) or packet->in_frame(%p). Not decoding " , packet - > packet . size , packet - > in_frame ) ;
}
2021-01-12 02:35:48 +08:00
if ( packet - > in_frame and ! packet - > image ) {
2021-02-10 03:53:14 +08:00
capture_image = packet - > image = new Image ( camera_width , camera_height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ) ;
2020-12-28 01:00:18 +08:00
packet - > get_image ( ) ;
}
} // end if need to decode
if ( packet - > image ) {
/* Deinterlacing */
if ( deinterlacing_value ) {
if ( deinterlacing_value = = 1 ) {
capture_image - > Deinterlace_Discard ( ) ;
} else if ( deinterlacing_value = = 2 ) {
capture_image - > Deinterlace_Linear ( ) ;
} else if ( deinterlacing_value = = 3 ) {
capture_image - > Deinterlace_Blend ( ) ;
} else if ( deinterlacing_value = = 4 ) {
capture_image - > Deinterlace_4Field ( next_buffer . image , ( deinterlacing > > 8 ) & 0xff ) ;
} else if ( deinterlacing_value = = 5 ) {
capture_image - > Deinterlace_Blend_CustomRatio ( ( deinterlacing > > 8 ) & 0xff ) ;
2020-12-13 07:52:57 +08:00
}
2017-11-28 00:28:36 +08:00
}
2016-04-29 20:57:31 +08:00
2020-12-28 01:00:18 +08:00
if ( orientation ! = ROTATE_0 ) {
Debug ( 2 , " Doing rotation " ) ;
switch ( orientation ) {
case ROTATE_0 :
// No action required
break ;
case ROTATE_90 :
case ROTATE_180 :
case ROTATE_270 :
capture_image - > Rotate ( ( orientation - 1 ) * 90 ) ;
break ;
case FLIP_HORI :
case FLIP_VERT :
capture_image - > Flip ( orientation = = FLIP_HORI ) ;
break ;
}
} // end if have rotation
2016-04-29 20:57:31 +08:00
2020-12-28 01:00:18 +08:00
if ( privacy_bitmask ) {
Debug ( 1 , " Applying privacy " ) ;
capture_image - > MaskPrivacy ( privacy_bitmask ) ;
2016-04-29 20:57:31 +08:00
}
2020-12-28 01:00:18 +08:00
if ( config . timestamp_on_capture ) {
Debug ( 1 , " Timestampprivacy " ) ;
2021-01-31 09:35:10 +08:00
TimestampImage ( packet - > image , packet - > timestamp ) ;
2020-11-12 06:42:29 +08:00
}
2017-10-23 21:51:41 +08:00
2021-01-07 22:40:24 +08:00
if ( ! ref_image . Buffer ( ) ) {
// First image, so assign it to ref image
Debug ( 1 , " Assigning ref image %dx%d size: %d " , width , height , camera - > ImageSize ( ) ) ;
ref_image . Assign ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ,
packet - > image - > Buffer ( ) , camera - > ImageSize ( ) ) ;
}
2021-01-31 09:35:10 +08:00
image_buffer [ index ] . image - > Assign ( * ( packet - > image ) ) ;
2020-12-28 01:00:18 +08:00
* ( image_buffer [ index ] . timestamp ) = * ( packet - > timestamp ) ;
} // end if have image
2017-10-23 21:51:41 +08:00
2021-01-31 09:35:10 +08:00
shared_data - > signal = ( capture_image and signal_check_points ) ? CheckSignal ( capture_image ) : true ;
2017-11-18 05:49:01 +08:00
shared_data - > last_write_index = index ;
2020-12-28 01:00:18 +08:00
shared_data - > last_write_time = packet - > timestamp - > tv_sec ;
2017-11-18 05:49:01 +08:00
image_count + + ;
2021-01-07 00:31:31 +08:00
2021-02-11 03:11:00 +08:00
// Will only be queued if there are iterators allocated in the queue.
if ( ! packetqueue . queuePacket ( packet ) ) {
2021-01-07 00:31:31 +08:00
delete packet ;
}
2020-12-28 01:00:18 +08:00
UpdateCaptureFPS ( ) ;
2017-11-15 05:16:07 +08:00
} else { // result == 0
2017-11-21 00:48:56 +08:00
// Question is, do we update last_write_index etc?
2021-01-07 00:31:31 +08:00
delete packet ;
2017-12-20 03:06:58 +08:00
return 0 ;
2017-11-15 05:16:07 +08:00
} // end if result
2017-11-13 23:17:46 +08:00
} // end if deinterlacing
2016-04-29 20:57:31 +08:00
2017-11-04 01:49:42 +08:00
// Icon: I'm not sure these should be here. They have nothing to do with capturing
if ( shared_data - > action & GET_SETTINGS ) {
shared_data - > brightness = camera - > Brightness ( ) ;
shared_data - > hue = camera - > Hue ( ) ;
shared_data - > colour = camera - > Colour ( ) ;
shared_data - > contrast = camera - > Contrast ( ) ;
shared_data - > action & = ~ GET_SETTINGS ;
}
if ( shared_data - > action & SET_SETTINGS ) {
2019-04-16 00:55:28 +08:00
camera - > Brightness ( shared_data - > brightness ) ;
camera - > Hue ( shared_data - > hue ) ;
camera - > Colour ( shared_data - > colour ) ;
camera - > Contrast ( shared_data - > contrast ) ;
2017-11-04 01:49:42 +08:00
shared_data - > action & = ~ SET_SETTINGS ;
}
return captureResult ;
2017-11-10 03:50:20 +08:00
} // end Monitor::Capture
2019-04-16 00:55:28 +08:00
void Monitor : : TimestampImage ( Image * ts_image , const struct timeval * ts_time ) const {
if ( ! label_format [ 0 ] )
return ;
// Expand the strftime macros first
char label_time_text [ 256 ] ;
strftime ( label_time_text , sizeof ( label_time_text ) , label_format , localtime ( & ts_time - > tv_sec ) ) ;
char label_text [ 1024 ] ;
const char * s_ptr = label_time_text ;
char * d_ptr = label_text ;
while ( * s_ptr & & ( ( d_ptr - label_text ) < ( unsigned int ) sizeof ( label_text ) ) ) {
if ( * s_ptr = = config . timestamp_code_char [ 0 ] ) {
bool found_macro = false ;
switch ( * ( s_ptr + 1 ) ) {
case ' N ' :
d_ptr + = snprintf ( d_ptr , sizeof ( label_text ) - ( d_ptr - label_text ) , " %s " , name ) ;
found_macro = true ;
break ;
case ' Q ' :
d_ptr + = snprintf ( d_ptr , sizeof ( label_text ) - ( d_ptr - label_text ) , " %s " , trigger_data - > trigger_showtext ) ;
found_macro = true ;
break ;
case ' f ' :
d_ptr + = snprintf ( d_ptr , sizeof ( label_text ) - ( d_ptr - label_text ) , " %02ld " , ts_time - > tv_usec / 10000 ) ;
found_macro = true ;
break ;
}
if ( found_macro ) {
s_ptr + = 2 ;
continue ;
2016-04-29 20:57:31 +08:00
}
}
2019-04-16 00:55:28 +08:00
* d_ptr + + = * s_ptr + + ;
} // end while
* d_ptr = ' \0 ' ;
2020-07-23 01:14:40 +08:00
Debug ( 2 , " annotating %s " , label_text ) ;
2019-04-26 02:49:16 +08:00
ts_image - > Annotate ( label_text , label_coord , label_size ) ;
2020-07-23 01:14:40 +08:00
Debug ( 2 , " done annotating %s " , label_text ) ;
2019-04-16 00:55:28 +08:00
} // end void Monitor::TimestampImage
2016-04-01 00:54:56 +08:00
2016-09-13 20:58:38 +08:00
bool Monitor : : closeEvent ( ) {
2019-04-16 00:55:28 +08:00
if ( ! event )
return false ;
delete event ;
2020-08-26 07:45:48 +08:00
event = nullptr ;
2021-02-08 09:44:25 +08:00
if ( shared_data )
video_store_data - > recording = ( struct timeval ) { 0 } ;
2019-04-16 00:55:28 +08:00
return true ;
} // end bool Monitor::closeEvent()
2016-04-01 00:54:56 +08:00
2019-04-16 00:55:28 +08:00
unsigned int Monitor : : DetectMotion ( const Image & comp_image , Event : : StringSet & zoneSet ) {
2016-04-29 20:57:31 +08:00
bool alarm = false ;
unsigned int score = 0 ;
2019-04-16 00:55:28 +08:00
if ( n_zones < = 0 ) return alarm ;
2016-04-29 20:57:31 +08:00
2019-04-16 00:55:28 +08:00
ref_image . Delta ( comp_image , & delta_image ) ;
2016-04-29 20:57:31 +08:00
2016-12-09 00:49:54 +08:00
if ( config . record_diag_images ) {
2020-11-13 00:53:51 +08:00
ref_image . WriteJpeg ( diag_path_ref . c_str ( ) , config . record_diag_images_fifo ) ;
delta_image . WriteJpeg ( diag_path_delta . c_str ( ) , config . record_diag_images_fifo ) ;
2016-04-29 20:57:31 +08:00
}
// Blank out all exclusion zones
2016-12-09 00:49:54 +08:00
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + ) {
2016-04-29 20:57:31 +08:00
Zone * zone = zones [ n_zone ] ;
// need previous alarmed state for preclusive zone, so don't clear just yet
2019-04-16 01:26:55 +08:00
if ( ! zone - > IsPreclusive ( ) )
2016-04-29 20:57:31 +08:00
zone - > ClearAlarm ( ) ;
2016-12-09 00:49:54 +08:00
if ( ! zone - > IsInactive ( ) ) {
2016-04-29 20:57:31 +08:00
continue ;
}
2019-04-16 01:26:55 +08:00
Debug ( 3 , " Blanking inactive zone %s " , zone - > Label ( ) ) ;
delta_image . Fill ( RGB_BLACK , zone - > GetPolygon ( ) ) ;
2019-04-16 00:55:28 +08:00
} // end foreach zone
2016-04-29 20:57:31 +08:00
// Check preclusive zones first
2016-12-09 00:49:54 +08:00
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + ) {
2016-04-29 20:57:31 +08:00
Zone * zone = zones [ n_zone ] ;
2016-12-09 00:49:54 +08:00
if ( ! zone - > IsPreclusive ( ) ) {
2016-04-29 20:57:31 +08:00
continue ;
}
int old_zone_score = zone - > Score ( ) ;
bool old_zone_alarmed = zone - > Alarmed ( ) ;
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Checking preclusive zone %s - old score: %d, state: %s " ,
zone - > Label ( ) , old_zone_score , zone - > Alarmed ( ) ? " alarmed " : " quiet " ) ;
if ( zone - > CheckAlarms ( & delta_image ) ) {
2016-04-29 20:57:31 +08:00
alarm = true ;
score + = zone - > Score ( ) ;
zone - > SetAlarm ( ) ;
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Zone is alarmed, zone score = %d " , zone - > Score ( ) ) ;
zoneSet . insert ( zone - > Label ( ) ) ;
2016-04-29 20:57:31 +08:00
//zone->ResetStats();
} else {
// check if end of alarm
2019-04-16 00:55:28 +08:00
if ( old_zone_alarmed ) {
Debug ( 3 , " Preclusive Zone %s alarm Ends. Previous score: %d " ,
zone - > Label ( ) , old_zone_score ) ;
if ( old_zone_score > 0 ) {
2016-04-29 20:57:31 +08:00
zone - > SetExtendAlarmCount ( zone - > GetExtendAlarmFrames ( ) ) ;
}
2019-04-16 00:55:28 +08:00
if ( zone - > CheckExtendAlarmCount ( ) ) {
alarm = true ;
2017-05-17 00:04:56 +08:00
zone - > SetAlarm ( ) ;
2016-04-29 20:57:31 +08:00
} else {
zone - > ClearAlarm ( ) ;
}
2018-03-09 20:23:40 +08:00
}
2019-04-16 00:55:28 +08:00
} // end if CheckAlarms
} // end foreach zone
2016-04-29 20:57:31 +08:00
Coord alarm_centre ;
int top_score = - 1 ;
2017-05-17 00:04:56 +08:00
if ( alarm ) {
2016-04-29 20:57:31 +08:00
alarm = false ;
score = 0 ;
2016-12-09 00:49:54 +08:00
} else {
2016-04-29 20:57:31 +08:00
// Find all alarm pixels in active zones
2016-12-09 00:49:54 +08:00
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + ) {
2016-04-29 20:57:31 +08:00
Zone * zone = zones [ n_zone ] ;
2016-12-09 00:49:54 +08:00
if ( ! zone - > IsActive ( ) | | zone - > IsPreclusive ( ) ) {
2016-04-29 20:57:31 +08:00
continue ;
}
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Checking active zone %s " , zone - > Label ( ) ) ;
if ( zone - > CheckAlarms ( & delta_image ) ) {
2016-04-29 20:57:31 +08:00
alarm = true ;
score + = zone - > Score ( ) ;
zone - > SetAlarm ( ) ;
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Zone is alarmed, zone score = %d " , zone - > Score ( ) ) ;
zoneSet . insert ( zone - > Label ( ) ) ;
2016-12-09 00:49:54 +08:00
if ( config . opt_control & & track_motion ) {
if ( ( int ) zone - > Score ( ) > top_score ) {
2016-04-29 20:57:31 +08:00
top_score = zone - > Score ( ) ;
alarm_centre = zone - > GetAlarmCentre ( ) ;
}
}
}
2019-04-16 00:55:28 +08:00
} // end foreach zone
2016-04-29 20:57:31 +08:00
2016-12-09 00:49:54 +08:00
if ( alarm ) {
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + ) {
2016-04-29 20:57:31 +08:00
Zone * zone = zones [ n_zone ] ;
2017-02-19 04:22:56 +08:00
// Wasn't this zone already checked above?
2016-12-09 00:49:54 +08:00
if ( ! zone - > IsInclusive ( ) ) {
2016-04-29 20:57:31 +08:00
continue ;
}
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Checking inclusive zone %s " , zone - > Label ( ) ) ;
if ( zone - > CheckAlarms ( & delta_image ) ) {
2016-04-29 20:57:31 +08:00
alarm = true ;
score + = zone - > Score ( ) ;
zone - > SetAlarm ( ) ;
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Zone is alarmed, zone score = %d " , zone - > Score ( ) ) ;
2016-04-29 20:57:31 +08:00
zoneSet . insert ( zone - > Label ( ) ) ;
2016-12-09 00:49:54 +08:00
if ( config . opt_control & & track_motion ) {
if ( zone - > Score ( ) > ( unsigned int ) top_score ) {
2016-04-29 20:57:31 +08:00
top_score = zone - > Score ( ) ;
alarm_centre = zone - > GetAlarmCentre ( ) ;
}
}
2019-04-16 00:55:28 +08:00
} // end if CheckAlarm
} // end foreach zone
2016-12-09 00:49:54 +08:00
} else {
2016-04-29 20:57:31 +08:00
// Find all alarm pixels in exclusive zones
2016-12-09 00:49:54 +08:00
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + ) {
2016-04-29 20:57:31 +08:00
Zone * zone = zones [ n_zone ] ;
2016-12-09 00:49:54 +08:00
if ( ! zone - > IsExclusive ( ) ) {
2016-04-29 20:57:31 +08:00
continue ;
}
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Checking exclusive zone %s " , zone - > Label ( ) ) ;
if ( zone - > CheckAlarms ( & delta_image ) ) {
2016-04-29 20:57:31 +08:00
alarm = true ;
score + = zone - > Score ( ) ;
zone - > SetAlarm ( ) ;
2019-04-16 00:55:28 +08:00
Debug ( 3 , " Zone is alarmed, zone score = %d " , zone - > Score ( ) ) ;
zoneSet . insert ( zone - > Label ( ) ) ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
} // end foreach zone
2017-02-19 04:22:56 +08:00
} // end if alarm or not
2019-04-16 00:55:28 +08:00
} // end if alarm
2016-04-29 20:57:31 +08:00
2016-12-09 00:49:54 +08:00
if ( top_score > 0 ) {
2016-04-29 20:57:31 +08:00
shared_data - > alarm_x = alarm_centre . X ( ) ;
shared_data - > alarm_y = alarm_centre . Y ( ) ;
2019-04-16 00:55:28 +08:00
Info ( " Got alarm centre at %d,%d, at count %d " ,
2019-04-26 00:41:48 +08:00
shared_data - > alarm_x , shared_data - > alarm_y , analysis_image_count ) ;
2016-12-09 00:49:54 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > alarm_x = shared_data - > alarm_y = - 1 ;
}
// This is a small and innocent hack to prevent scores of 0 being returned in alarm state
2019-04-16 00:55:28 +08:00
return score ? score : alarm ;
} // end MotionDetect
2005-12-23 00:46:25 +08:00
2021-02-11 06:18:21 +08:00
// TODO: Move the camera specific things to the camera classes and avoid these casts.
2019-04-16 00:55:28 +08:00
bool Monitor : : DumpSettings ( char * output , bool verbose ) {
2016-04-29 20:57:31 +08:00
output [ 0 ] = 0 ;
2009-05-08 17:47:37 +08:00
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Id : %d \n " , id ) ;
sprintf ( output + strlen ( output ) , " Name : %s \n " , name ) ;
sprintf ( output + strlen ( output ) , " Type : %s \n " , camera - > IsLocal ( ) ? " Local " : ( camera - > IsRemote ( ) ? " Remote " : " File " ) ) ;
2011-02-16 05:59:06 +08:00
# if ZM_HAS_V4L
2016-12-09 00:49:54 +08:00
if ( camera - > IsLocal ( ) ) {
2021-02-11 06:18:21 +08:00
LocalCamera * cam = static_cast < LocalCamera * > ( camera . get ( ) ) ;
sprintf ( output + strlen ( output ) , " Device : %s \n " , cam - > Device ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Channel : %d \n " , cam - > Channel ( ) ) ;
sprintf ( output + strlen ( output ) , " Standard : %d \n " , cam - > Standard ( ) ) ;
2016-12-09 00:49:54 +08:00
} else
2011-02-16 05:59:06 +08:00
# endif // ZM_HAS_V4L
2016-12-09 00:49:54 +08:00
if ( camera - > IsRemote ( ) ) {
2021-02-11 06:18:21 +08:00
RemoteCamera * cam = static_cast < RemoteCamera * > ( camera . get ( ) ) ;
sprintf ( output + strlen ( output ) , " Protocol : %s \n " , cam - > Protocol ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Host : %s \n " , cam - > Host ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Port : %s \n " , cam - > Port ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Path : %s \n " , cam - > Path ( ) . c_str ( ) ) ;
2016-12-09 00:49:54 +08:00
} else if ( camera - > IsFile ( ) ) {
2021-02-11 06:18:21 +08:00
FileCamera * cam = static_cast < FileCamera * > ( camera . get ( ) ) ;
sprintf ( output + strlen ( output ) , " Path : %s \n " , cam - > Path ( ) ) ;
2016-04-29 20:57:31 +08:00
}
2009-01-28 23:02:33 +08:00
# if HAVE_LIBAVFORMAT
2016-12-09 00:49:54 +08:00
else if ( camera - > IsFfmpeg ( ) ) {
2021-02-11 06:18:21 +08:00
FfmpegCamera * cam = static_cast < FfmpegCamera * > ( camera . get ( ) ) ;
sprintf ( output + strlen ( output ) , " Path : %s \n " , cam - > Path ( ) . c_str ( ) ) ;
2016-04-29 20:57:31 +08:00
}
2009-01-28 23:02:33 +08:00
# endif // HAVE_LIBAVFORMAT
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Width : %d \n " , camera - > Width ( ) ) ;
sprintf ( output + strlen ( output ) , " Height : %d \n " , camera - > Height ( ) ) ;
2011-02-16 05:59:06 +08:00
# if ZM_HAS_V4L
2016-12-09 00:49:54 +08:00
if ( camera - > IsLocal ( ) ) {
2021-02-11 06:18:21 +08:00
LocalCamera * cam = static_cast < LocalCamera * > ( camera . get ( ) ) ;
sprintf ( output + strlen ( output ) , " Palette : %d \n " , cam - > Palette ( ) ) ;
2016-04-29 20:57:31 +08:00
}
2011-02-16 05:59:06 +08:00
# endif // ZM_HAS_V4L
2018-03-09 20:23:40 +08:00
sprintf ( output + strlen ( output ) , " Colours : %d \n " , camera - > Colours ( ) ) ;
sprintf ( output + strlen ( output ) , " Subpixel Order : %d \n " , camera - > SubpixelOrder ( ) ) ;
sprintf ( output + strlen ( output ) , " Event Prefix : %s \n " , event_prefix ) ;
sprintf ( output + strlen ( output ) , " Label Format : %s \n " , label_format ) ;
sprintf ( output + strlen ( output ) , " Label Coord : %d,%d \n " , label_coord . X ( ) , label_coord . Y ( ) ) ;
sprintf ( output + strlen ( output ) , " Label Size : %d \n " , label_size ) ;
sprintf ( output + strlen ( output ) , " Image Buffer Count : %d \n " , image_buffer_count ) ;
sprintf ( output + strlen ( output ) , " Warmup Count : %d \n " , warmup_count ) ;
sprintf ( output + strlen ( output ) , " Pre Event Count : %d \n " , pre_event_count ) ;
sprintf ( output + strlen ( output ) , " Post Event Count : %d \n " , post_event_count ) ;
sprintf ( output + strlen ( output ) , " Stream Replay Buffer : %d \n " , stream_replay_buffer ) ;
sprintf ( output + strlen ( output ) , " Alarm Frame Count : %d \n " , alarm_frame_count ) ;
sprintf ( output + strlen ( output ) , " Section Length : %d \n " , section_length ) ;
2019-06-24 23:29:00 +08:00
sprintf ( output + strlen ( output ) , " Min Section Length : %d \n " , min_section_length ) ;
2018-03-09 20:23:40 +08:00
sprintf ( output + strlen ( output ) , " Maximum FPS : %.2f \n " , capture_delay ? ( double ) DT_PREC_3 / capture_delay : 0.0 ) ;
sprintf ( output + strlen ( output ) , " Alarm Maximum FPS : %.2f \n " , alarm_capture_delay ? ( double ) DT_PREC_3 / alarm_capture_delay : 0.0 ) ;
sprintf ( output + strlen ( output ) , " Reference Blend %%ge : %d \n " , ref_blend_perc ) ;
sprintf ( output + strlen ( output ) , " Alarm Reference Blend %%ge : %d \n " , alarm_ref_blend_perc ) ;
sprintf ( output + strlen ( output ) , " Track Motion : %d \n " , track_motion ) ;
sprintf ( output + strlen ( output ) , " Function: %d - %s \n " , function ,
2016-04-29 20:57:31 +08:00
function = = NONE ? " None " : (
function = = MONITOR ? " Monitor Only " : (
function = = MODECT ? " Motion Detection " : (
function = = RECORD ? " Continuous Record " : (
function = = MOCORD ? " Continuous Record with Motion Detection " : (
function = = NODECT ? " Externally Triggered only, no Motion Detection " : " Unknown "
) ) ) ) ) ) ;
2018-03-09 20:23:40 +08:00
sprintf ( output + strlen ( output ) , " Zones : %d \n " , n_zones ) ;
2016-12-09 00:49:54 +08:00
for ( int i = 0 ; i < n_zones ; i + + ) {
2018-03-09 20:23:40 +08:00
zones [ i ] - > DumpSettings ( output + strlen ( output ) , verbose ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
return true ;
} // bool Monitor::DumpSettings(char *output, bool verbose)
2006-01-23 02:31:55 +08:00
2018-03-09 20:23:40 +08:00
unsigned int Monitor : : Colours ( ) const { return camera - > Colours ( ) ; }
unsigned int Monitor : : SubpixelOrder ( ) const { return camera - > SubpixelOrder ( ) ; }
2017-11-28 21:29:03 +08:00
2016-06-22 01:48:32 +08:00
int Monitor : : PrimeCapture ( ) {
2017-11-22 00:58:15 +08:00
int ret = camera - > PrimeCapture ( ) ;
2020-12-17 23:16:54 +08:00
if ( ret > 0 ) {
2021-02-01 10:30:12 +08:00
//if ( packetqueue )
//delete packetqueue;
2017-12-01 20:26:34 +08:00
video_stream_id = camera - > get_VideoStreamId ( ) ;
2018-10-21 05:31:14 +08:00
audio_stream_id = camera - > get_AudioStreamId ( ) ;
2021-02-01 10:30:12 +08:00
//packetqueue = new PacketQueue(image_buffer_count, video_stream_id, audio_stream_id);
packetqueue . addStreamId ( video_stream_id ) ;
if ( audio_stream_id )
packetqueue . addStreamId ( audio_stream_id ) ;
2020-12-15 23:14:19 +08:00
Debug ( 2 , " Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d " ,
video_stream_id , audio_stream_id , pre_event_buffer_count ) ;
} else {
2020-12-17 23:16:54 +08:00
Debug ( 2 , " Failed to prime %d " , ret ) ;
2017-12-01 20:26:34 +08:00
}
2017-11-22 00:58:15 +08:00
return ret ;
2021-01-07 00:31:31 +08:00
} // end int Monitor::PrimeCapture()
2018-03-09 22:03:19 +08:00
2018-03-09 20:23:40 +08:00
int Monitor : : PreCapture ( ) const { return camera - > PreCapture ( ) ; }
2020-12-17 23:16:54 +08:00
int Monitor : : PostCapture ( ) const { return camera - > PostCapture ( ) ; }
2020-12-10 04:00:11 +08:00
int Monitor : : Close ( ) {
2021-02-11 05:16:44 +08:00
if ( camera )
camera - > Close ( ) ;
2021-02-01 10:30:12 +08:00
packetqueue . clear ( ) ;
#if 0
2020-12-10 04:00:11 +08:00
if ( packetqueue ) {
delete packetqueue ;
packetqueue = nullptr ;
2021-01-07 00:31:31 +08:00
analysis_it = nullptr ; // deleted by packetqueue
2020-12-10 04:00:11 +08:00
}
2021-02-01 10:30:12 +08:00
# endif
2021-02-03 03:49:17 +08:00
return 1 ;
2020-12-10 04:00:11 +08:00
} ;
2016-10-12 21:12:09 +08:00
Monitor : : Orientation Monitor : : getOrientation ( ) const { return orientation ; }
2017-04-11 09:51:18 +08:00
2020-12-28 01:00:18 +08:00
// Wait for camera to get an image, and then assign it as the base reference image.
// So this should be done as the first task in the analysis thread startup.
2021-02-06 00:04:02 +08:00
// This function is deprecated.
2017-10-07 23:30:41 +08:00
void Monitor : : get_ref_image ( ) {
2021-02-06 00:04:02 +08:00
ZMPacket * snap = nullptr ;
if ( ! analysis_it )
analysis_it = packetqueue . get_video_it ( true ) ;
2020-12-28 01:00:18 +08:00
while (
(
2021-02-01 10:30:12 +08:00
! ( snap = packetqueue . get_packet ( analysis_it ) )
2020-12-28 01:00:18 +08:00
or
( snap - > packet . stream_index ! = video_stream_id )
or
! snap - > image
)
and ! zm_terminate ) {
2021-01-07 22:40:24 +08:00
2019-02-25 23:21:43 +08:00
Debug ( 1 , " Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d) " ,
2018-08-12 06:49:48 +08:00
shared_data - > last_write_index , shared_data - > last_write_time ) ;
2021-02-06 00:04:02 +08:00
if ( snap and ! snap - > image ) {
2020-12-28 01:00:18 +08:00
snap - > unlock ( ) ;
// can't analyse it anyways, incremement
2021-02-01 10:30:12 +08:00
packetqueue . increment_it ( analysis_it ) ;
2020-12-28 01:00:18 +08:00
}
2019-02-25 23:21:43 +08:00
//usleep(10000);
2017-10-07 23:30:41 +08:00
}
2018-02-16 04:54:13 +08:00
if ( zm_terminate )
return ;
2017-12-09 10:29:05 +08:00
2020-12-28 01:00:18 +08:00
Debug ( 1 , " get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p " ,
snap - > packet . stream_index , video_stream_id , snap - > image_index , snap - > image ) ;
// Might not have been decoded yet FIXME
if ( snap - > image ) {
2021-01-07 00:31:31 +08:00
ref_image . Assign ( width , height , camera - > Colours ( ) ,
camera - > SubpixelOrder ( ) , snap - > image - > Buffer ( ) , camera - > ImageSize ( ) ) ;
2020-12-28 01:00:18 +08:00
Debug ( 2 , " Have ref image about to unlock " ) ;
} else {
Debug ( 2 , " Have no ref image about to unlock " ) ;
}
2019-02-25 23:21:43 +08:00
snap - > unlock ( ) ;
2017-04-11 09:51:18 +08:00
}
2018-05-03 03:20:01 +08:00
2019-04-16 00:55:28 +08:00
std : : vector < Group * > Monitor : : Groups ( ) {
2018-05-03 03:20:01 +08:00
// At the moment, only load groups once.
2019-04-16 00:55:28 +08:00
if ( ! groups . size ( ) ) {
2018-05-13 05:34:26 +08:00
std : : string sql = stringtf (
2019-08-15 04:18:21 +08:00
" SELECT `Id`, `ParentId`, `Name` FROM `Groups` WHERE `Groups.Id` IN "
2021-01-07 00:31:31 +08:00
" (SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=%d) " , id ) ;
2018-05-03 03:20:01 +08:00
MYSQL_RES * result = zmDbFetch ( sql . c_str ( ) ) ;
if ( ! result ) {
Error ( " Can't load groups: %s " , mysql_error ( & dbconn ) ) ;
return groups ;
}
int n_groups = mysql_num_rows ( result ) ;
2018-05-13 05:34:26 +08:00
Debug ( 1 , " Got %d groups " , n_groups ) ;
2018-05-03 03:20:01 +08:00
while ( MYSQL_ROW dbrow = mysql_fetch_row ( result ) ) {
2018-05-13 05:34:26 +08:00
groups . push_back ( new Group ( dbrow ) ) ;
2018-05-03 03:20:01 +08:00
}
if ( mysql_errno ( & dbconn ) ) {
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
}
mysql_free_result ( result ) ;
}
return groups ;
2019-04-16 00:55:28 +08:00
} // end Monitor::Groups()
2018-05-13 05:34:26 +08:00
StringVector Monitor : : GroupNames ( ) {
StringVector groupnames ;
2019-04-16 00:55:28 +08:00
for ( Group * g : Groups ( ) ) {
2018-05-13 05:34:26 +08:00
groupnames . push_back ( std : : string ( g - > Name ( ) ) ) ;
2019-04-16 00:55:28 +08:00
Debug ( 1 , " Groups: %s " , g - > Name ( ) ) ;
2018-05-13 05:34:26 +08:00
}
return groupnames ;
2019-04-16 00:55:28 +08:00
} // end Monitor::GroupNames()