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
2008-07-14 23:21:16 +08:00
# include <sys/types.h>
# include <sys/stat.h>
2008-12-06 07:14:20 +08:00
# include <arpa/inet.h>
2007-08-30 02:11:09 +08:00
# include <glob.h>
2018-04-25 01:16:19 +08:00
# include <cinttypes>
2003-03-26 19:57:29 +08:00
# include "zm.h"
# include "zm_db.h"
2008-07-16 16:35:59 +08:00
# include "zm_time.h"
2004-03-04 23:05:54 +08:00
# include "zm_mpeg.h"
2007-08-30 02:11:09 +08:00
# include "zm_signal.h"
2003-03-26 19:57:29 +08:00
# include "zm_monitor.h"
2013-12-20 00:38:07 +08:00
# include "zm_video.h"
2017-06-23 05:58:32 +08:00
# include "zm_eventstream.h"
2011-02-16 05:59:06 +08:00
# if ZM_HAS_V4L
2003-03-26 19:57:29 +08:00
# include "zm_local_camera.h"
2011-02-16 05:59:06 +08:00
# endif // ZM_HAS_V4L
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"
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
2005-10-18 05:55:02 +08:00
# include "zm_file_camera.h"
2009-01-20 23:01:32 +08:00
# if HAVE_LIBAVFORMAT
# include "zm_ffmpeg_camera.h"
# endif // HAVE_LIBAVFORMAT
2019-05-17 03:37:03 +08:00
# include "zm_fifo.h"
2013-12-13 01:45:29 +08:00
# if HAVE_LIBVLC
# include "zm_libvlc_camera.h"
# endif // HAVE_LIBVLC
2013-11-04 23:10:07 +08:00
# if HAVE_LIBCURL
# include "zm_curl_camera.h"
# endif // HAVE_LIBCURL
2003-03-26 19:57:29 +08:00
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 =
2019-08-15 04:18:21 +08:00
" SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `LinkedMonitors`, "
" `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`, "
" `DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
" `SaveJPEGs`, `VideoWriter`, `EncoderParameters`, "
2018-03-09 20:23:40 +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`, "
" `FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`, `SignalCheckPoints`, `SignalCheckColour` FROM `Monitors` " ;
2018-03-09 20:23:40 +08:00
std : : string CameraType_Strings [ ] = {
" Local " ,
" Remote " ,
" File " ,
" Ffmpeg " ,
" LibVLC " ,
" CURL " ,
} ;
2018-04-12 22:28:22 +08:00
2018-03-09 20:23:40 +08:00
2013-03-17 07:45:21 +08:00
std : : vector < std : : string > split ( const std : : string & s , char delim ) {
2016-04-29 20:57:31 +08:00
std : : vector < std : : string > elems ;
std : : stringstream ss ( s ) ;
std : : string item ;
while ( std : : getline ( ss , item , delim ) ) {
elems . push_back ( trimSpaces ( item ) ) ;
}
return elems ;
2013-03-17 07:45:21 +08:00
}
2017-11-28 22:50:09 +08:00
Monitor : : MonitorLink : : MonitorLink ( int p_id , const char * p_name ) :
id ( p_id ) ,
shared_data ( NULL ) ,
trigger_data ( NULL ) ,
video_store_data ( NULL )
{
2017-12-13 02:42:48 +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 ;
2017-06-13 09:39:37 +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 ;
mem_ptr = 0 ;
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
last_event = 0 ;
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-06-24 04:56:35 +08:00
if ( ! last_connect_time | | ( time ( 0 ) - last_connect_time ) > ZM_MAX_RESTART_DELAY ) {
2016-04-29 20:57:31 +08:00
last_connect_time = time ( 0 ) ;
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-24 04:56:35 +08:00
Debug ( 1 , " link.mem.size=%d " , mem_size ) ;
2008-07-14 23:21:16 +08:00
# if ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
map_fd = open ( mem_file , O_RDWR , ( mode_t ) 0600 ) ;
if ( map_fd < 0 ) {
Debug ( 3 , " Can't open linked memory map file %s: %s " , mem_file , strerror ( errno ) ) ;
disconnect ( ) ;
return ( false ) ;
}
while ( map_fd < = 2 ) {
int new_map_fd = dup ( map_fd ) ;
Warning ( " Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d " , map_fd , new_map_fd ) ;
close ( map_fd ) ;
map_fd = new_map_fd ;
}
struct stat map_stat ;
if ( fstat ( map_fd , & map_stat ) < 0 ) {
Error ( " Can't stat linked memory map file %s: %s " , mem_file , strerror ( errno ) ) ;
disconnect ( ) ;
return ( false ) ;
}
if ( map_stat . st_size = = 0 ) {
Error ( " Linked memory map file %s is empty: %s " , mem_file , strerror ( errno ) ) ;
disconnect ( ) ;
return ( false ) ;
} else if ( map_stat . st_size < mem_size ) {
Error ( " Got unexpected memory map file size %ld, expected %d " , map_stat . st_size , mem_size ) ;
disconnect ( ) ;
return ( false ) ;
}
mem_ptr = ( unsigned char * ) mmap ( NULL , mem_size , PROT_READ | PROT_WRITE , MAP_SHARED , map_fd , 0 ) ;
if ( mem_ptr = = MAP_FAILED ) {
Error ( " Can't map file %s (%d bytes) to memory: %s " , mem_file , mem_size , strerror ( errno ) ) ;
disconnect ( ) ;
return ( false ) ;
}
2008-07-14 23:21:16 +08:00
# else // ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
shm_id = shmget ( ( config . shm_key & 0xffff0000 ) | id , mem_size , 0700 ) ;
if ( shm_id < 0 ) {
Debug ( 3 , " Can't shmget link memory: %s " , strerror ( errno ) ) ;
connected = false ;
return ( false ) ;
}
mem_ptr = ( unsigned char * ) shmat ( shm_id , 0 , 0 ) ;
2017-05-11 01:16:08 +08:00
if ( mem_ptr < ( void * ) 0 ) {
2016-04-29 20:57:31 +08:00
Debug ( 3 , " Can't shmat link memory: %s " , strerror ( errno ) ) ;
connected = false ;
return ( false ) ;
}
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 ) {
Debug ( 3 , " Linked memory not initialised by capture daemon " ) ;
disconnect ( ) ;
return ( false ) ;
}
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
last_state = shared_data - > state ;
last_event = shared_data - > last_event ;
connected = true ;
2006-01-23 02:31:55 +08:00
2016-04-29 20:57:31 +08:00
return ( true ) ;
}
return ( false ) ;
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 ;
mem_ptr = 0 ;
}
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 ;
} else if ( shared_data - > last_event ! = last_event ) {
2016-04-29 20:57:31 +08:00
last_event = shared_data - > last_event ;
}
2018-04-13 04:40:11 +08:00
return false ;
2006-01-23 02:31:55 +08:00
}
2003-09-23 17:52:45 +08:00
Monitor : : Monitor (
2016-04-29 20:57:31 +08:00
int p_id ,
const char * p_name ,
const unsigned int p_server_id ,
const unsigned int p_storage_id ,
int p_function ,
bool p_enabled ,
const char * p_linked_monitors ,
Camera * p_camera ,
int p_orientation ,
unsigned int p_deinterlacing ,
2019-06-26 03:34:17 +08:00
const std : : string & p_decoder_hwaccel_name ,
const std : : string & p_decoder_hwaccel_device ,
2016-04-29 20:57:31 +08:00
int p_savejpegs ,
2016-09-13 09:35:14 +08:00
VideoWriter p_videowriter ,
2016-04-29 20:57:31 +08:00
std : : string p_encoderparams ,
bool p_record_audio ,
const char * p_event_prefix ,
const char * p_label_format ,
const Coord & p_label_coord ,
int p_label_size ,
int p_image_buffer_count ,
int p_warmup_count ,
int p_pre_event_count ,
int p_post_event_count ,
int p_stream_replay_buffer ,
int p_alarm_frame_count ,
int p_section_length ,
2019-06-24 23:29:00 +08:00
int p_min_section_length ,
2016-04-29 20:57:31 +08:00
int p_frame_skip ,
int p_motion_frame_skip ,
2019-07-31 23:23:02 +08:00
double p_capture_max_fps ,
2016-04-29 20:57:31 +08:00
double p_analysis_fps ,
unsigned int p_analysis_update_delay ,
int p_capture_delay ,
int p_alarm_capture_delay ,
int p_fps_report_interval ,
int p_ref_blend_perc ,
int p_alarm_ref_blend_perc ,
bool p_track_motion ,
2018-04-19 22:10:14 +08:00
int p_signal_check_points ,
2016-04-29 20:57:31 +08:00
Rgb p_signal_check_colour ,
bool p_embed_exif ,
Purpose p_purpose ,
int p_n_zones ,
Zone * p_zones [ ]
2003-09-23 17:52:45 +08:00
) : id ( p_id ) ,
2016-04-29 20:57:31 +08:00
server_id ( p_server_id ) ,
storage_id ( p_storage_id ) ,
function ( ( Function ) p_function ) ,
enabled ( p_enabled ) ,
2016-04-26 03:57:39 +08:00
width ( ( p_orientation = = ROTATE_90 | | p_orientation = = ROTATE_270 ) ? p_camera - > Height ( ) : p_camera - > Width ( ) ) ,
height ( ( p_orientation = = ROTATE_90 | | p_orientation = = ROTATE_270 ) ? p_camera - > Width ( ) : p_camera - > Height ( ) ) ,
2016-04-29 20:57:31 +08:00
orientation ( ( Orientation ) p_orientation ) ,
deinterlacing ( p_deinterlacing ) ,
2019-06-26 03:34:17 +08:00
decoder_hwaccel_name ( p_decoder_hwaccel_name ) ,
decoder_hwaccel_device ( p_decoder_hwaccel_device ) ,
2018-03-09 20:23:40 +08:00
savejpegs ( p_savejpegs ) ,
2016-09-13 09:35:14 +08:00
videowriter ( p_videowriter ) ,
2016-04-29 20:57:31 +08:00
encoderparams ( p_encoderparams ) ,
record_audio ( p_record_audio ) ,
label_coord ( p_label_coord ) ,
label_size ( p_label_size ) ,
image_buffer_count ( p_image_buffer_count ) ,
warmup_count ( p_warmup_count ) ,
pre_event_count ( p_pre_event_count ) ,
post_event_count ( p_post_event_count ) ,
2019-11-02 05:29:55 +08:00
video_buffer_duration ( { 0 } ) ,
2016-04-29 20:57:31 +08:00
stream_replay_buffer ( p_stream_replay_buffer ) ,
section_length ( p_section_length ) ,
2019-06-24 23:29:00 +08:00
min_section_length ( p_min_section_length ) ,
2016-04-29 20:57:31 +08:00
frame_skip ( p_frame_skip ) ,
motion_frame_skip ( p_motion_frame_skip ) ,
2019-07-31 23:23:02 +08:00
capture_max_fps ( p_capture_max_fps ) ,
2016-04-29 20:57:31 +08:00
analysis_fps ( p_analysis_fps ) ,
analysis_update_delay ( p_analysis_update_delay ) ,
capture_delay ( p_capture_delay ) ,
alarm_capture_delay ( p_alarm_capture_delay ) ,
alarm_frame_count ( p_alarm_frame_count ) ,
fps_report_interval ( p_fps_report_interval ) ,
ref_blend_perc ( p_ref_blend_perc ) ,
alarm_ref_blend_perc ( p_alarm_ref_blend_perc ) ,
track_motion ( p_track_motion ) ,
2018-04-19 22:10:14 +08:00
signal_check_points ( p_signal_check_points ) ,
2016-04-29 20:57:31 +08:00
signal_check_colour ( p_signal_check_colour ) ,
embed_exif ( p_embed_exif ) ,
delta_image ( width , height , ZM_COLOUR_GRAY8 , ZM_SUBPIX_ORDER_NONE ) ,
ref_image ( width , height , p_camera - > Colours ( ) , p_camera - > SubpixelOrder ( ) ) ,
purpose ( p_purpose ) ,
last_motion_score ( 0 ) ,
camera ( p_camera ) ,
n_zones ( p_n_zones ) ,
zones ( p_zones ) ,
timestamps ( 0 ) ,
images ( 0 ) ,
2018-03-02 11:22:22 +08:00
privacy_bitmask ( NULL ) ,
event_delete_thread ( NULL )
2003-03-26 19:57:29 +08:00
{
2019-11-02 05:29:55 +08:00
if ( analysis_fps > 0.0 ) {
uint64_t usec = round ( 1000000 * pre_event_count / analysis_fps ) ;
video_buffer_duration . tv_sec = usec / 1000000 ;
video_buffer_duration . tv_usec = usec % 1000000 ;
}
2019-06-26 03:34:17 +08:00
strncpy ( name , p_name , sizeof ( name ) - 1 ) ;
2016-04-29 20:57:31 +08:00
2019-04-16 01:26:55 +08:00
strncpy ( event_prefix , p_event_prefix , sizeof ( event_prefix ) - 1 ) ;
strncpy ( label_format , p_label_format , sizeof ( label_format ) - 1 ) ;
2020-07-31 03:06:05 +08:00
Debug ( 1 , " encoder params %s " , encoderparams . c_str ( ) ) ;
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 ;
}
}
/* Parse encoder parameters */
ParseEncoderParameters ( encoderparams . c_str ( ) , & encoderparamsvec ) ;
fps = 0.0 ;
2018-04-25 02:11:27 +08:00
last_camera_bytes = 0 ;
2016-04-29 20:57:31 +08:00
event_count = 0 ;
image_count = 0 ;
ready_count = warmup_count ;
first_alarm_count = 0 ;
last_alarm_count = 0 ;
state = IDLE ;
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 ;
auto_resume_time = 0 ;
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 ;
Debug ( 1 , " monitor purpose=%d " , purpose ) ;
mem_size = sizeof ( SharedData )
+ sizeof ( TriggerData )
+ sizeof ( VideoStoreData ) //Information to pass back to the capture process
+ ( image_buffer_count * sizeof ( struct timeval ) )
+ ( image_buffer_count * camera - > ImageSize ( ) )
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 ) ) ,
image_buffer_count , camera - > ImageSize ( ) , ( image_buffer_count * camera - > ImageSize ( ) ) ,
mem_size ) ;
2016-04-29 20:57:31 +08:00
mem_ptr = NULL ;
2018-04-24 23:41:54 +08:00
storage = new Storage ( storage_id ) ;
Debug ( 1 , " Storage path: %s " , storage - > Path ( ) ) ;
2017-01-18 22:31:08 +08:00
// Should maybe store this for later use
2016-11-28 09:10:12 +08:00
char monitor_dir [ PATH_MAX ] = " " ;
2018-04-24 23:41:54 +08:00
snprintf ( monitor_dir , sizeof ( monitor_dir ) , " %s/%d " , storage - > Path ( ) , id ) ;
2016-11-28 09:10:12 +08:00
2018-03-09 20:23:40 +08:00
if ( purpose = = CAPTURE ) {
2018-08-10 23:14:41 +08:00
if ( mkdir ( monitor_dir , 0755 ) ) {
if ( errno ! = EEXIST ) {
Error ( " Can't mkdir %s: %s " , monitor_dir , strerror ( errno ) ) ;
2016-11-28 09:10:12 +08:00
}
2017-01-18 22:31:08 +08:00
}
2016-05-07 00:39:10 +08:00
2017-08-12 00:03:37 +08:00
if ( ! this - > connect ( ) ) {
Error ( " unable to connect, but doing capture " ) ;
exit ( - 1 ) ;
}
2018-03-09 20:23:40 +08:00
2019-04-16 01:26:55 +08:00
memset ( mem_ptr , 0 , mem_size ) ;
2016-04-29 20:57:31 +08:00
shared_data - > size = sizeof ( SharedData ) ;
shared_data - > active = enabled ;
shared_data - > signal = false ;
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 = 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 ( ) ;
2018-03-13 07:38:12 +08:00
shared_data - > alarm_cause [ 0 ] = 0 ;
2016-04-29 20:57:31 +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 ;
2017-04-13 01:36:39 +08:00
video_store_data - > recording = ( struct timeval ) { 0 } ;
2016-04-29 20:57:31 +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;
} else if ( purpose = = ANALYSIS ) {
2017-12-13 01:37:49 +08:00
if ( ! ( this - > connect ( ) & & mem_ptr ) ) exit ( - 1 ) ;
2016-04-29 20:57:31 +08:00
shared_data - > state = IDLE ;
shared_data - > last_read_time = 0 ;
shared_data - > alarm_x = - 1 ;
shared_data - > alarm_y = - 1 ;
2020-10-09 04:46:30 +08:00
} else {
shared_data = nullptr ;
2016-04-29 20:57:31 +08:00
}
if ( ( ! mem_ptr ) | | ! shared_data - > valid ) {
if ( purpose ! = QUERY ) {
2018-03-09 20:23:40 +08:00
Error ( " Shared data not initialised by capture daemon for monitor %s " , name ) ;
exit ( - 1 ) ;
2016-04-29 20:57:31 +08:00
}
}
start_time = last_fps_time = time ( 0 ) ;
event = 0 ;
2018-12-28 00:50:43 +08:00
Debug ( 1 , " Monitor %s has function %d, \n "
" label format = '%s', label X = %d, label Y = %d, label size = %d, \n "
" image buffer count = %d, warmup count = %d, pre-event count = %d, post-event count = %d, alarm frame count = %d, \n "
" fps report interval = %d, ref blend percentage = %d, alarm ref blend percentage = %d, track motion = %d " ,
name , function ,
label_format , label_coord . X ( ) , label_coord . Y ( ) , label_size ,
image_buffer_count , warmup_count , pre_event_count , post_event_count , alarm_frame_count ,
fps_report_interval , ref_blend_perc , alarm_ref_blend_perc , track_motion ) ;
2016-04-29 20:57:31 +08:00
2016-09-27 08:08:09 +08:00
//Set video recording flag for event start constructor and easy reference in code
videoRecording = ( ( GetOptVideoWriter ( ) = = H264PASSTHROUGH ) & & camera - > SupportsNativeVideo ( ) ) ;
2017-12-13 01:37:49 +08:00
n_linked_monitors = 0 ;
linked_monitors = 0 ;
2016-04-29 20:57:31 +08:00
if ( purpose = = ANALYSIS ) {
2018-03-09 20:23:40 +08:00
while (
2017-11-29 00:11:41 +08:00
( shared_data - > last_write_index = = ( unsigned int ) image_buffer_count )
& &
2018-03-09 20:23:40 +08:00
( shared_data - > last_write_time = = 0 )
& &
2018-01-12 23:12:54 +08:00
( ! zm_terminate )
2017-11-29 00:11:41 +08:00
) {
2018-12-28 00:50:43 +08:00
Debug ( 1 , " Waiting for capture daemon last_write_index(%d), last_write_time(%d) " ,
shared_data - > last_write_index , shared_data - > last_write_time ) ;
2018-01-29 04:16:06 +08:00
sleep ( 1 ) ;
2016-04-29 20:57:31 +08:00
}
2020-07-08 04:14:38 +08:00
2019-04-16 01:26:55 +08:00
ref_image . Assign ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ,
image_buffer [ shared_data - > last_write_index ] . image - > Buffer ( ) , camera - > ImageSize ( ) ) ;
2016-04-29 20:57:31 +08:00
adaptive_skip = true ;
2018-12-28 00:50:43 +08:00
ReloadLinkedMonitors ( p_linked_monitors ) ;
2019-04-16 01:26:55 +08:00
if ( config . record_diag_images ) {
2019-05-17 03:37:03 +08:00
diag_path_r = stringtf ( config . record_diag_images_fifo ? " %s/%d/diagpipe-r.jpg " : " %s/%d/diag-r.jpg " , storage - > Path ( ) , id ) ;
diag_path_d = stringtf ( config . record_diag_images_fifo ? " %s/%d/diagpipe-d.jpg " : " %s/%d/diag-d.jpg " , storage - > Path ( ) , id ) ;
if ( config . record_diag_images_fifo ) {
FifoStream : : fifo_create_if_missing ( diag_path_r . c_str ( ) ) ;
FifoStream : : fifo_create_if_missing ( diag_path_d . c_str ( ) ) ;
}
2019-04-16 01:26:55 +08:00
}
2018-12-28 00:50:43 +08:00
} // end if purpose == ANALYSIS
2017-10-20 05:12:36 +08:00
} // Monitor::Monitor
2003-03-26 19:57:29 +08:00
2014-06-27 02:54:47 +08:00
bool Monitor : : connect ( ) {
2018-03-09 20:50:47 +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
2018-12-28 00:50:43 +08:00
mem_ptr = ( unsigned char * ) mmap ( NULL , 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
2018-12-28 00:50:43 +08:00
mem_ptr = ( unsigned char * ) mmap ( NULL , mem_size , PROT_READ | PROT_WRITE , MAP_SHARED , map_fd , 0 ) ;
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
2017-08-12 00:03:37 +08:00
if ( mem_ptr = = MAP_FAILED )
2018-12-28 00:50:43 +08:00
Fatal ( " Can't map file %s (%d bytes) to memory: %s(%d) " , mem_file , mem_size , strerror ( errno ) , errno ) ;
2017-08-12 00:03:37 +08:00
if ( mem_ptr = = NULL ) {
2020-07-07 21:16:42 +08:00
Error ( " mmap gave a NULL address: " ) ;
2017-08-12 00:03:37 +08:00
} else {
2018-12-28 00:50:43 +08:00
Debug ( 3 , " mmapped to %p " , mem_ptr ) ;
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
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 ) ) ;
struct timeval * shared_timestamps = ( struct timeval * ) ( ( char * ) video_store_data + sizeof ( VideoStoreData ) ) ;
unsigned char * shared_images = ( unsigned char * ) ( ( char * ) shared_timestamps + ( image_buffer_count * sizeof ( struct timeval ) ) ) ;
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
}
2018-12-28 00:50:43 +08:00
Debug ( 3 , " Allocating %d image buffers " , image_buffer_count ) ;
2018-02-24 08:01:42 +08:00
image_buffer = new Snapshot [ image_buffer_count ] ;
for ( int i = 0 ; i < image_buffer_count ; i + + ) {
image_buffer [ i ] . timestamp = & ( shared_timestamps [ i ] ) ;
2020-08-06 22:11:00 +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 */
}
if ( ( deinterlacing & 0xff ) = = 4 ) {
/* Four field motion adaptive deinterlacing in use */
/* Allocate a buffer for the next image */
next_buffer . image = new Image ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ) ;
next_buffer . timestamp = new struct timeval ;
}
2018-08-01 01:36:03 +08:00
if ( purpose = = ANALYSIS ) {
if ( analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1 ;
pre_event_buffer = new Snapshot [ pre_event_buffer_count ] ;
for ( int i = 0 ; i < pre_event_buffer_count ; i + + ) {
pre_event_buffer [ i ] . timestamp = new struct timeval ;
* pre_event_buffer [ i ] . timestamp = { 0 , 0 } ;
pre_event_buffer [ i ] . image = new Image ( width , height , camera - > Colours ( ) , camera - > SubpixelOrder ( ) ) ;
}
2018-12-28 00:50:43 +08:00
} // end if max_analysis_fps
2018-05-27 01:01:30 +08:00
timestamps = new struct timeval * [ pre_event_count ] ;
images = new Image * [ pre_event_count ] ;
last_signal = shared_data - > signal ;
2018-12-28 00:50:43 +08:00
} // end if purpose == ANALYSIS
2017-08-12 00:03:37 +08:00
Debug ( 3 , " Success connecting " ) ;
2016-04-29 20:57:31 +08:00
return true ;
2018-12-28 00:50:43 +08:00
} // end Monitor::connect
2014-06-27 02:54:47 +08:00
2016-06-22 02:02:36 +08:00
Monitor : : ~ Monitor ( ) {
2018-12-21 02:41:57 +08:00
if ( n_linked_monitors ) {
for ( int i = 0 ; i < n_linked_monitors ; i + + ) {
delete linked_monitors [ i ] ;
}
delete [ ] linked_monitors ;
linked_monitors = 0 ;
}
2016-04-29 20:57:31 +08:00
if ( timestamps ) {
delete [ ] timestamps ;
timestamps = 0 ;
}
if ( images ) {
delete [ ] images ;
images = 0 ;
}
if ( privacy_bitmask ) {
delete [ ] privacy_bitmask ;
privacy_bitmask = NULL ;
}
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 ( ) ;
2018-03-29 02:03:56 +08:00
// closeEvent may start another thread to close the event, so wait for it to finish
if ( event_delete_thread ) {
event_delete_thread - > join ( ) ;
delete event_delete_thread ;
event_delete_thread = NULL ;
}
2016-09-13 21:36:45 +08:00
}
2016-04-29 20:57:31 +08:00
2016-06-22 02:02:36 +08:00
if ( ( deinterlacing & 0xff ) = = 4 ) {
2016-04-29 20:57:31 +08:00
delete next_buffer . image ;
delete next_buffer . timestamp ;
}
2016-06-22 02:02:36 +08:00
for ( int i = 0 ; i < image_buffer_count ; i + + ) {
2016-04-29 20:57:31 +08:00
delete image_buffer [ i ] . image ;
}
delete [ ] image_buffer ;
} // end if mem_ptr
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 ;
delete camera ;
delete storage ;
if ( mem_ptr ) {
2016-06-22 02:02:36 +08:00
if ( purpose = = ANALYSIS ) {
2016-04-29 20:57:31 +08:00
shared_data - > state = state = IDLE ;
shared_data - > last_read_index = image_buffer_count ;
shared_data - > last_read_time = 0 ;
2016-06-22 02:02:36 +08:00
if ( analysis_fps ) {
for ( int i = 0 ; i < pre_event_buffer_count ; i + + ) {
2016-04-29 20:57:31 +08:00
delete pre_event_buffer [ i ] . image ;
delete pre_event_buffer [ i ] . timestamp ;
}
delete [ ] pre_event_buffer ;
}
2016-06-22 02:02:36 +08:00
} else if ( purpose = = CAPTURE ) {
2016-04-29 20:57:31 +08:00
shared_data - > valid = false ;
memset ( mem_ptr , 0 , mem_size ) ;
}
2006-01-23 02:31:55 +08:00
2008-07-14 23:21:16 +08:00
# if ZM_MEM_MAPPED
2018-12-28 00:50:43 +08:00
if ( msync ( mem_ptr , mem_size , MS_SYNC ) < 0 )
Error ( " Can't msync: %s " , strerror ( errno ) ) ;
if ( munmap ( mem_ptr , mem_size ) < 0 )
Fatal ( " Can't munmap: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
close ( map_fd ) ;
2016-11-14 06:40:54 +08:00
2016-11-15 09:01:00 +08:00
if ( purpose = = CAPTURE ) {
2017-06-14 06:02:28 +08:00
// How about we store this in the object on instantiation so that we don't have to do this again.
2017-05-17 00:04:56 +08:00
char mmap_path [ PATH_MAX ] = " " ;
2018-12-28 00:50:43 +08:00
snprintf ( mmap_path , sizeof ( mmap_path ) , " %s/zm.mmap.%d " , staticConfig . PATH_MAP . c_str ( ) , id ) ;
2016-11-14 06:40:54 +08:00
2018-12-28 00:50:43 +08:00
if ( unlink ( mmap_path ) < 0 ) {
Warning ( " Can't unlink '%s': %s " , mmap_path , strerror ( errno ) ) ;
2017-05-17 00:04:56 +08:00
}
2016-11-14 06:40:54 +08:00
}
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 ;
2018-12-28 00:50:43 +08:00
if ( shmctl ( shm_id , IPC_STAT , & shm_data ) < 0 ) {
Fatal ( " Can't shmctl: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
}
if ( shm_data . shm_nattch < = 1 ) {
2018-12-28 00:50:43 +08:00
if ( shmctl ( shm_id , IPC_RMID , 0 ) < 0 ) {
Fatal ( " Can't shmctl: %s " , strerror ( errno ) ) ;
2016-04-29 20:57:31 +08:00
}
}
2008-07-14 23:21:16 +08:00
# endif // ZM_MEM_MAPPED
2016-04-29 20:57:31 +08:00
} // end if mem_ptr
2006-01-23 02:31:55 +08:00
}
2016-06-22 02:02:36 +08:00
void Monitor : : AddZones ( int p_n_zones , Zone * p_zones [ ] ) {
2016-04-29 20:57:31 +08:00
for ( int i = 0 ; i < n_zones ; i + + )
delete zones [ i ] ;
delete [ ] zones ;
n_zones = p_n_zones ;
zones = p_zones ;
2003-03-26 19:57:29 +08:00
}
2016-06-22 02:02:36 +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 ;
2017-12-13 01:52:30 +08:00
privacy_bitmask = NULL ;
}
2016-04-29 20:57:31 +08:00
Image * privacy_image = NULL ;
2016-06-22 02:02:36 +08:00
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( p_zones [ i ] - > IsPrivacy ( ) ) {
if ( ! privacy_image ) {
2016-04-29 20:57:31 +08:00
privacy_image = new Image ( width , height , 1 , ZM_SUBPIX_ORDER_NONE ) ;
privacy_image - > Clear ( ) ;
}
privacy_image - > Fill ( 0xff , p_zones [ i ] - > GetPolygon ( ) ) ;
privacy_image - > Outline ( 0xff , p_zones [ i ] - > GetPolygon ( ) ) ;
}
} // end foreach zone
if ( privacy_image )
privacy_bitmask = privacy_image - > Buffer ( ) ;
2015-08-20 20:20:41 +08:00
}
2016-06-22 02:02:36 +08:00
Monitor : : State Monitor : : GetState ( ) const {
2018-11-27 05:20:52 +08:00
return ( State ) shared_data - > state ;
2003-03-26 19:57:29 +08:00
}
2016-06-22 02:02:36 +08:00
int Monitor : : GetImage ( int index , int scale ) {
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 ) ) {
Snapshot * snap = & image_buffer [ index ] ;
Image * snap_image = snap - > image ;
2019-05-07 00:16:06 +08:00
alarm_image . Assign ( * snap_image ) ;
2016-04-29 20:57:31 +08:00
//write_image.Assign( *snap_image );
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
}
2016-06-22 02:02:36 +08:00
struct timeval Monitor : : GetTimestamp ( int index ) const {
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
Snapshot * snap = & image_buffer [ index ] ;
2019-05-07 00:16:06 +08:00
return * ( snap - > timestamp ) ;
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
static struct timeval null_tv = { 0 , 0 } ;
2019-05-07 00:16:06 +08:00
return null_tv ;
2016-04-29 20:57:31 +08:00
}
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-12-05 07:23:08 +08:00
#if 0
2018-04-18 01:51:20 +08:00
Debug ( 2 , " mem_ptr(%x), State(%d) last_read_index(%d) last_read_time(%d) last_event(% " PRIu64 " ) " ,
2017-11-29 00:55:47 +08:00
mem_ptr ,
shared_data - > state ,
shared_data - > last_read_index ,
shared_data - > last_read_time ,
shared_data - > last_event
) ;
2018-12-05 07:23:08 +08:00
# endif
2018-04-13 04:40:11 +08:00
return shared_data - > last_event ;
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 {
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 ;
2016-06-22 02:02:36 +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
}
Snapshot * snap1 = & image_buffer [ index1 ] ;
2016-06-22 02:02:36 +08:00
if ( ! snap1 - > timestamp | | ! 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 ;
int image_count = image_buffer_count ;
int index2 = ( index1 + 1 ) % image_buffer_count ;
Snapshot * 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
while ( ! snap2 - > timestamp | | ! 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 ;
snap2 = & image_buffer [ index2 ] ;
image_count - - ;
}
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 ;
}
2016-04-29 20:57:31 +08:00
double curr_fps = image_count / time_diff ;
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
}
2016-06-22 02:02:36 +08:00
useconds_t Monitor : : GetAnalysisRate ( ) {
2016-04-29 20:57:31 +08:00
double capturing_fps = GetFPS ( ) ;
2016-06-22 02:02:36 +08:00
if ( ! analysis_fps ) {
2019-04-16 00:55:28 +08:00
return 0 ;
2016-06-22 02:02:36 +08:00
} else if ( analysis_fps > capturing_fps ) {
2019-04-16 00:55:28 +08:00
Warning ( " Analysis fps (%.2f) is greater than capturing fps (%.2f) " , analysis_fps , capturing_fps ) ;
return 0 ;
2016-06-22 02:02:36 +08:00
} else {
2019-04-16 00:55:28 +08:00
return ( ( 1000000 / analysis_fps ) - ( 1000000 / capturing_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 ) {
2016-04-29 20:57:31 +08:00
double capturing_fps = GetFPS ( ) ;
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
}
}
Image * zone_image = NULL ;
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 ;
Snapshot * snap = & image_buffer [ 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 ;
stream = NULL ;
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
2016-06-22 02:02:36 +08:00
bool Monitor : : Analyse ( ) {
if ( shared_data - > last_read_index = = shared_data - > last_write_index ) {
2016-09-27 08:08:09 +08:00
// I wonder how often this happens. Maybe if this happens we should sleep or something?
2018-05-27 01:01:30 +08:00
return false ;
2016-04-29 20:57:31 +08:00
}
struct timeval now ;
2018-05-27 01:01:30 +08:00
gettimeofday ( & now , NULL ) ;
2016-04-29 20:57:31 +08:00
2016-06-22 02:02:36 +08:00
if ( image_count & & fps_report_interval & & ! ( image_count % fps_report_interval ) ) {
2018-02-27 09:08:05 +08:00
if ( now . tv_sec ! = last_fps_time ) {
double new_fps = double ( fps_report_interval ) / ( now . tv_sec - last_fps_time ) ;
Info ( " %s: %d - Analysing at %.2f fps " , name , image_count , new_fps ) ;
if ( fps ! = new_fps ) {
fps = new_fps ;
2018-04-28 04:20:38 +08:00
db_mutex . lock ( ) ;
2018-02-27 09:08:05 +08:00
static 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 , fps , fps ) ;
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
}
2018-04-05 23:30:14 +08:00
db_mutex . unlock ( ) ;
2018-02-27 09:08:05 +08:00
} // end if fps != new_fps
2018-02-06 11:59:22 +08:00
2018-02-27 09:08:05 +08:00
last_fps_time = now . tv_sec ;
}
2016-04-29 20:57:31 +08:00
}
int index ;
2016-06-22 02:02:36 +08:00
if ( adaptive_skip ) {
2018-05-27 01:01:30 +08:00
// I think the idea behind adaptive skip is if we are falling behind, then skip a bunch, but not all
2016-04-29 20:57:31 +08:00
int read_margin = shared_data - > last_read_index - shared_data - > last_write_index ;
if ( read_margin < 0 ) read_margin + = image_buffer_count ;
int step = 1 ;
2016-04-30 20:27:10 +08:00
// Isn't read_margin always > 0 here?
2016-06-22 02:02:36 +08:00
if ( read_margin > 0 ) {
2016-04-30 20:27:10 +08:00
// TODO explain this so... 90% of image buffer / 50% of read margin?
2016-04-29 20:57:31 +08:00
step = ( 9 * image_buffer_count ) / ( 5 * read_margin ) ;
}
int pending_frames = shared_data - > last_write_index - shared_data - > last_read_index ;
if ( pending_frames < 0 ) pending_frames + = image_buffer_count ;
2018-05-27 01:01:30 +08:00
Debug ( 4 ,
" ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d " ,
shared_data - > last_read_index , shared_data - > last_write_index , pending_frames , read_margin , step
) ;
2016-06-22 02:02:36 +08:00
if ( step < = pending_frames ) {
2016-04-29 20:57:31 +08:00
index = ( shared_data - > last_read_index + step ) % image_buffer_count ;
2016-06-22 02:02:36 +08:00
} else {
if ( pending_frames ) {
2019-04-16 00:55:28 +08:00
Warning ( " Approaching buffer overrun, consider slowing capture, simplifying analysis or increasing ring buffer size " ) ;
2016-04-29 20:57:31 +08:00
}
index = shared_data - > last_write_index % image_buffer_count ;
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
index = shared_data - > last_write_index % image_buffer_count ;
}
Snapshot * snap = & image_buffer [ index ] ;
struct timeval * timestamp = snap - > timestamp ;
Image * snap_image = snap - > image ;
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 ;
}
2016-06-22 02:02:36 +08:00
if ( shared_data - > action & RESUME ) {
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 ;
ref_image = * snap_image ;
ready_count = image_count + ( warmup_count / 2 ) ;
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 ;
ref_image = * snap_image ;
ready_count = image_count + ( warmup_count / 2 ) ;
auto_resume_time = 0 ;
}
2016-06-22 02:02:36 +08:00
if ( Enabled ( ) ) {
2016-04-29 20:57:31 +08:00
bool signal = shared_data - > signal ;
bool signal_change = ( signal ! = last_signal ) ;
2016-06-22 01:48:32 +08:00
Debug ( 3 , " Motion detection is enabled signal(%d) signal_change(%d) " , signal , signal_change ) ;
2018-03-09 20:23:40 +08:00
2016-06-22 02:02:36 +08:00
if ( trigger_data - > trigger_state ! = TRIGGER_OFF ) {
2016-04-29 20:57:31 +08:00
unsigned int score = 0 ;
2016-06-22 02:02:36 +08:00
if ( Ready ( ) ) {
2016-04-29 20:57:31 +08:00
std : : string cause ;
Event : : StringSetMap noteSetMap ;
2016-06-22 02:02:36 +08:00
if ( trigger_data - > trigger_state = = TRIGGER_ON ) {
2016-04-29 20:57:31 +08:00
score + = trigger_data - > trigger_score ;
2019-06-25 09:11:52 +08:00
Debug ( 1 , " Triggered on score += %d => %d " , trigger_data - > trigger_score , score ) ;
2016-06-22 02:02:36 +08:00
if ( ! event ) {
2016-04-29 20:57:31 +08:00
cause + = trigger_data - > trigger_cause ;
}
Event : : StringSet noteSet ;
2019-06-25 01:13:46 +08:00
noteSet . insert ( trigger_data - > trigger_text ) ;
2016-04-29 20:57:31 +08:00
noteSetMap [ trigger_data - > trigger_cause ] = noteSet ;
}
2019-06-25 09:11:52 +08:00
2016-06-22 02:02:36 +08:00
if ( signal_change ) {
2016-04-29 20:57:31 +08:00
const char * signalText ;
2017-05-17 00:04:56 +08:00
if ( ! signal ) {
2016-04-29 20:57:31 +08:00
signalText = " Lost " ;
2017-05-17 00:04:56 +08:00
} else {
2016-04-29 20:57:31 +08:00
signalText = " Reacquired " ;
score + = 100 ;
}
2019-04-16 00:55:28 +08:00
Warning ( " %s: %s " , SIGNAL_CAUSE , signalText ) ;
2016-06-22 02:02:36 +08:00
if ( event & & ! signal ) {
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Closing event % " PRIu64 " , signal loss " , name , image_count , event - > Id ( ) ) ;
2016-04-29 20:57:31 +08:00
closeEvent ( ) ;
}
2016-06-22 02:02:36 +08:00
if ( ! event ) {
2016-04-29 20:57:31 +08:00
if ( cause . length ( ) )
cause + = " , " ;
cause + = SIGNAL_CAUSE ;
}
Event : : StringSet noteSet ;
2019-06-25 01:13:46 +08:00
noteSet . insert ( signalText ) ;
2016-04-29 20:57:31 +08:00
noteSetMap [ SIGNAL_CAUSE ] = noteSet ;
shared_data - > state = state = IDLE ;
shared_data - > active = signal ;
ref_image = * snap_image ;
2017-05-17 00:04:56 +08:00
2019-06-24 23:29:00 +08:00
} else if ( signal ) {
if ( Active ( ) & & ( function = = MODECT | | function = = MOCORD ) ) {
// All is good, so add motion detection score.
Event : : StringSet zoneSet ;
if ( ( ! motion_frame_skip ) | | ! ( image_count % ( motion_frame_skip + 1 ) ) ) {
// Get new score.
int new_motion_score = DetectMotion ( * snap_image , zoneSet ) ;
Debug ( 3 ,
2020-10-06 00:11:39 +08:00
" After motion detection, score(%d), last_motion_score(%d), new motion score(%d) " ,
score , last_motion_score , new_motion_score
2019-06-24 23:29:00 +08:00
) ;
last_motion_score = new_motion_score ;
}
if ( last_motion_score ) {
2018-05-27 01:01:30 +08:00
score + = last_motion_score ;
2020-10-09 03:50:12 +08:00
// cause is calculated every frame,
//if ( !event ) {
2019-06-25 01:13:46 +08:00
if ( cause . length ( ) )
cause + = " , " ;
cause + = MOTION_CAUSE ;
2020-10-09 03:50:12 +08:00
//}
2019-06-25 01:13:46 +08:00
noteSetMap [ MOTION_CAUSE ] = zoneSet ;
} // end if motion_score
//shared_data->active = signal; // unneccessary active gets set on signal change
} // end if active and doing motion detection
2017-07-26 22:38:18 +08:00
2019-06-25 01:13:46 +08:00
// Check to see if linked monitors are triggering.
2019-05-12 21:35:48 +08:00
if ( n_linked_monitors > 0 ) {
2019-06-25 01:13:46 +08:00
// FIXME improve logic here
2019-05-12 21:35:48 +08:00
bool first_link = true ;
Event : : StringSet noteSet ;
for ( int i = 0 ; i < n_linked_monitors ; i + + ) {
// TODO: Shouldn't we try to connect?
if ( linked_monitors [ i ] - > isConnected ( ) ) {
if ( linked_monitors [ i ] - > hasAlarmed ( ) ) {
if ( ! event ) {
if ( first_link ) {
if ( cause . length ( ) )
cause + = " , " ;
cause + = LINKED_CAUSE ;
first_link = false ;
}
2016-04-29 20:57:31 +08:00
}
2019-05-12 21:35:48 +08:00
noteSet . insert ( linked_monitors [ i ] - > Name ( ) ) ;
score + = 50 ;
2016-04-29 20:57:31 +08:00
}
2016-06-22 02:02:36 +08:00
} else {
2020-06-24 04:56:35 +08:00
Debug ( 1 , " Linked monitor %d %d is not connected. Connecting. " , i , linked_monitors [ i ] - > Id ( ) ) ;
2019-05-12 21:35:48 +08:00
linked_monitors [ i ] - > connect ( ) ;
}
} // end foreach linked_monit
if ( noteSet . size ( ) > 0 )
noteSetMap [ LINKED_CAUSE ] = noteSet ;
} // end if linked_monitors
//TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag?
if ( function = = RECORD | | function = = MOCORD ) {
if ( event ) {
Debug ( 3 , " Have signal and recording with open event at (%d.%d) " , timestamp - > tv_sec , timestamp - > tv_usec ) ;
if ( section_length
& & ( ( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) > = section_length )
2020-06-27 07:30:35 +08:00
& & ( ( function = = MOCORD & & ( event_close_mode ! = CLOSE_TIME ) ) | | ! ( timestamp - > tv_sec % section_length ) )
2019-05-12 21:35:48 +08:00
) {
Info ( " %s: %03d - Closing event % " PRIu64 " , section end forced %d - %d = %d >= %d " ,
name , image_count , event - > Id ( ) ,
2019-06-24 23:29:00 +08:00
timestamp - > tv_sec , video_store_data - > recording . tv_sec ,
2019-05-12 21:35:48 +08:00
timestamp - > tv_sec - video_store_data - > recording . tv_sec ,
section_length
) ;
closeEvent ( ) ;
} // end if section_length
} // end if event
2019-05-24 22:52:31 +08:00
if ( ! event ) {
2019-05-12 21:35:48 +08:00
// Create event
event = new Event ( this , * timestamp , " Continuous " , noteSetMap , videoRecording ) ;
2021-04-01 00:06:33 +08:00
if ( event - > Id ( ) ) {
shared_data - > last_event = 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 ( ) ;
2019-05-12 21:35:48 +08:00
2021-04-01 00:06:33 +08:00
Info ( " %s: %03d - Opening new event % " PRIu64 " , section start " , name , image_count , event - > Id ( ) ) ;
2019-05-12 21:35:48 +08:00
2021-04-01 00:06:33 +08:00
/* To prevent cancelling out an existing alert\prealarm\alarm state */
if ( state = = IDLE ) {
shared_data - > state = state = TAPE ;
}
} else {
delete event ;
event = nullptr ;
2016-04-29 20:57:31 +08:00
}
2019-05-12 21:35:48 +08:00
} // end if ! event
} // end if function == RECORD || function == MOCORD)
} // end if !signal_change && signal
2019-04-16 05:54:30 +08:00
2016-06-22 02:02:36 +08:00
if ( score ) {
2019-06-25 09:11:52 +08:00
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 )
) {
2020-07-20 22:22:40 +08:00
Info ( " %s: %03d - Closing event % " PRIu64 " , continuous end, alarm begins " ,
2019-06-25 09:11:52 +08:00
name , image_count , event - > Id ( ) ) ;
closeEvent ( ) ;
2019-08-01 05:17:54 +08:00
} else if ( event ) {
2019-06-25 01:13:46 +08:00
// 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
2019-06-28 03:48:10 +08:00
Debug ( 3 , " pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d " ,
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 ) ) {
2019-04-25 01:55:57 +08:00
// lets construct alarm cause. It will contain cause + names of zones alarmed
2019-05-24 22:52:31 +08:00
std : : string alarm_cause = " " ;
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( zones [ i ] - > Alarmed ( ) ) {
2019-06-28 03:48:10 +08:00
alarm_cause = alarm_cause + " , " + std : : string ( zones [ i ] - > Label ( ) ) ;
2019-04-25 01:55:57 +08:00
}
2019-05-24 22:52:31 +08:00
}
2020-10-09 03:50:12 +08:00
if ( ! alarm_cause . empty ( ) ) alarm_cause [ 0 ] = ' ' ; // replace leading , with a space
2019-05-24 22:52:31 +08:00
alarm_cause = cause + alarm_cause ;
2019-06-28 03:48:10 +08:00
strncpy ( shared_data - > alarm_cause , alarm_cause . c_str ( ) , sizeof ( shared_data - > alarm_cause ) - 1 ) ;
2019-05-24 22:52:31 +08:00
Info ( " %s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s " ,
2019-04-25 01:55:57 +08:00
name , image_count , Event : : PreAlarmCount ( ) , alarm_frame_count , shared_data - > alarm_cause ) ;
2019-06-25 01:13:46 +08:00
if ( ! event ) {
2016-04-29 20:57:31 +08:00
int pre_index ;
int pre_event_images = pre_event_count ;
2018-05-24 22:59:15 +08:00
if ( analysis_fps & & pre_event_count ) {
2016-04-29 20:57:31 +08:00
// If analysis fps is set,
// compute the index for pre event images in the dedicated buffer
2019-06-28 03:48:10 +08:00
pre_index = pre_event_buffer_count ? image_count % pre_event_buffer_count : 0 ;
Debug ( 3 , " pre-index %d = image_count(%d) %% pre_event_buffer_count(%d) " ,
pre_index , image_count , pre_event_buffer_count ) ;
2016-04-29 20:57:31 +08:00
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
2016-06-22 02:02:36 +08:00
while ( pre_event_images & & ! pre_event_buffer [ pre_index ] . timestamp - > tv_sec ) {
2016-04-29 20:57:31 +08:00
pre_index = ( pre_index + 1 ) % pre_event_buffer_count ;
// Slot is empty, removing image from counter
pre_event_images - - ;
}
2019-06-28 03:48:10 +08:00
Debug ( 3 , " pre-index %d, pre-event_images %d " ,
pre_index , pre_event_images ) ;
2016-04-29 20:57:31 +08:00
2018-05-24 22:59:15 +08:00
event = new Event ( this , * ( pre_event_buffer [ pre_index ] . timestamp ) , cause , noteSetMap ) ;
2021-04-01 00:06:33 +08:00
if ( ! event - > Id ( ) ) {
delete event ;
event = nullptr ;
}
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
// If analysis fps is not set (analysis performed at capturing framerate),
// compute the index for pre event images in the capturing buffer
if ( alarm_frame_count > 1 )
pre_index = ( ( index + image_buffer_count ) - ( ( alarm_frame_count - 1 ) + pre_event_count ) ) % image_buffer_count ;
else
pre_index = ( ( index + image_buffer_count ) - pre_event_count ) % image_buffer_count ;
2019-06-28 03:48:10 +08:00
Debug ( 3 , " Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d) " ,
2018-08-01 01:22:52 +08:00
pre_index , index , image_buffer_count , pre_event_count ) ;
2018-06-15 22:16:28 +08:00
2016-04-29 20:57:31 +08:00
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
2016-06-22 02:02:36 +08:00
while ( pre_event_images & & ! image_buffer [ pre_index ] . timestamp - > tv_sec ) {
2016-04-29 20:57:31 +08:00
pre_index = ( pre_index + 1 ) % image_buffer_count ;
// Slot is empty, removing image from counter
pre_event_images - - ;
}
2018-05-24 22:59:15 +08:00
event = new Event ( this , * ( image_buffer [ pre_index ] . timestamp ) , cause , noteSetMap ) ;
2021-04-01 00:06:33 +08:00
if ( ! event - > Id ( ) ) {
delete event ;
event = nullptr ;
}
2019-04-16 05:54:30 +08:00
} // end if analysis_fps && pre_event_count
2016-04-29 20:57:31 +08:00
shared_data - > last_event = event - > Id ( ) ;
//set up video store data
snprintf ( video_store_data - > event_file , sizeof ( video_store_data - > event_file ) , " %s " , event - > getEventFile ( ) ) ;
2017-04-13 01:36:39 +08:00
video_store_data - > recording = event - > StartTime ( ) ;
2016-04-29 20:57:31 +08:00
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Opening new event % " PRIu64 " , alarm start " , name , image_count , event - > Id ( ) ) ;
2016-04-29 20:57:31 +08:00
2016-06-22 02:02:36 +08:00
if ( pre_event_images ) {
if ( analysis_fps ) {
for ( int i = 0 ; i < pre_event_images ; i + + ) {
2016-04-29 20:57:31 +08:00
timestamps [ i ] = pre_event_buffer [ pre_index ] . timestamp ;
images [ i ] = pre_event_buffer [ pre_index ] . image ;
pre_index = ( pre_index + 1 ) % pre_event_buffer_count ;
}
2016-06-22 02:02:36 +08:00
} else {
for ( int i = 0 ; i < pre_event_images ; i + + ) {
2016-04-29 20:57:31 +08:00
timestamps [ i ] = image_buffer [ pre_index ] . timestamp ;
images [ i ] = image_buffer [ pre_index ] . image ;
pre_index = ( pre_index + 1 ) % image_buffer_count ;
}
2016-06-22 02:02:36 +08:00
}
2019-05-24 22:52:31 +08:00
event - > AddFrames ( pre_event_images , images , timestamps ) ;
2020-10-05 21:19:48 +08:00
} else if ( alarm_frame_count > 1 ) {
int temp_alarm_frame_count = alarm_frame_count ;
while ( - - temp_alarm_frame_count ) {
Debug ( 1 , " Adding previous frame due to alarm_frame_count %d " , pre_index ) ;
event - > AddFrame ( image_buffer [ pre_index ] . image , * image_buffer [ pre_index ] . timestamp , 0 , nullptr ) ;
pre_index = ( pre_index + 1 ) % image_buffer_count ;
}
} // end if pre_event_images
if ( ( alarm_frame_count > 1 ) & & Event : : PreAlarmCount ( ) ) {
Debug ( 1 , " alarm frame count so SavePreAlarmFrames " ) ;
2016-04-29 20:57:31 +08:00
event - > SavePreAlarmFrames ( ) ;
}
}
2020-08-11 23:02:26 +08:00
shared_data - > state = state = ALARM ;
2016-06-22 02:02:36 +08:00
} else if ( state ! = PREALARM ) {
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Gone into prealarm state " , name , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > state = state = PREALARM ;
}
2016-06-22 02:02:36 +08:00
} else if ( state = = ALERT ) {
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Gone back into alarm state " , name , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > state = state = ALARM ;
}
last_alarm_count = image_count ;
2019-04-16 05:54:30 +08:00
} else { // not score
2016-06-22 02:02:36 +08:00
if ( state = = ALARM ) {
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Gone into alert state " , name , image_count ) ;
2016-04-29 20:57:31 +08:00
shared_data - > state = state = ALERT ;
2016-06-22 02:02:36 +08:00
} else if ( state = = ALERT ) {
2019-06-28 09:50:12 +08:00
if (
( image_count - last_alarm_count > post_event_count )
& & ( ( timestamp - > tv_sec - video_store_data - > recording . tv_sec ) > = min_section_length )
) {
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Left alarm state (% " PRIu64 " ) - %d(%d) images " ,
name , image_count , event - > Id ( ) , event - > Frames ( ) , event - > AlarmFrames ( ) ) ;
2016-04-29 20:57:31 +08:00
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
2020-04-13 08:42:43 +08:00
if ( ( function ! = MOCORD & & function ! = RECORD ) | | event_close_mode = = CLOSE_ALARM ) {
2016-04-29 20:57:31 +08:00
shared_data - > state = state = IDLE ;
2019-04-16 00:55:28 +08:00
Info ( " %s: %03d - Closing event % " PRIu64 " , alarm end%s " ,
name , image_count , event - > Id ( ) , ( function = = MOCORD ) ? " , section truncated " : " " ) ;
2016-04-29 20:57:31 +08:00
closeEvent ( ) ;
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > state = state = TAPE ;
}
2020-10-06 00:11:39 +08:00
} else {
Debug ( 1 , " Not leaving ALERT 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) " , image_count , last_alarm_count , post_event_count , timestamp - > tv_sec , video_store_data - > recording . tv_sec , min_section_length ) ;
2016-04-29 20:57:31 +08:00
}
2019-04-16 00:55:28 +08:00
} // end if ALARM or ALERT
2016-06-22 02:02:36 +08:00
if ( state = = PREALARM ) {
if ( function ! = MOCORD ) {
2016-04-29 20:57:31 +08:00
shared_data - > state = state = IDLE ;
2016-06-22 02:02:36 +08:00
} else {
2016-04-29 20:57:31 +08:00
shared_data - > state = state = TAPE ;
}
}
if ( Event : : PreAlarmCount ( ) )
Event : : EmptyPreAlarmFrames ( ) ;
2019-04-16 05:54:30 +08:00
} // end if score or not
2016-06-22 02:02:36 +08:00
if ( state ! = IDLE ) {
if ( state = = PREALARM | | state = = ALARM ) {
if ( config . create_analysis_images ) {
2016-04-29 20:57:31 +08:00
bool got_anal_image = false ;
2019-05-24 22:52:31 +08:00
alarm_image . Assign ( * snap_image ) ;
for ( int i = 0 ; i < n_zones ; i + + ) {
2016-06-22 02:02:36 +08:00
if ( zones [ i ] - > Alarmed ( ) ) {
if ( zones [ i ] - > AlarmImage ( ) ) {
2019-04-16 00:55:28 +08:00
alarm_image . Overlay ( * ( zones [ i ] - > AlarmImage ( ) ) ) ;
2016-04-29 20:57:31 +08:00
got_anal_image = true ;
}
2019-05-24 22:52:31 +08:00
if ( config . record_event_stats & & ( state = = ALARM ) )
2019-04-16 00:55:28 +08:00
zones [ i ] - > RecordStats ( event ) ;
2019-05-24 22:52:31 +08:00
} // end if zone is alarmed
2019-04-16 00:55:28 +08:00
} // end foreach zone
2020-07-07 21:16:42 +08:00
if ( state = = PREALARM )
Event : : AddPreAlarmFrame ( snap_image , * timestamp , score , ( got_anal_image ? & alarm_image : NULL ) ) ;
else
event - > AddFrame ( snap_image , * timestamp , score , ( got_anal_image ? & alarm_image : NULL ) ) ;
2016-06-22 02:02:36 +08:00
} else {
2019-05-24 22:52:31 +08:00
// Not doing alarm frame storage
if ( state = = PREALARM ) {
2019-04-16 00:55:28 +08:00
Event : : AddPreAlarmFrame ( snap_image , * timestamp , score ) ;
2019-05-24 22:52:31 +08:00
} else {
2019-04-16 00:55:28 +08:00
event - > AddFrame ( snap_image , * timestamp , score ) ;
2019-05-24 22:52:31 +08:00
if ( config . record_event_stats ) {
for ( int i = 0 ; i < n_zones ; i + + ) {
if ( zones [ i ] - > Alarmed ( ) )
zones [ i ] - > RecordStats ( event ) ;
}
} // end if config.record_event_stats
}
} // end if config.create_analysis_images
2019-04-16 23:30:18 +08:00
if ( event ) {
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
) ;
2019-05-03 21:41:29 +08:00
closeEvent ( ) ;
event = new Event ( this , * timestamp , cause , noteSetMap ) ;
2021-04-01 00:06:33 +08:00
if ( ! event - > Id ( ) ) {
delete event ;
event = nullptr ;
} else {
shared_data - > last_event = 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 ( ) ;
}
2019-04-16 23:30:18 +08:00
}
} // end if event
2019-03-26 00:33:30 +08:00
2016-06-22 02:02:36 +08:00
} else if ( state = = ALERT ) {
2019-04-16 00:55:28 +08:00
event - > AddFrame ( snap_image , * timestamp ) ;
2016-04-29 20:57:31 +08:00
if ( noteSetMap . size ( ) > 0 )
2019-04-16 00:55:28 +08:00
event - > updateNotes ( noteSetMap ) ;
2016-06-22 02:02:36 +08:00
} else if ( state = = TAPE ) {
2016-04-29 20:57:31 +08:00
//Video Storage: activate only for supported cameras. Event::AddFrame knows whether or not we are recording video and saves frames accordingly
2017-04-13 01:36:39 +08:00
//if((GetOptVideoWriter() == 2) && camera->SupportsNativeVideo()) {
// I don't think this is required, and causes problems, as the event file hasn't been setup yet.
2018-03-09 20:23:40 +08:00
//Warning("In state TAPE,
2017-04-13 01:36:39 +08:00
//video_store_data->recording = event->StartTime();
//}
2018-09-12 01:22:01 +08:00
if ( ( ! frame_skip ) | | ! ( image_count % ( frame_skip + 1 ) ) ) {
2016-06-22 02:02:36 +08:00
if ( config . bulk_frame_interval > 1 ) {
2019-04-16 00:55:28 +08:00
event - > AddFrame ( snap_image , * timestamp , ( event - > Frames ( ) < pre_event_count ? 0 : - 1 ) ) ;
2016-06-22 02:02:36 +08:00
} else {
2019-04-16 00:55:28 +08:00
event - > AddFrame ( snap_image , * timestamp ) ;
2016-04-29 20:57:31 +08:00
}
}
}
2016-12-09 00:49:54 +08:00
} // end if ! IDLE
2016-04-29 20:57:31 +08:00
}
2016-06-22 02:02:36 +08:00
} else {
if ( event ) {
2019-01-16 00:34:17 +08:00
Info ( " %s: %03d - Closing event % " PRIu64 " , trigger off " , name , image_count , event - > Id ( ) ) ;
2016-04-29 20:57:31 +08:00
closeEvent ( ) ;
}
shared_data - > state = state = IDLE ;
2018-09-11 05:11:16 +08:00
trigger_data - > trigger_state = TRIGGER_CANCEL ;
2017-04-06 04:10:21 +08:00
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
2016-06-22 02:02:36 +08:00
if ( ( ! signal_change & & signal ) & & ( function = = MODECT | | function = = MOCORD ) ) {
2016-04-29 20:57:31 +08:00
if ( state = = ALARM ) {
ref_image . Blend ( * snap_image , alarm_ref_blend_perc ) ;
} else {
ref_image . Blend ( * snap_image , ref_blend_perc ) ;
}
}
last_signal = signal ;
2017-04-06 04:10:21 +08:00
} // end if Enabled()
2016-04-29 20:57:31 +08:00
2017-04-06 04:10:21 +08:00
shared_data - > last_read_index = index % image_buffer_count ;
2016-04-29 20:57:31 +08:00
//shared_data->last_read_time = image_buffer[index].timestamp->tv_sec;
shared_data - > last_read_time = now . tv_sec ;
2018-03-09 20:23:40 +08:00
if ( analysis_fps & & pre_event_buffer_count ) {
2016-04-29 20:57:31 +08:00
// If analysis fps is set, add analysed image to dedicated pre event buffer
int pre_index = image_count % pre_event_buffer_count ;
pre_event_buffer [ pre_index ] . image - > Assign ( * snap - > image ) ;
2019-04-16 00:55:28 +08:00
memcpy ( pre_event_buffer [ pre_index ] . timestamp , snap - > timestamp , sizeof ( struct timeval ) ) ;
2016-04-29 20:57:31 +08:00
}
image_count + + ;
2018-09-12 01:22:01 +08:00
return true ;
2019-04-16 00:55:28 +08:00
} // end Monitor::Analyze
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
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
static char sql [ ZM_SQL_MED_BUFSIZ ] ;
// This seems to have fallen out of date.
2019-06-24 23:29:00 +08:00
snprintf ( sql , sizeof ( sql ) ,
2019-08-15 04:18:21 +08:00
" SELECT `Function`+0, `Enabled`, `LinkedMonitors`, `EventPrefix`, `LabelFormat`, "
" `LabelX`, `LabelY`, `LabelSize`, `WarmupCount`, `PreEventCount`, `PostEventCount`, "
" `AlarmFrameCount`, `SectionLength`, `MinSectionLength`, `FrameSkip`, "
" `MotionFrameSkip`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`, "
" `FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, "
2020-04-17 22:26:03 +08:00
" `SignalCheckPoints`, `SignalCheckColour` FROM `Monitors` WHERE `Id` = '%d' " , id ) ;
2016-04-29 20:57:31 +08:00
2018-03-27 22:04:19 +08:00
zmDbRow * row = zmDbFetchOne ( sql ) ;
if ( ! row ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
} else if ( MYSQL_ROW dbrow = row - > mysql_row ( ) ) {
2016-04-29 20:57:31 +08:00
int index = 0 ;
function = ( Function ) atoi ( dbrow [ index + + ] ) ;
enabled = atoi ( dbrow [ index + + ] ) ;
const char * p_linked_monitors = dbrow [ index + + ] ;
2018-01-17 00:08:35 +08:00
if ( dbrow [ index ] ) {
2019-04-16 00:55:28 +08:00
strncpy ( event_prefix , dbrow [ index + + ] , sizeof ( event_prefix ) - 1 ) ;
2018-01-17 00:08:35 +08:00
} else {
event_prefix [ 0 ] = 0 ;
index + + ;
}
if ( dbrow [ index ] ) {
2019-04-16 00:55:28 +08:00
strncpy ( label_format , dbrow [ index + + ] , sizeof ( label_format ) - 1 ) ;
2018-01-17 00:08:35 +08:00
} else {
label_format [ 0 ] = 0 ;
index + + ;
}
2016-04-29 20:57:31 +08:00
label_coord = Coord ( atoi ( dbrow [ index ] ) , atoi ( dbrow [ index + 1 ] ) ) ; index + = 2 ;
label_size = atoi ( dbrow [ index + + ] ) ;
warmup_count = atoi ( dbrow [ index + + ] ) ;
pre_event_count = atoi ( dbrow [ index + + ] ) ;
post_event_count = atoi ( dbrow [ index + + ] ) ;
alarm_frame_count = atoi ( dbrow [ index + + ] ) ;
section_length = atoi ( dbrow [ index + + ] ) ;
2019-06-24 23:29:00 +08:00
min_section_length = atoi ( dbrow [ index + + ] ) ;
2016-04-29 20:57:31 +08:00
frame_skip = atoi ( dbrow [ index + + ] ) ;
motion_frame_skip = atoi ( dbrow [ index + + ] ) ;
analysis_fps = dbrow [ index ] ? strtod ( dbrow [ index ] , NULL ) : 0 ; index + + ;
analysis_update_delay = strtoul ( dbrow [ index + + ] , NULL , 0 ) ;
2019-07-31 23:23:02 +08:00
capture_max_fps = dbrow [ index ] ? atof ( dbrow [ index ] ) : 0.0 ; index + + ;
capture_delay = ( capture_max_fps > 0.0 ) ? int ( DT_PREC_3 / capture_max_fps ) : 0 ;
2016-04-29 20:57:31 +08:00
alarm_capture_delay = ( dbrow [ index ] & & atof ( dbrow [ index ] ) > 0.0 ) ? int ( DT_PREC_3 / atof ( dbrow [ index ] ) ) : 0 ; index + + ;
fps_report_interval = atoi ( dbrow [ index + + ] ) ;
ref_blend_perc = atoi ( dbrow [ index + + ] ) ;
alarm_ref_blend_perc = atoi ( dbrow [ index + + ] ) ;
track_motion = atoi ( dbrow [ index + + ] ) ;
2018-03-09 20:23:40 +08:00
2020-04-17 22:26:03 +08:00
signal_check_points = dbrow [ index ] ? atoi ( dbrow [ index ] ) : 0 ; index + + ;
signal_check_colour = strtol ( dbrow [ index ] [ 0 ] = = ' # ' ? dbrow [ index ] + 1 : dbrow [ index ] , 0 , 16 ) ; index + + ;
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 ;
ready_count = image_count + warmup_count ;
2018-04-24 04:24:15 +08:00
ReloadLinkedMonitors ( p_linked_monitors ) ;
2018-04-12 23:29:35 +08:00
delete row ;
2018-03-27 22:04:19 +08:00
} // end if row
2016-04-29 20:57:31 +08:00
ReloadZones ( ) ;
2020-04-17 22:26:03 +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 ) ;
for ( int i = 0 ; i < n_zones ; i + + ) {
2016-04-29 20:57:31 +08:00
delete zones [ i ] ;
}
delete [ ] zones ;
zones = 0 ;
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 ) {
2019-04-16 00:55:28 +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 ;
linked_monitors = 0 ;
}
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 ] ;
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 " ,
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 ) ;
2016-06-22 02:02:36 +08:00
if ( ! result ) {
2018-04-24 04:24:15 +08:00
db_mutex . unlock ( ) ;
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
db_mutex . unlock ( ) ;
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 ) ;
Debug ( 1 , " Linking to monitor %d " , link_ids [ i ] ) ;
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
2018-03-09 20:23:40 +08:00
int Monitor : : LoadMonitors ( std : : string sql , Monitor * * & monitors , Purpose purpose ) {
2016-04-29 20:57:31 +08:00
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 ( ) ) ;
2016-04-29 20:57:31 +08:00
if ( ! result ) {
2018-03-09 20:23:40 +08:00
Error ( " Can't load local monitors: %s " , mysql_error ( & dbconn ) ) ;
return 0 ;
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 ) ;
2016-04-29 20:57:31 +08:00
delete [ ] monitors ;
monitors = new Monitor * [ n_monitors ] ;
2019-04-16 00:55:28 +08:00
for ( int i = 0 ; MYSQL_ROW dbrow = mysql_fetch_row ( result ) ; i + + ) {
2018-03-09 20:23:40 +08:00
monitors [ i ] = Monitor : : Load ( dbrow , 1 , purpose ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
if ( mysql_errno ( & dbconn ) ) {
Error ( " Can't fetch row: %s " , mysql_error ( & dbconn ) ) ;
return 0 ;
}
mysql_free_result ( result ) ;
2016-04-29 20:57:31 +08:00
2018-03-09 20:23:40 +08:00
return n_monitors ;
2019-04-16 00:55:28 +08:00
} // end int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose)
2016-04-29 20:57:31 +08:00
2018-03-09 20:23:40 +08:00
# if ZM_HAS_V4L
2019-04-16 00:55:28 +08:00
int Monitor : : LoadLocalMonitors ( const char * device , Monitor * * & monitors , 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 ) ;
2018-03-09 20:23:40 +08:00
return LoadMonitors ( sql , monitors , purpose ) ;
2019-04-16 00:55:28 +08:00
} // end int Monitor::LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose purpose)
2011-02-16 05:59:06 +08:00
# endif // ZM_HAS_V4L
2003-03-26 19:57:29 +08:00
2019-04-16 00:55:28 +08:00
int Monitor : : LoadRemoteMonitors ( const char * protocol , const char * host , const char * port , const char * path , Monitor * * & monitors , 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 ) ;
2016-04-29 20:57:31 +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 ) ;
2018-03-09 20:23:40 +08:00
return LoadMonitors ( sql , monitors , purpose ) ;
2019-04-16 00:55:28 +08:00
} // end int Monitor::LoadRemoteMonitors
2003-03-26 19:57:29 +08:00
2019-04-16 00:55:28 +08:00
int Monitor : : LoadFileMonitors ( const char * file , Monitor * * & monitors , 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 ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
return LoadMonitors ( sql , monitors , purpose ) ;
2019-04-16 00:55:28 +08:00
} // end int Monitor::LoadFileMonitors
2005-10-18 05:55:02 +08:00
2009-01-28 23:02:33 +08:00
# if HAVE_LIBAVFORMAT
2018-03-09 20:23:40 +08:00
int Monitor : : LoadFfmpegMonitors ( const char * file , Monitor * * & monitors , 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 ) + " ' " ;
2016-04-29 20:57:31 +08:00
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 ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
return LoadMonitors ( sql , monitors , purpose ) ;
2019-04-16 00:55:28 +08:00
} // end int Monitor::LoadFfmpegMonitors
2009-01-28 23:02:33 +08:00
# endif // HAVE_LIBAVFORMAT
2009-01-20 23:01:32 +08:00
2020-04-17 22:26:03 +08:00
/* For reference
std : : string load_monitor_sql =
" SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `LinkedMonitors`, "
" `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`, "
" `DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
" `SaveJPEGs`, `VideoWriter`, `EncoderParameters`, "
//" OutputCodec, Encoder, OutputContainer, "
" `RecordAudio`, "
" `Brightness`, `Contrast`, `Hue`, `Colour`, "
" `EventPrefix`, `LabelFormat`, `LabelX`, `LabelY`, `LabelSize`, "
" `ImageBufferCount`, `WarmupCount`, `PreEventCount`, `PostEventCount`, `StreamReplayBuffer`, `AlarmFrameCount`, "
" `SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, "
" `FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`, `SignalCheckPoints`, `SignalCheckColour` FROM `Monitors` " ;
2018-03-09 20:50:47 +08:00
*/
Monitor * Monitor : : Load ( MYSQL_ROW dbrow , bool load_zones , Purpose purpose ) {
int col = 0 ;
int id = atoi ( dbrow [ col ] ) ; col + + ;
const char * name = dbrow [ col ] ; col + + ;
int server_id = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
int storage_id = atoi ( dbrow [ col ] ) ; col + + ;
std : : string type = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
Function function = ( Function ) atoi ( dbrow [ col ] ) ; col + + ;
int enabled = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
const char * linked_monitors = dbrow [ col ] ; col + + ;
2016-04-29 20:57:31 +08:00
2018-03-09 20:50:47 +08:00
double analysis_fps = dbrow [ col ] ? strtod ( dbrow [ col ] , NULL ) : 0 ; col + + ;
unsigned int analysis_update_delay = strtoul ( dbrow [ col + + ] , NULL , 0 ) ;
2019-07-31 23:23:02 +08:00
double capture_max_fps = dbrow [ col ] ? atof ( dbrow [ col ] ) : 0.0 ; col + + ;
double capture_delay = ( capture_max_fps > 0.0 ) ? int ( DT_PREC_3 / capture_max_fps ) : 0 ;
2018-03-09 20:50:47 +08:00
unsigned int alarm_capture_delay = ( dbrow [ col ] & & atof ( dbrow [ col ] ) > 0.0 ) ? int ( DT_PREC_3 / atof ( dbrow [ col ] ) ) : 0 ; col + + ;
const char * device = dbrow [ col ] ; col + + ;
2016-04-29 20:57:31 +08:00
int channel = atoi ( dbrow [ col ] ) ; col + + ;
int format = atoi ( dbrow [ col ] ) ; col + + ;
bool 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 + + ;
int v4l_captures_per_frame = 0 ;
if ( dbrow [ col ] ) {
2018-03-09 20:50:47 +08:00
v4l_captures_per_frame = atoi ( dbrow [ col ] ) ;
2016-04-29 20:57:31 +08:00
} else {
v4l_captures_per_frame = config . captures_per_frame ;
}
col + + ;
2016-06-24 22:29:50 +08:00
std : : string protocol = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
std : : string method = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2016-06-10 00:51:22 +08:00
std : : string options = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2016-06-24 22:29:50 +08:00
std : : string user = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
std : : string pass = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2018-03-09 20:50:47 +08:00
std : : string host = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
std : : string port = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
std : : string path = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2016-04-29 20:57:31 +08:00
int width = atoi ( dbrow [ col ] ) ; col + + ;
int height = atoi ( dbrow [ col ] ) ; col + + ;
int colours = atoi ( dbrow [ col ] ) ; col + + ;
int palette = atoi ( dbrow [ col ] ) ; col + + ;
Orientation orientation = ( Orientation ) atoi ( dbrow [ col ] ) ; col + + ;
2018-03-09 20:50:47 +08:00
int deinterlacing = atoi ( dbrow [ col ] ) ; col + + ;
2019-06-26 03:34:17 +08:00
std : : string decoder_hwaccel_name = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
std : : string decoder_hwaccel_device = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2016-07-14 22:27:24 +08:00
bool rtsp_describe = ( dbrow [ col ] & & * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
2018-03-09 20:50:47 +08:00
2016-04-29 20:57:31 +08:00
int savejpegs = atoi ( dbrow [ col ] ) ; col + + ;
2016-09-13 09:35:14 +08:00
VideoWriter videowriter = ( VideoWriter ) atoi ( dbrow [ col ] ) ; col + + ;
2018-03-09 20:50:47 +08:00
const char * encoderparams = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2016-04-29 20:57:31 +08:00
bool record_audio = ( * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
int brightness = atoi ( dbrow [ col ] ) ; col + + ;
int contrast = atoi ( dbrow [ col ] ) ; col + + ;
int hue = atoi ( dbrow [ col ] ) ; col + + ;
int colour = atoi ( dbrow [ col ] ) ; col + + ;
2018-03-09 20:50:47 +08:00
const char * event_prefix = dbrow [ col ] ; col + + ;
2018-03-14 03:06:03 +08:00
const char * label_format = dbrow [ col ] ? dbrow [ col ] : " " ; col + + ;
2018-03-09 20:50:47 +08:00
Coord label_coord = Coord ( atoi ( dbrow [ col ] ) , atoi ( dbrow [ col + 1 ] ) ) ; col + = 2 ;
2016-04-29 20:57:31 +08:00
int label_size = atoi ( dbrow [ col ] ) ; col + + ;
int image_buffer_count = atoi ( dbrow [ col ] ) ; col + + ;
int warmup_count = atoi ( dbrow [ col ] ) ; col + + ;
int pre_event_count = atoi ( dbrow [ col ] ) ; col + + ;
int post_event_count = atoi ( dbrow [ col ] ) ; col + + ;
int stream_replay_buffer = atoi ( dbrow [ col ] ) ; col + + ;
int alarm_frame_count = atoi ( dbrow [ col ] ) ; col + + ;
int section_length = atoi ( dbrow [ col ] ) ; col + + ;
2019-06-24 23:29:00 +08:00
int min_section_length = atoi ( dbrow [ col ] ) ; col + + ;
2016-04-29 20:57:31 +08:00
int frame_skip = atoi ( dbrow [ col ] ) ; col + + ;
int motion_frame_skip = atoi ( dbrow [ col ] ) ; col + + ;
int fps_report_interval = atoi ( dbrow [ col ] ) ; col + + ;
int ref_blend_perc = atoi ( dbrow [ col ] ) ; col + + ;
int alarm_ref_blend_perc = atoi ( dbrow [ col ] ) ; col + + ;
int track_motion = atoi ( dbrow [ col ] ) ; col + + ;
2020-04-17 22:26:03 +08:00
bool embed_exif = ( * dbrow [ col ] ! = ' 0 ' ) ; col + + ;
2018-04-19 22:10:14 +08:00
int signal_check_points = dbrow [ col ] ? atoi ( dbrow [ col ] ) : 0 ; col + + ;
2018-03-09 20:50:47 +08:00
int signal_check_color = strtol ( dbrow [ col ] [ 0 ] = = ' # ' ? dbrow [ col ] + 1 : dbrow [ col ] , 0 , 16 ) ; col + + ;
2016-04-29 20:57:31 +08:00
Camera * camera = 0 ;
if ( type = = " Local " ) {
2018-03-09 20:50:47 +08:00
2018-11-13 00:56:18 +08:00
# if ZM_HAS_V4L
2018-03-09 20:50:47 +08:00
int extras = ( deinterlacing > > 24 ) & 0xff ;
2016-04-29 20:57:31 +08:00
camera = new LocalCamera (
2017-10-04 04:28:56 +08:00
id ,
2018-03-09 20:50:47 +08:00
device ,
channel ,
format ,
v4l_multi_buffer ,
v4l_captures_per_frame ,
method ,
2017-10-04 04:28:56 +08:00
width ,
height ,
colours ,
2018-03-09 20:50:47 +08:00
palette ,
2017-10-04 04:28:56 +08:00
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
2018-03-09 20:50:47 +08:00
record_audio ,
extras
) ;
2018-11-13 00:56:18 +08:00
# else
Fatal ( " ZoneMinder not built with Local Camera support " ) ;
# endif
2016-04-29 20:57:31 +08:00
} else if ( type = = " Remote " ) {
if ( protocol = = " http " ) {
camera = new RemoteCameraHttp (
id ,
2018-03-09 20:50:47 +08:00
method ,
host ,
port ,
path ,
2016-04-29 20:57:31 +08:00
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2018-03-09 20:50:47 +08:00
}
2011-02-16 05:59:06 +08:00
# if HAVE_LIBAVFORMAT
2018-03-09 20:50:47 +08:00
else if ( protocol = = " rtsp " ) {
2016-04-29 20:57:31 +08:00
camera = new RemoteCameraRtsp (
id ,
2018-03-09 20:50:47 +08:00
method ,
host , // Host
port , // Port
path , // Path
2016-04-29 20:57:31 +08:00
width ,
height ,
rtsp_describe ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2018-03-09 20:50:47 +08:00
}
2009-01-28 23:02:33 +08:00
# endif // HAVE_LIBAVFORMAT
2018-03-09 20:50:47 +08:00
else {
2019-04-16 00:55:28 +08:00
Fatal ( " Unexpected remote camera protocol '%s' " , protocol . c_str ( ) ) ;
2016-04-29 20:57:31 +08:00
}
} else if ( type = = " File " ) {
camera = new FileCamera (
id ,
path . c_str ( ) ,
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
} else if ( type = = " Ffmpeg " ) {
2011-02-16 05:59:06 +08:00
# if HAVE_LIBAVFORMAT
2016-04-29 20:57:31 +08:00
camera = new FfmpegCamera (
id ,
2018-03-09 20:50:47 +08:00
path ,
2016-04-29 20:57:31 +08:00
method ,
options ,
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
2019-06-26 03:34:17 +08:00
record_audio ,
decoder_hwaccel_name ,
decoder_hwaccel_device
) ;
2009-01-28 23:02:33 +08:00
# endif // HAVE_LIBAVFORMAT
2018-03-09 20:50:47 +08:00
} else if ( type = = " NVSocket " ) {
camera = new RemoteCameraNVSocket (
id ,
host . c_str ( ) ,
port . c_str ( ) ,
path . c_str ( ) ,
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
} else if ( type = = " Libvlc " ) {
2013-12-13 01:45:29 +08:00
# if HAVE_LIBVLC
2016-04-29 20:57:31 +08:00
camera = new LibvlcCamera (
id ,
path . c_str ( ) ,
method ,
options ,
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2013-12-13 01:45:29 +08:00
# else // HAVE_LIBVLC
2019-04-16 00:55:28 +08:00
Fatal ( " You must have vlc libraries installed to use vlc cameras for monitor %d " , id ) ;
2013-12-13 01:45:29 +08:00
# endif // HAVE_LIBVLC
2016-04-29 20:57:31 +08:00
} else if ( type = = " cURL " ) {
2013-11-04 22:52:21 +08:00
# if HAVE_LIBCURL
2016-04-29 20:57:31 +08:00
camera = new cURLCamera (
id ,
path . c_str ( ) ,
user . c_str ( ) ,
pass . c_str ( ) ,
width ,
height ,
colours ,
brightness ,
contrast ,
hue ,
colour ,
purpose = = CAPTURE ,
record_audio
) ;
2013-11-04 22:52:21 +08:00
# else // HAVE_LIBCURL
2019-04-16 00:55:28 +08:00
Fatal ( " You must have libcurl installed to use ffmpeg cameras for monitor %d " , id ) ;
2013-11-04 22:52:21 +08:00
# endif // HAVE_LIBCURL
2016-04-29 20:57:31 +08:00
} else {
2019-04-16 00:55:28 +08:00
Fatal ( " Bogus monitor type '%s' for monitor %d " , type . c_str ( ) , id ) ;
2018-03-09 20:50:47 +08:00
} // end if type
2019-04-16 00:55:28 +08:00
Monitor * monitor = new Monitor (
2018-03-09 20:50:47 +08:00
id ,
name ,
server_id ,
storage_id ,
( int ) function ,
enabled ,
linked_monitors ,
camera ,
orientation ,
deinterlacing ,
2019-06-26 03:34:17 +08:00
decoder_hwaccel_name ,
decoder_hwaccel_device ,
2018-03-09 20:50:47 +08:00
savejpegs ,
videowriter ,
encoderparams ,
record_audio ,
event_prefix ,
label_format ,
label_coord ,
label_size ,
image_buffer_count ,
warmup_count ,
pre_event_count ,
post_event_count ,
stream_replay_buffer ,
alarm_frame_count ,
section_length ,
2019-06-24 23:29:00 +08:00
min_section_length ,
2018-03-09 20:50:47 +08:00
frame_skip ,
motion_frame_skip ,
2019-07-31 23:23:02 +08:00
capture_max_fps ,
2018-03-09 20:50:47 +08:00
analysis_fps ,
analysis_update_delay ,
capture_delay ,
alarm_capture_delay ,
fps_report_interval ,
ref_blend_perc ,
alarm_ref_blend_perc ,
track_motion ,
2018-04-19 22:10:14 +08:00
signal_check_points ,
2018-03-09 20:50:47 +08:00
signal_check_color ,
embed_exif ,
purpose ,
0 ,
0
2019-04-16 00:55:28 +08:00
) ;
2018-03-09 20:50:47 +08:00
camera - > setMonitor ( monitor ) ;
2020-10-09 04:46:30 +08:00
int n_zones = 0 ;
if ( load_zones ) {
Zone * * zones = 0 ;
n_zones = Zone : : Load ( monitor , zones ) ;
monitor - > AddZones ( n_zones , zones ) ;
monitor - > AddPrivacyBitmask ( zones ) ;
}
2018-03-09 20:50:47 +08:00
Debug ( 1 , " Loaded monitor %d(%s), %d zones " , id , name , n_zones ) ;
return monitor ;
2019-04-16 00:55:28 +08:00
} // end Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose)
2003-03-26 19:57:29 +08:00
2018-03-09 20:23:40 +08:00
Monitor * Monitor : : Load ( unsigned int p_id , bool load_zones , Purpose purpose ) {
2019-08-18 02:36:52 +08:00
std : : string sql = load_monitor_sql + stringtf ( " WHERE `Id`=%d " , p_id ) ;
2018-03-09 20:23:40 +08:00
zmDbRow dbrow ;
if ( ! dbrow . fetch ( sql . c_str ( ) ) ) {
Error ( " Can't use query result: %s " , mysql_error ( & dbconn ) ) ;
return NULL ;
}
Monitor * monitor = Monitor : : Load ( dbrow . mysql_row ( ) , load_zones , purpose ) ;
return monitor ;
} // end Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose)
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
int captureResult ;
2016-09-13 09:35:14 +08:00
unsigned int index = image_count % image_buffer_count ;
2016-04-29 20:57:31 +08:00
Image * capture_image = image_buffer [ index ] . image ;
2016-09-13 09:35:14 +08:00
unsigned int deinterlacing_value = deinterlacing & 0xff ;
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 */
2018-03-09 20:23:40 +08:00
capture_image - > CopyBuffer ( * ( next_buffer . image ) ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
2016-04-29 20:57:31 +08:00
/* Capture a new next image */
2018-03-09 20:23:40 +08:00
2016-04-29 20:57:31 +08:00
//Check if FFMPEG camera
2016-09-13 09:35:14 +08:00
// Icon: I don't think we can support de-interlacing on ffmpeg input.... most of the time it will be h264 or mpeg4
2017-05-17 00:04:56 +08:00
if ( ( videowriter = = H264PASSTHROUGH ) & & camera - > SupportsNativeVideo ( ) ) {
2016-05-17 22:01:33 +08:00
captureResult = camera - > CaptureAndRecord ( * ( next_buffer . image ) ,
2017-05-20 03:07:41 +08:00
video_store_data - > recording ,
video_store_data - > event_file ) ;
2017-05-17 00:04:56 +08:00
} else {
2016-04-29 20:57:31 +08:00
captureResult = camera - > Capture ( * ( next_buffer . image ) ) ;
}
if ( FirstCapture ) {
2016-09-13 09:35:14 +08:00
FirstCapture = 0 ;
return 0 ;
}
2016-04-29 20:57:31 +08:00
} else {
//Check if FFMPEG camera
2017-11-04 01:49:42 +08:00
if ( ( videowriter = = H264PASSTHROUGH ) & & camera - > SupportsNativeVideo ( ) ) {
2016-04-29 20:57:31 +08:00
//Warning("ZMC: Recording: %d", video_store_data->recording);
2017-11-04 01:49:42 +08:00
// Should return -1 on error, like loss of signal. Should return 0 if ok but no video frame. > 0 for received a frame.
captureResult = camera - > CaptureAndRecord (
* capture_image ,
video_store_data - > recording ,
video_store_data - > event_file
) ;
} else {
2016-04-29 20:57:31 +08:00
/* Capture directly into image buffer, avoiding the need to memcpy() */
captureResult = camera - > Capture ( * capture_image ) ;
}
2019-04-16 00:55:28 +08:00
} // end if deinterlacing or not
2018-04-18 00:30:58 +08:00
2017-11-04 01:49:42 +08:00
if ( captureResult < 0 ) {
2018-09-05 23:16:17 +08:00
Info ( " Return from Capture (%d), signal loss " , captureResult ) ;
2018-09-11 05:11:16 +08:00
// Tell zma to end the event. zma will reset TRIGGER
trigger_data - > trigger_state = TRIGGER_OFF ;
2016-04-29 20:57:31 +08:00
// Unable to capture image for temporary reason
// Fake a signal loss image
Rgb signalcolor ;
signalcolor = rgb_convert ( signal_check_colour , ZM_SUBPIX_ORDER_BGR ) ; /* HTML colour code is actually BGR in memory, we want RGB */
capture_image - > Fill ( signalcolor ) ;
2017-11-04 01:49:42 +08:00
} else if ( captureResult > 0 ) {
2018-04-18 00:30:58 +08:00
Debug ( 4 , " Return from Capture (%d) " , captureResult ) ;
2018-03-09 20:23:40 +08:00
2016-04-29 20:57:31 +08:00
/* Deinterlacing */
2016-09-13 09:35:14 +08:00
if ( deinterlacing_value = = 1 ) {
2016-04-29 20:57:31 +08:00
capture_image - > Deinterlace_Discard ( ) ;
2016-09-13 09:35:14 +08:00
} else if ( deinterlacing_value = = 2 ) {
2016-04-29 20:57:31 +08:00
capture_image - > Deinterlace_Linear ( ) ;
2016-09-13 09:35:14 +08:00
} else if ( deinterlacing_value = = 3 ) {
2016-04-29 20:57:31 +08:00
capture_image - > Deinterlace_Blend ( ) ;
2016-09-13 09:35:14 +08:00
} else if ( deinterlacing_value = = 4 ) {
2019-04-16 00:55:28 +08:00
capture_image - > Deinterlace_4Field ( next_buffer . image , ( deinterlacing > > 8 ) & 0xff ) ;
2016-09-13 09:35:14 +08:00
} else if ( deinterlacing_value = = 5 ) {
2019-04-16 00:55:28 +08:00
capture_image - > Deinterlace_Blend_CustomRatio ( ( deinterlacing > > 8 ) & 0xff ) ;
2016-04-29 20:57:31 +08:00
}
2018-03-09 20:23:40 +08:00
2016-04-29 20:57:31 +08:00
if ( orientation ! = ROTATE_0 ) {
switch ( orientation ) {
2019-04-16 00:55:28 +08:00
case ROTATE_0 :
2016-04-29 20:57:31 +08:00
// No action required
break ;
case ROTATE_90 :
case ROTATE_180 :
2019-04-16 00:55:28 +08:00
case ROTATE_270 :
capture_image - > Rotate ( ( orientation - 1 ) * 90 ) ;
2016-04-29 20:57:31 +08:00
break ;
case FLIP_HORI :
2019-04-16 00:55:28 +08:00
case FLIP_VERT :
capture_image - > Flip ( orientation = = FLIP_HORI ) ;
2016-04-29 20:57:31 +08:00
break ;
}
2018-04-18 00:30:58 +08:00
} // end if have rotation
2016-04-29 20:57:31 +08:00
if ( capture_image - > Size ( ) > camera - > ImageSize ( ) ) {
2019-04-16 00:55:28 +08:00
Error ( " Captured image %d does not match expected size %d check width, height and colour depth " ,
2020-07-08 04:14:38 +08:00
capture_image - > Size ( ) , camera - > ImageSize ( ) ) ;
2018-02-03 05:07:13 +08:00
return - 1 ;
2016-04-29 20:57:31 +08:00
}
2016-09-13 09:35:14 +08:00
if ( ( index = = shared_data - > last_read_index ) & & ( function > MONITOR ) ) {
2019-04-16 00:55:28 +08:00
Warning ( " Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size " ,
index , image_count ) ;
2016-04-29 20:57:31 +08:00
time_t now = time ( 0 ) ;
double approxFps = double ( image_buffer_count ) / double ( now - image_buffer [ index ] . timestamp - > tv_sec ) ;
time_t last_read_delta = now - shared_data - > last_read_time ;
if ( last_read_delta > ( image_buffer_count / approxFps ) ) {
2019-04-16 00:55:28 +08:00
Warning ( " Last image read from shared memory %ld seconds ago, zma may have gone away " , last_read_delta ) ;
2016-04-29 20:57:31 +08:00
shared_data - > last_read_index = image_buffer_count ;
}
2019-04-16 00:55:28 +08:00
} // end if overrun
2016-04-29 20:57:31 +08:00
if ( privacy_bitmask )
2019-04-16 00:55:28 +08:00
capture_image - > MaskPrivacy ( privacy_bitmask ) ;
2016-04-29 20:57:31 +08:00
2017-11-05 22:59:06 +08:00
// Might be able to remove this call, when we start passing around ZMPackets, which will already have a timestamp
2019-04-16 00:55:28 +08:00
gettimeofday ( image_buffer [ index ] . timestamp , NULL ) ;
2016-04-29 20:57:31 +08:00
if ( config . timestamp_on_capture ) {
2019-04-16 00:55:28 +08:00
TimestampImage ( capture_image , image_buffer [ index ] . timestamp ) ;
2016-04-29 20:57:31 +08:00
}
2017-11-05 22:59:06 +08:00
// Maybe we don't need to do this on all camera types
2018-04-19 22:10:14 +08:00
shared_data - > signal = signal_check_points ? CheckSignal ( capture_image ) : true ;
2016-04-29 20:57:31 +08:00
shared_data - > last_write_index = index ;
shared_data - > last_write_time = image_buffer [ index ] . timestamp - > tv_sec ;
image_count + + ;
2018-04-07 05:21:22 +08:00
if ( image_count & & fps_report_interval & & ( ( ! ( image_count % fps_report_interval ) ) | | image_count < 5 ) ) {
2017-11-22 12:57:01 +08:00
time_t now = image_buffer [ index ] . timestamp - > tv_sec ;
// If we are too fast, we get div by zero. This seems to happen in the case of audio packets.
if ( now ! = last_fps_time ) {
// # of images per interval / the amount of time it took
2019-02-17 00:49:28 +08:00
double new_fps = double ( image_count % fps_report_interval ? image_count : fps_report_interval ) / ( now - last_fps_time ) ;
2018-04-25 02:11:27 +08:00
unsigned int new_camera_bytes = camera - > Bytes ( ) ;
unsigned int new_capture_bandwidth = ( new_camera_bytes - last_camera_bytes ) / ( now - last_fps_time ) ;
last_camera_bytes = new_camera_bytes ;
2018-02-06 11:59:22 +08:00
//Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time );
2017-11-22 12:57:01 +08:00
//Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps );
2019-04-16 00:55:28 +08:00
Info ( " %s: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec " ,
name , image_count , new_fps , new_capture_bandwidth ) ;
2017-11-22 12:57:01 +08:00
last_fps_time = now ;
2018-08-01 00:07:57 +08:00
fps = new_fps ;
db_mutex . lock ( ) ;
static char sql [ ZM_SQL_SML_BUFSIZ ] ;
2018-12-28 02:50:17 +08:00
// 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
2018-08-01 00:07:57 +08:00
snprintf ( sql , sizeof ( sql ) ,
2018-12-28 02:50:17 +08:00
" INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth,Status) "
" VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE "
" CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected' " ,
2018-08-01 00:07:57 +08:00
id , fps , new_capture_bandwidth , fps , new_capture_bandwidth ) ;
if ( mysql_query ( & dbconn , sql ) ) {
Error ( " Can't run query: %s " , mysql_error ( & dbconn ) ) ;
}
db_mutex . unlock ( ) ;
Debug ( 4 , sql ) ;
2018-02-06 11:59:22 +08:00
} // end if time has changed since last update
2018-04-18 00:30:58 +08:00
} // end if it might be time to report the fps
} // end if captureResult
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 ;
2019-04-16 00:55:28 +08:00
} // end int Monitor::Capture
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 ' ;
ts_image - > Annotate ( label_text , label_coord , label_size ) ;
} // 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 ;
if ( function = = RECORD | | function = = MOCORD ) {
//FIXME Is this neccessary? ENdTime should be set in the destructor
gettimeofday ( & ( event - > EndTime ( ) ) , NULL ) ;
}
if ( event_delete_thread ) {
event_delete_thread - > join ( ) ;
delete event_delete_thread ;
event_delete_thread = NULL ;
}
2018-05-03 03:20:01 +08:00
#if 0
2019-04-16 00:55:28 +08:00
event_delete_thread = new std : : thread ( [ ] ( Event * event ) {
2018-03-03 10:26:21 +08:00
Event * e = event ;
event = NULL ;
delete e ;
e = NULL ;
2019-04-16 00:55:28 +08:00
} , event ) ;
2018-05-03 03:20:01 +08:00
# else
2019-04-16 00:55:28 +08:00
delete event ;
event = NULL ;
2018-05-03 03:20:01 +08:00
# endif
2019-04-16 00:55:28 +08:00
video_store_data - > recording = ( struct timeval ) { 0 } ;
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 ) {
2019-05-17 03:37:03 +08:00
ref_image . WriteJpeg ( diag_path_r . c_str ( ) , config . record_diag_images_fifo ) ;
delta_image . WriteJpeg ( diag_path_d . 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 " ,
shared_data - > alarm_x , shared_data - > alarm_y , 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
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 ( ) ) {
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Device : %s \n " , ( ( LocalCamera * ) camera ) - > Device ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Channel : %d \n " , ( ( LocalCamera * ) camera ) - > Channel ( ) ) ;
sprintf ( output + strlen ( output ) , " Standard : %d \n " , ( ( LocalCamera * ) camera ) - > 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 ( ) ) {
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Protocol : %s \n " , ( ( RemoteCamera * ) camera ) - > Protocol ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Host : %s \n " , ( ( RemoteCamera * ) camera ) - > Host ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Port : %s \n " , ( ( RemoteCamera * ) camera ) - > Port ( ) . c_str ( ) ) ;
sprintf ( output + strlen ( output ) , " Path : %s \n " , ( ( RemoteCamera * ) camera ) - > Path ( ) . c_str ( ) ) ;
2016-12-09 00:49:54 +08:00
} else if ( camera - > IsFile ( ) ) {
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Path : %s \n " , ( ( FileCamera * ) camera ) - > Path ( ) ) ;
}
2009-01-28 23:02:33 +08:00
# if HAVE_LIBAVFORMAT
2016-12-09 00:49:54 +08:00
else if ( camera - > IsFfmpeg ( ) ) {
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Path : %s \n " , ( ( FfmpegCamera * ) camera ) - > Path ( ) . c_str ( ) ) ;
}
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 ( ) ) {
2016-04-29 20:57:31 +08:00
sprintf ( output + strlen ( output ) , " Palette : %d \n " , ( ( LocalCamera * ) camera ) - > Palette ( ) ) ;
}
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 ( ) ; }
int Monitor : : PrimeCapture ( ) const { return camera - > PrimeCapture ( ) ; }
int Monitor : : PreCapture ( ) const { return camera - > PreCapture ( ) ; }
int Monitor : : PostCapture ( ) const { return camera - > PostCapture ( ) ; }
2018-04-17 22:02:52 +08:00
int Monitor : : Close ( ) { return camera - > Close ( ) ; } ;
2016-10-12 21:12:09 +08:00
Monitor : : Orientation Monitor : : getOrientation ( ) const { return orientation ; }
2017-04-11 09:51:18 +08:00
2018-03-09 20:23:40 +08:00
Monitor : : Snapshot * Monitor : : getSnapshot ( ) const {
2017-04-11 09:51:18 +08:00
return & image_buffer [ shared_data - > last_write_index % image_buffer_count ] ;
}
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 "
" (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()