Merge branch 'master' into zma_to_thread
This commit is contained in:
commit
4ff341a0f5
|
@ -0,0 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: zoneminder # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
31
.travis.yml
31
.travis.yml
|
@ -13,7 +13,7 @@ addons:
|
|||
ssh_known_hosts: zmrepo.zoneminder.com
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: ppa:iconnor/zoneminder
|
||||
- sourceline: ppa:iconnor/zoneminder-master
|
||||
- key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04
|
||||
packages:
|
||||
- gdebi
|
||||
|
@ -33,24 +33,23 @@ install:
|
|||
|
||||
env:
|
||||
- SMPFLAGS=-j4 OS=el DIST=7
|
||||
- SMPFLAGS=-j4 OS=el DIST=8
|
||||
- SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack
|
||||
- SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack
|
||||
- SMPFLAGS=-j4 OS=fedora DIST=30
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
||||
- SMPFLAGS=-j4 OS=fedora DIST=31
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic ARCH=i386
|
||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386
|
||||
- SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
|
||||
- SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386
|
||||
- SMPFLAGS=-j4 OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
|
||||
- SMPFLAGS=-j4 OS=eslint DIST=eslint
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
@ -58,12 +57,6 @@ services:
|
|||
- mysql
|
||||
- docker
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: eslint
|
||||
install: npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5
|
||||
script: eslint --ext .php,.js .
|
||||
|
||||
script:
|
||||
- utils/packpack/startpackpack.sh
|
||||
|
||||
|
|
|
@ -140,9 +140,9 @@ set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH
|
|||
"Location of temporary files, default: /tmp/zm")
|
||||
set(ZM_LOGDIR "/var/log/zm" CACHE PATH
|
||||
"Location of generated log files, default: /var/log/zm")
|
||||
set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH
|
||||
set(ZM_WEBDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/www" CACHE PATH
|
||||
"Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
|
||||
set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
|
||||
set(ZM_CGIDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
|
||||
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
|
||||
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
|
||||
"Location of the web server cache busting files, default: /var/cache/zoneminder")
|
||||
|
@ -547,6 +547,7 @@ if(AVCODEC_LIBRARIES)
|
|||
check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H)
|
||||
set(optlibsfound "${optlibsfound} AVCodec")
|
||||
else(AVCODEC_LIBRARIES)
|
||||
message(WARNING "\nWhile it should be possible to build ZM without AVCODEC the result will pretty useless.")
|
||||
set(optlibsnotfound "${optlibsnotfound} AVCodec")
|
||||
endif(AVCODEC_LIBRARIES)
|
||||
|
||||
|
@ -905,7 +906,7 @@ message(STATUS "Optional libraries not found:${optlibsnotfound}")
|
|||
|
||||
# Run ZM configuration generator
|
||||
message(STATUS "Running ZoneMinder configuration generator")
|
||||
execute_process(COMMAND perl ./zmconfgen.pl RESULT_VARIABLE zmconfgen_result)
|
||||
execute_process(COMMAND perl ${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl RESULT_VARIABLE zmconfgen_result)
|
||||
if(zmconfgen_result EQUAL 0)
|
||||
message(STATUS
|
||||
"ZoneMinder configuration generator completed successfully")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
ZoneMinder
|
||||
==========
|
||||
|
||||
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||
|
||||
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder)
|
||||
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
||||
[![IRC Network](https://img.shields.io/badge/irc-%23zoneminder-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=zoneminder)
|
||||
|
||||
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
||||
|
||||
|
|
|
@ -287,6 +287,9 @@ CREATE TABLE `Filters` (
|
|||
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
|
||||
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
|
||||
`AutoEmail` tinyint(3) unsigned NOT NULL default '0',
|
||||
`EmailTo` TEXT,
|
||||
`EmailSubject` TEXT,
|
||||
`EmailBody` TEXT,
|
||||
`AutoMessage` tinyint(3) unsigned NOT NULL default '0',
|
||||
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
|
||||
`AutoExecuteCmd` tinytext,
|
||||
|
@ -434,6 +437,7 @@ DROP TABLE IF EXISTS `Monitors`;
|
|||
CREATE TABLE `Monitors` (
|
||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
`Notes` TEXT,
|
||||
`ServerId` int(10) unsigned,
|
||||
`StorageId` smallint(5) unsigned default 0,
|
||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local',
|
||||
|
@ -759,18 +763,91 @@ insert into Users VALUES (NULL,'admin','$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC
|
|||
-- Add a sample filter to purge the oldest 100 events when the disk is 95% full
|
||||
--
|
||||
|
||||
insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',
|
||||
INSERT INTO `Filters`
|
||||
(
|
||||
`Name`,
|
||||
`Query_json`,
|
||||
`AutoArchive`,
|
||||
`AutoVideo`,
|
||||
`AutoUpload`,
|
||||
`AutoEmail`,
|
||||
`EmailTo`,
|
||||
`EmailSubject`,
|
||||
`EmailBody`,
|
||||
`AutoMessage`,
|
||||
`AutoExecute`,
|
||||
`AutoExecuteCmd`,
|
||||
`AutoDelete`,
|
||||
`AutoMove`,
|
||||
`AutoMoveTo`,
|
||||
`AutoCopy`,
|
||||
`AutoCopyTo`,
|
||||
`UpdateDiskSpace`,
|
||||
`Background`,
|
||||
`Concurrent`
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'PurgeWhenFull',
|
||||
'{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',
|
||||
0/*AutoArchive*/,
|
||||
0/*AutoVideo*/,
|
||||
0/*AutoUpload*/,
|
||||
0/*AutoEmail*/,
|
||||
''/*EmailTo*/,
|
||||
''/*EmailSubject*/,
|
||||
''/*EmailBody*/,
|
||||
0/*AutoMessage*/,
|
||||
0/*AutoExecute*/,'',
|
||||
1/*AutoDelete*/,
|
||||
0/*AutoMove*/,0/*MoveTo*/,
|
||||
0/*AutoCopy*/,0/*CopyTo*/,
|
||||
0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/);
|
||||
insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',0,0,0,0,0,0,'',0,0,0,0,0,1,1,0);
|
||||
0/*UpdateDiskSpace*/,
|
||||
1/*Background*/,
|
||||
0/*Concurrent*/
|
||||
);
|
||||
INSERT INTO `Filters`
|
||||
(
|
||||
`Name`,
|
||||
`Query_json`,
|
||||
`AutoArchive`,
|
||||
`AutoVideo`,
|
||||
`AutoUpload`,
|
||||
`AutoEmail`,
|
||||
`EmailTo`,
|
||||
`EmailSubject`,
|
||||
`EmailBody`,
|
||||
`AutoMessage`,
|
||||
`AutoExecute`,
|
||||
`AutoExecuteCmd`,
|
||||
`AutoDelete`,
|
||||
`AutoMove`,
|
||||
`AutoMoveTo`,
|
||||
`AutoCopy`,
|
||||
`AutoCopyTo`,
|
||||
`UpdateDiskSpace`,
|
||||
`Background`,
|
||||
`Concurrent`
|
||||
)
|
||||
VALUES (
|
||||
'Update DiskSpace',
|
||||
'{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',
|
||||
0/*AutoArchive*/,
|
||||
0/*AutoVideo*/,
|
||||
0/*AutoUpload*/,
|
||||
0/*AutoEmail*/,
|
||||
''/*EmailTo*/,
|
||||
''/*EmailSubject*/,
|
||||
''/*EmailBody*/,
|
||||
0/*AutoMessage*/,
|
||||
0/*AutoExecute*/,'',
|
||||
0/*AutoDelete*/,
|
||||
0/*AutoMove*/,0/*MoveTo*/,
|
||||
0/*AutoCopy*/,0/*CopyTo*/,
|
||||
1/*UpdateDiskSpace*/,
|
||||
1/*Background*/,
|
||||
0/*Concurrent*/
|
||||
);
|
||||
|
||||
--
|
||||
-- Add in some sample control protocol definitions
|
||||
|
@ -808,6 +885,7 @@ INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,0
|
|||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'D-Link DCS-5020L','Remote','DCS5020L',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,24,1,0,1,1,1,0,1,0,1,0,0,1,30,0,0,0,0,0,1,0,0,1,30,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
|
@ -819,6 +897,9 @@ INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0
|
|||
--
|
||||
-- Add some monitor preset values
|
||||
--
|
||||
|
||||
INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 640x480, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=1',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 3840x2160, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=0',NULL,3840,2160,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
|
@ -842,6 +923,7 @@ INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rt
|
|||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'Notes'
|
||||
) > 0,
|
||||
"SELECT 'Column Notes already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Notes` TEXT AFTER `Name`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
|
@ -0,0 +1,5 @@
|
|||
--
|
||||
-- This updates a 1.33.16 database to 1.34.0
|
||||
--
|
||||
-- No changes required
|
||||
--
|
|
@ -0,0 +1,5 @@
|
|||
--
|
||||
-- This updates a 1.34.0 database to 1.34.1
|
||||
--
|
||||
-- No changes required
|
||||
--
|
|
@ -0,0 +1,5 @@
|
|||
--
|
||||
-- This updates a 1.34.1 database to 1.34.2
|
||||
--
|
||||
-- No changes required
|
||||
--
|
|
@ -0,0 +1,5 @@
|
|||
--
|
||||
-- This updates a 1.34.2 database to 1.34.3
|
||||
--
|
||||
-- No changes required
|
||||
--
|
|
@ -0,0 +1,5 @@
|
|||
--
|
||||
-- This updates a 1.34.3 database to 1.34.4
|
||||
--
|
||||
-- No changes required
|
||||
--
|
|
@ -0,0 +1,5 @@
|
|||
--
|
||||
-- This updates a 1.34.4 database to 1.34.5
|
||||
--
|
||||
-- No changes required
|
||||
--
|
|
@ -0,0 +1,41 @@
|
|||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Filters'
|
||||
AND column_name = 'EmailTo'
|
||||
) > 0,
|
||||
"SELECT 'Column EmailTo already exists in Filters'",
|
||||
"ALTER TABLE `Filters` ADD `EmailTo` TEXT AFTER `AutoEmail`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
||||
UPDATE Filters SET EmailTo=(SELECT Value FROM Config WHERE Name='ZM_EMAIL_ADDRESS') WHERE AutoEmail=1;
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Filters'
|
||||
AND column_name = 'EmailSubject'
|
||||
) > 0,
|
||||
"SELECT 'Column EmailSubject already exists in Filters'",
|
||||
"ALTER TABLE `Filters` ADD `EmailSubject` TEXT AFTER `EmailTo`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
||||
UPDATE Filters SET EmailSubject=(SELECT Value FROM Config WHERE Name='ZM_EMAIL_SUBJECT') WHERE AutoEmail=1;
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Filters'
|
||||
AND column_name = 'EmailBody'
|
||||
) > 0,
|
||||
"SELECT 'Column EmailBody already exists in Filters'",
|
||||
"ALTER TABLE `Filters` ADD `EmailBody` TEXT AFTER `EmailSubject`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
||||
UPDATE Filters SET EmailBody=(SELECT Value FROM Config WHERE Name='ZM_EMAIL_BODY') WHERE AutoEmail=1;
|
|
@ -22,14 +22,10 @@ What's New
|
|||
switching between httpd <-> nginx requires manaully changing ownership of
|
||||
all event folders and the php session folder after the change.
|
||||
|
||||
4. If you have installed ZoneMinder from the FedBerry repositories, this build
|
||||
of ZoneMinder has support for Raspberry Pi hardware acceleration when using
|
||||
ffmpeg. Unforunately, there is a problem with the same hardware acceleration
|
||||
when using libvlc. Consequently, libvlc support in this build of ZoneMinder
|
||||
has been disabled until the problem is resolved. See the following bug
|
||||
report for details: https://trac.videolan.org/vlc/ticket/18594
|
||||
4. The timezone must now be set from the ZoneMinder web console. See the
|
||||
appropriate README, mentioned in the next step, for details.
|
||||
|
||||
5. Continue on to the next README that corresponds to the chosen webserver:
|
||||
6. Continue on to the next README that corresponds to your chosen webserver:
|
||||
|
||||
README.httpd - Follow these steps when using Apache
|
||||
README.nginx - Follow these steps when using Nginx
|
||||
|
|
|
@ -36,20 +36,17 @@ NOTE: EL7 users should replace "dnf" with "yum" in the instructions below.
|
|||
sudo chown root:apache *.conf
|
||||
sudo chmod 640 *.conf
|
||||
|
||||
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
|
||||
timezone. PHP will complain loudly if this is not set, or if it is set
|
||||
incorrectly, and these complaints will show up in the zoneminder logging
|
||||
system as errors.
|
||||
4. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||
|
||||
If you are not sure of the proper timezone specification to use, look at
|
||||
http://php.net/date.timezone
|
||||
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||
Do this after completing step 10, below.
|
||||
|
||||
Note that timezone errors will appear in the ZoneMinder log until this
|
||||
has been completed.
|
||||
|
||||
5. Disable SELinux
|
||||
|
||||
We currently do not have the resources to create and maintain an accurate
|
||||
SELinux policy for ZoneMinder on Fedora. We will gladly accept pull
|
||||
reqeusts from anyone who wishes to do the work. In the meantime, SELinux
|
||||
will need to be disabled or put into permissive mode.
|
||||
SELinux must be disabled or put into permissive mode. This is not optional!
|
||||
|
||||
To immediately disbale SELinux for the current seesion, issue the following
|
||||
from the command line:
|
||||
|
@ -166,3 +163,11 @@ Upgrades
|
|||
sudo systemctl restart httpd
|
||||
sudo systemctl start zoneminder
|
||||
|
||||
6. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||
|
||||
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||
Do this now.
|
||||
|
||||
Note that timezone errors will appear in the ZoneMinder log until this
|
||||
has been completed.
|
||||
|
||||
|
|
|
@ -34,13 +34,13 @@ New installs
|
|||
sudo chown root:nginx *.conf
|
||||
sudo chmod 640 *.conf
|
||||
|
||||
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
|
||||
timezone. PHP will complain loudly if this is not set, or if it is set
|
||||
incorrectly, and these complaints will show up in the zoneminder logging
|
||||
system as errors.
|
||||
4. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||
|
||||
If you are not sure of the proper timezone specification to use, look at
|
||||
http://php.net/date.timezone
|
||||
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||
Do this after completing step 10, below.
|
||||
|
||||
Note that timezone errors will appear in the ZoneMinder log until this
|
||||
has been completed.
|
||||
|
||||
5. Disable SELinux
|
||||
|
||||
|
@ -169,3 +169,11 @@ Upgrades
|
|||
sudo systemctl restart php-fpm
|
||||
sudo systemctl start zoneminder
|
||||
|
||||
6. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||
|
||||
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||
Do this now.
|
||||
|
||||
Note that timezone errors will appear in the ZoneMinder log until this
|
||||
has been completed.
|
||||
|
||||
|
|
|
@ -14,16 +14,21 @@
|
|||
# This will tell zoneminder's cmake process we are building against a known distro
|
||||
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
|
||||
|
||||
# Fedora >= 25 needs apcu backwards compatibility module
|
||||
%if 0%{?fedora} >= 25
|
||||
# Fedora needs apcu backwards compatibility module
|
||||
%if 0%{?fedora}
|
||||
%global with_apcu_bc 1
|
||||
%endif
|
||||
|
||||
# Newer php's keep json functions in a subpackage
|
||||
%if 0%{?fedora} || 0%{?rhel} >= 8
|
||||
%global with_php_json 1
|
||||
%endif
|
||||
|
||||
# The default for everything but el7 these days
|
||||
%global _hardened_build 1
|
||||
|
||||
Name: zoneminder
|
||||
Version: 1.33.14
|
||||
Version: 1.35.0
|
||||
Release: 1%{?dist}
|
||||
Summary: A camera monitoring and analysis tool
|
||||
Group: System Environment/Daemons
|
||||
|
@ -105,7 +110,7 @@ Summary: Common files for ZoneMinder, not tied to a specific web server
|
|||
Requires: php-mysqli
|
||||
Requires: php-common
|
||||
Requires: php-gd
|
||||
%{?fedora:Requires: php-json}
|
||||
%{?with_php_json:Requires: php-json}
|
||||
Requires: php-pecl-apcu
|
||||
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
|
||||
Requires: cambozola
|
||||
|
@ -411,26 +416,26 @@ EOF
|
|||
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
||||
|
||||
%changelog
|
||||
* Sun Aug 11 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.14-1
|
||||
- Bump to 1.33.13 Development
|
||||
* Tue Feb 04 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.2-1
|
||||
- 1.34.2 Release
|
||||
|
||||
* Sun Jul 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.12-1
|
||||
- Bump to 1.33.12 Development
|
||||
* Fri Jan 31 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.1-1
|
||||
- 1.34.1 Release
|
||||
|
||||
* Sun Jun 23 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.9-1
|
||||
- Bump to 1.33.9 Development
|
||||
* Sat Jan 18 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.0-1
|
||||
- 1.34.0 Release
|
||||
|
||||
* Tue Apr 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.8-1
|
||||
- Bump to 1.33.8 Development
|
||||
* Tue Dec 17 2019 Leigh Scott <leigh123linux@gmail.com> - 1.32.3-5
|
||||
- Mass rebuild for x264
|
||||
|
||||
* Sun Apr 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.6-1
|
||||
- Bump to 1.33.6 Development
|
||||
* Wed Aug 07 2019 Leigh Scott <leigh123linux@gmail.com> - 1.32.3-4
|
||||
- Rebuild for new ffmpeg version
|
||||
|
||||
* Sat Mar 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.4-1
|
||||
- Bump to 1.33.4 Development
|
||||
* Tue Mar 12 2019 Sérgio Basto <sergio@serjux.com> - 1.32.3-3
|
||||
- Mass rebuild for x264
|
||||
|
||||
* Tue Dec 11 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.0-1
|
||||
- Bump to 1.33.0 Development
|
||||
* Tue Mar 05 2019 RPM Fusion Release Engineering <leigh123linux@gmail.com> - 1.32.3-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
||||
|
||||
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
||||
- 1.32.3 Release
|
||||
|
|
|
@ -6,6 +6,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
|
|||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# Order matters. This alias must come first.
|
||||
Alias /zm/cache /var/cache/zoneminder/cache
|
||||
<Directory /var/cache/zoneminder/cache>
|
||||
|
|
|
@ -61,7 +61,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,libjson-maybexs-perl
|
||||
,libsys-mmap-perl [!hurd-any]
|
||||
,liburi-encode-perl
|
||||
,libwww-perl
|
||||
,libwww-perl, liburi-perl
|
||||
,libdata-dump-perl
|
||||
,libdatetime-perl
|
||||
,libclass-std-fast-perl
|
||||
|
@ -74,7 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,libfile-slurp-perl
|
||||
,mysql-client | mariadb-client | virtual-mysql-client
|
||||
,perl-modules
|
||||
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc
|
||||
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc, php-json | php5-json
|
||||
,policykit-1
|
||||
,rsyslog | system-log-daemon
|
||||
,zip
|
||||
|
|
|
@ -127,7 +127,7 @@ If you are using the old credentials mechanism present in v1.0, then the credent
|
|||
Key lifetime (v2.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_exipres`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs.
|
||||
In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_expires`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs.
|
||||
|
||||
Understanding access/refresh tokens (v2.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -482,6 +482,7 @@ Create a Zone
|
|||
&Zone[Units]=Percent\
|
||||
&Zone[NumCoords]=4\
|
||||
&Zone[Coords]=0,0 639,0 639,479 0,479\
|
||||
&Zone[Area]=307200\
|
||||
&Zone[AlarmRGB]=16711680\
|
||||
&Zone[CheckMethod]=Blobs\
|
||||
&Zone[MinPixelThreshold]=25\
|
||||
|
|
|
@ -68,9 +68,13 @@ Here is what the filter window looks like
|
|||
* %ESM% Maximum score of the event
|
||||
* %EP% Path to the event
|
||||
* %EPS% Path to the event stream
|
||||
* %EPF1% Path to the frame view for the first alarmed event image
|
||||
* %EPFM% Path to the frame view for the (first) event image with the highest score
|
||||
* %EFMOD% Path to image containing object detection, in frame view
|
||||
* %EPI% Path to the event images
|
||||
* %EPI1% Path to the first alarmed event image
|
||||
* %EPIM% Path to the (first) event image with the highest score
|
||||
* %EPI1% Path to the first alarmed event image, suitable for use in img tags
|
||||
* %EPIM% Path to the (first) event image with the highest score, suitable for use in img tags
|
||||
* %EIMOD% Path to image containing object detection, suitable for use in img tags
|
||||
* %EI1% Attach first alarmed event image
|
||||
* %EIM% Attach (first) event image with the highest score
|
||||
* %EV% Attach event mpeg video
|
||||
|
@ -81,7 +85,6 @@ Here is what the filter window looks like
|
|||
* %MEW% Number of events for the monitor in the last week
|
||||
* %MEM% Number of events for the monitor in the last month
|
||||
* %MEA% Number of archived events for the monitor
|
||||
* %MOD% Path to image containing object detection
|
||||
* %MP% Path to the monitor window
|
||||
* %MPS% Path to the monitor stream
|
||||
* %MPI% Path to the monitor recent image
|
||||
|
|
|
@ -109,7 +109,7 @@ This brings up the new monitor window:
|
|||
|
||||
* In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone`
|
||||
|
||||
* In Analytis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs
|
||||
* In Analysis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs
|
||||
|
||||
.. note::
|
||||
Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example)
|
||||
|
@ -123,6 +123,8 @@ This brings up the new monitor window:
|
|||
* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera)
|
||||
* Note that starting ZM 1.34, GPUs are supported. In my case, I have an NVIDIA GeForce GTX1050i. These ``cuda`` and ``cuvid`` parameters are what my system supports to use the NVIDIA hardware decoder and GPU resources. If you don't have a GPU, or don't know how to configure your ffmpeg to support it, leave it empty for now. In future, we will add a section on how to set up a GPU
|
||||
|
||||
**NOTE**: It is entirely possible that ``cuda`` and ``cuvid`` don't work for you and you need different values. Isaac uses ``cuda`` in ``DecoderHWAccelName`` and leaves ``DecoderHWAccelDevice`` empty. Try that too.
|
||||
|
||||
.. todo::
|
||||
add GPU docs
|
||||
|
||||
|
|
|
@ -28,3 +28,4 @@ zmtelemetry\[[[:digit:]]+\]: INF \[Telemetry data uploaded successfully.\]$
|
|||
zmtelemetry\[[[:digit:]]+\]: INF \[Sending data to ZoneMinder Telemetry server.\]$
|
||||
zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send to ZoneMinder Telemetry server.\]$
|
||||
web_php\[[[:digit:]]+\]: INF \[Login successful for user "[[:alnum:]]+"\]$
|
||||
zmeventnotification\[[[:digit:]]+\]: INF
|
||||
|
|
|
@ -114,7 +114,7 @@ sub get_service_urls {
|
|||
|
||||
my $result = $self->service('device', 'ep')->GetServices( {
|
||||
IncludeCapability => 'true', # boolean
|
||||
},,
|
||||
}
|
||||
);
|
||||
if ( $result ) {
|
||||
foreach my $svc ( @{ $result->get_Service() } ) {
|
||||
|
@ -142,7 +142,7 @@ sub get_service_urls {
|
|||
if ( my $function = $capabilities->can( "get_$capability" ) ) {
|
||||
my $Services = $function->( $capabilities );
|
||||
if ( !$Services ) {
|
||||
print "Nothing returned ffrom get_$capability\n";
|
||||
#print "Nothing returned from get_$capability\n";
|
||||
} else {
|
||||
foreach my $svc ( @{ $Services } ) {
|
||||
# The capability versions don't have a namespace, so just lowercase them.
|
||||
|
@ -202,7 +202,7 @@ sub BUILD {
|
|||
# deserializer_args => { strict => 0 }
|
||||
});
|
||||
|
||||
$services_of{$ident}{'device'} = { url => $url_svc_device, ep => $svc_device };
|
||||
$services_of{$ident}{device} = { url => $url_svc_device, ep => $svc_device };
|
||||
|
||||
# Can't, don't have credentials yet
|
||||
# $self->get_service_urls();
|
||||
|
@ -260,7 +260,7 @@ sub set_credentials {
|
|||
sub create_services {
|
||||
my ($self) = @_;
|
||||
|
||||
#$self->get_service_urls();
|
||||
$self->get_service_urls();
|
||||
|
||||
if ( defined $self->service('media', 'url') ) {
|
||||
$self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({
|
||||
|
|
|
@ -43,7 +43,7 @@ my %message_of :ATTR(:name<message> :default<()>);
|
|||
my %is_success_of :ATTR(:name<is_success> :default<()>);
|
||||
|
||||
my %local_addr_of :ATTR(:name<local_addr> :init_arg<local_addr> :default<()>);
|
||||
|
||||
my $net_interface;
|
||||
|
||||
# create methods normally inherited from SOAP::Client
|
||||
SUBFACTORY: {
|
||||
|
@ -60,14 +60,22 @@ sub _notify_response
|
|||
|
||||
}
|
||||
|
||||
sub set_net_interface {
|
||||
my $self = shift;
|
||||
$net_interface = shift;
|
||||
}
|
||||
|
||||
sub send_multi() {
|
||||
my ($self, $address, $port, $utf8_string) = @_;
|
||||
|
||||
my $destination = $address . ':' . $port;
|
||||
my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
|
||||
LocalPort=>$port, PeerAddr=>$destination, ReuseAddr=>1)
|
||||
|
||||
or die 'Cannot open multicast socket to ' . ${address} . ':' . ${port};
|
||||
my $socket = IO::Socket::Multicast->new(
|
||||
PROTO => 'udp',
|
||||
LocalPort=>$port,
|
||||
PeerAddr=>$destination,
|
||||
ReuseAddr=>1
|
||||
) or die 'Cannot open multicast socket to ' . ${address} . ':' . ${port};
|
||||
$_ = $socket->mcast_if($net_interface) if $net_interface;
|
||||
|
||||
my $bytes = $utf8_string;
|
||||
utf8::encode($bytes);
|
||||
|
@ -80,9 +88,11 @@ sub receive_multi() {
|
|||
my ($self, $address, $port) = @_;
|
||||
my $data = undef;
|
||||
|
||||
my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
|
||||
LocalPort=>$port, ReuseAddr=>1);
|
||||
$socket->mcast_add($address);
|
||||
my $socket = IO::Socket::Multicast->new(
|
||||
PROTO => 'udp',
|
||||
LocalPort=>$port,
|
||||
ReuseAddr=>1);
|
||||
$socket->mcast_add($address, $net_interface);
|
||||
|
||||
my $readbits = '';
|
||||
vec($readbits, $socket->fileno, 1) = 1;
|
||||
|
@ -98,10 +108,14 @@ sub receive_uni() {
|
|||
my ($self, $address, $port, $localaddr) = @_;
|
||||
my $data = undef;
|
||||
|
||||
my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
|
||||
LocalAddr => $localaddr, LocalPort=>$port, ReuseAddr=>1);
|
||||
my $socket = IO::Socket::Multicast->new(
|
||||
PROTO => 'udp',
|
||||
LocalAddr => $localaddr,
|
||||
LocalPort=>$port,
|
||||
ReuseAddr=>1
|
||||
);
|
||||
|
||||
$socket->mcast_add($address);
|
||||
$socket->mcast_add($address, $net_interface);
|
||||
|
||||
my $readbits = '';
|
||||
vec($readbits, $socket->fileno, 1) = 1;
|
||||
|
@ -126,6 +140,7 @@ sub send_receive {
|
|||
$self->send_multi($address, $port, $envelope);
|
||||
|
||||
my $localaddr = $self->get_local_addr();
|
||||
#warn "localddr $localaddr";
|
||||
|
||||
my ($response, $last_response);
|
||||
my $wait = WAIT_COUNT;
|
||||
|
@ -146,13 +161,12 @@ sub send_receive {
|
|||
|
||||
if ( $last_response ) {
|
||||
$self->set_code();
|
||||
$self->set_message("");
|
||||
$self->set_message('');
|
||||
$self->set_is_success(1);
|
||||
$self->set_status('OK');
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
$self->set_code();
|
||||
$self->set_message("Timed out waiting for response");
|
||||
$self->set_message('Timed out waiting for response');
|
||||
$self->set_is_success(0);
|
||||
$self->set_status('TIMEOUT');
|
||||
}
|
||||
|
@ -161,3 +175,4 @@ sub send_receive {
|
|||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
|
|
@ -785,7 +785,7 @@ our @options = (
|
|||
},
|
||||
{
|
||||
name => 'ZM_TIMEZONE',
|
||||
default => 'UTC',
|
||||
default => '',
|
||||
description => 'The timezone that php should use.',
|
||||
help => q`
|
||||
This should be set equal to the system timezone of the mysql server`,
|
||||
|
@ -1127,7 +1127,7 @@ our @options = (
|
|||
},
|
||||
{
|
||||
name => 'ZM_LOG_LEVEL_FILE',
|
||||
default => '-5',
|
||||
default => '1',
|
||||
description => 'Save logging output to component files',
|
||||
help => q`
|
||||
ZoneMinder logging is now more integrated between
|
||||
|
@ -1312,7 +1312,7 @@ our @options = (
|
|||
},
|
||||
{
|
||||
name => 'ZM_LOG_DEBUG_FILE',
|
||||
default => '@ZM_LOGDIR@/zm_debug.log+',
|
||||
default => '',
|
||||
description => 'Where extra debug is output to',
|
||||
help => q`
|
||||
This option allows you to specify a different target for debug
|
||||
|
@ -2213,7 +2213,7 @@ our @options = (
|
|||
that match the appropriate filters will be sent to.
|
||||
`,
|
||||
type => $types{email},
|
||||
category => 'mail',
|
||||
category => 'hidden',
|
||||
},
|
||||
{
|
||||
name => 'ZM_EMAIL_TEXT',
|
||||
|
@ -2253,7 +2253,7 @@ our @options = (
|
|||
sent for any events that match the appropriate filters.
|
||||
`,
|
||||
type => $types{string},
|
||||
category => 'mail',
|
||||
category => 'hidden',
|
||||
},
|
||||
{
|
||||
name => 'ZM_EMAIL_BODY',
|
||||
|
@ -2280,7 +2280,7 @@ our @options = (
|
|||
sent for any events that match the appropriate filters.
|
||||
`,
|
||||
type => $types{text},
|
||||
category => 'mail',
|
||||
category => 'hidden',
|
||||
},
|
||||
{
|
||||
name => 'ZM_OPT_MESSAGE',
|
||||
|
@ -2468,7 +2468,7 @@ our @options = (
|
|||
},
|
||||
{
|
||||
name => 'ZM_WATCH_MAX_DELAY',
|
||||
default => '5',
|
||||
default => '45',
|
||||
description => 'The maximum delay allowed since the last captured image',
|
||||
help => q`
|
||||
The zmwatch daemon checks the image capture performance of the
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Base Control Module, $Date$, $Revision$
|
||||
# ZoneMinder Base Control Module
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@ -46,11 +46,11 @@ our $AUTOLOAD;
|
|||
sub new {
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
if ( !defined($id) ) {
|
||||
Fatal('No monitor defined when invoking protocol '.$class);
|
||||
}
|
||||
my $self = {};
|
||||
$self->{name} = $class;
|
||||
if ( !defined($id) ) {
|
||||
Fatal('No monitor defined when invoking protocol '.$self->{name});
|
||||
}
|
||||
$self->{id} = $id;
|
||||
bless($self, $class);
|
||||
return $self;
|
||||
|
@ -78,7 +78,7 @@ sub AUTOLOAD {
|
|||
|
||||
sub getKey {
|
||||
my $self = shift;
|
||||
return( $self->{id} );
|
||||
return $self->{id};
|
||||
}
|
||||
|
||||
sub open {
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Acrest HTTP API Control Protocol Module, 20180214, Rev 3.0
|
||||
#
|
||||
# Change Log
|
||||
#
|
||||
# Rev 3.0:
|
||||
# - Fixes incorrect method names
|
||||
# - Updates control sequences to Amcrest HTTP Protocol API v 2.12
|
||||
# - Extends control features
|
||||
#
|
||||
# Rev 2.0:
|
||||
# - Fixed installation instructions text, no changes to functionality.
|
||||
# ZoneMinder Amcrest HTTP API Control Protocol Module
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
|
@ -38,6 +28,8 @@ use Time::HiRes qw( usleep );
|
|||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
require LWP::UserAgent;
|
||||
use URI;
|
||||
|
||||
our @ISA = qw(ZoneMinder::Control);
|
||||
|
||||
|
@ -50,130 +42,130 @@ our @ISA = qw(ZoneMinder::Control);
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
sub new
|
||||
{
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new($id);
|
||||
bless($self, $class);
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
Debug( "Received command: $name" );
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
sub open {
|
||||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
$self->{state} = 'open';
|
||||
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||
# Has no scheme at the beginning, so won't parse as a URI
|
||||
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
|
||||
}
|
||||
my $uri = URI->new($self->{Monitor}->{ControlAddress});
|
||||
|
||||
sub initUA
|
||||
{
|
||||
my $self = shift;
|
||||
my $user = undef;
|
||||
my $password = undef;
|
||||
my $address = undef;
|
||||
|
||||
if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ )
|
||||
{
|
||||
$user = $1;
|
||||
$password = $2;
|
||||
$address = $3;
|
||||
}
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->credentials("$address", "Login to " . $self->{Monitor}->{ControlDevice}, "$user", "$password");
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||
my ( $username, $password );
|
||||
my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice};
|
||||
if ( $self->{Monitor}->{ControlAddress} ) {
|
||||
( $username, $password ) = $uri->authority() =~ /^(.*):(.*)@(.*)$/;
|
||||
|
||||
$$self{address} = $uri->host_port();
|
||||
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
|
||||
# Testing seems to show that we need the username/password in each url as well as credentials
|
||||
$$self{base_url} = $uri->canonical();
|
||||
Debug('Using initial credentials for '.$uri->host_port().", $realm, $username, $password, base_url: $$self{base_url} auth:".$uri->authority());
|
||||
}
|
||||
|
||||
sub destroyUA
|
||||
{
|
||||
my $self = shift;
|
||||
# Detect REALM, has to be /cgi-bin/ptz.cgi because just / accepts no auth
|
||||
my $res = $self->{ua}->get($$self{base_url}.'cgi-bin/ptz.cgi');
|
||||
|
||||
$self->{ua} = undef;
|
||||
if ( $res->is_success ) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||
|
||||
my $headers = $res->headers();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
}
|
||||
|
||||
if ( $$headers{'www-authenticate'} ) {
|
||||
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||
if ( $tokens =~ /realm="([^"]+)"/i ) {
|
||||
if ( $realm ne $1 ) {
|
||||
$realm = $1;
|
||||
Debug("Changing REALM to ($realm)");
|
||||
$self->{ua}->credentials($$self{address}, $realm, $username, $password);
|
||||
$res = $self->{ua}->get($$self{base_url}.'cgi-bin/ptz.cgi');
|
||||
if ( $res->is_success() ) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
} elsif ( $res->status_line eq '400 Bad Request' ) {
|
||||
# In testing, this second request fails with Bad Request, I assume because we didn't actually give it a command.
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
} else {
|
||||
Error('Authentication still failed after updating REALM' . $res->status_line);
|
||||
$headers = $res->headers();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Header $k => $$headers{$k}");
|
||||
} # end foreach
|
||||
}
|
||||
} else {
|
||||
Error('Authentication failed, not a REALM problem');
|
||||
}
|
||||
} else {
|
||||
Error('Failed to match realm in tokens');
|
||||
} # end if
|
||||
} else {
|
||||
Debug('No headers line');
|
||||
} # end if headers
|
||||
} else {
|
||||
Error("Failed to get $$self{base_url}cgi-bin/ptz.cgi ".$res->status_line());
|
||||
|
||||
} # end if $res->status_line() eq '401 Unauthorized'
|
||||
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub close {
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
sub sendCmd {
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $result = undef;
|
||||
|
||||
destroyUA($self);
|
||||
initUA($self);
|
||||
$self->printMsg($cmd, 'Tx');
|
||||
|
||||
my $user = undef;
|
||||
my $password = undef;
|
||||
my $address = undef;
|
||||
my $res = $self->{ua}->get($$self{base_url}.$cmd);
|
||||
|
||||
if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ )
|
||||
{
|
||||
$user = $1;
|
||||
$password = $2;
|
||||
$address = $3;
|
||||
}
|
||||
|
||||
printMsg( $cmd, "Tx" );
|
||||
|
||||
my $req = HTTP::Request->new( GET=>"http://$address/$cmd" );
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success )
|
||||
{
|
||||
if ( $res->is_success ) {
|
||||
$result = !undef;
|
||||
# Command to camera appears successful, write Info item to log
|
||||
Info( "Camera control: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
||||
Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{base_url}.$cmd);
|
||||
# TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status.
|
||||
} else {
|
||||
# Try again
|
||||
$res = $self->{ua}->get($$self{base_url}.$cmd);
|
||||
if ( $res->is_success ) {
|
||||
# Command to camera appears successful, write Info item to log
|
||||
Info('Camera control 2: \''.$res->status_line().'\' for URL '.$$self{base_url}.$cmd);
|
||||
} else {
|
||||
Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$$self{base_url}.$cmd);
|
||||
$res = $self->{ua}->get('http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Camera control command FAILED: '".$res->status_line()."' for URL ".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
||||
}
|
||||
|
||||
return( $result );
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub reset
|
||||
{
|
||||
sub reset {
|
||||
my $self = shift;
|
||||
# This reboots the camera effectively resetting it
|
||||
Debug( "Camera Reset" );
|
||||
$self->sendCmd('cgi-bin/magicBox.cgi?action=reboot');
|
||||
##FIXME: Exit is a bad idea as it appears to cause zmc to run away.
|
||||
#Exit (0);
|
||||
}
|
||||
|
||||
# NOTE: I'm putting this in, but absolute camera movement does not seem to be well supported in the classic skin ATM.
|
||||
|
@ -188,79 +180,71 @@ sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here...
|
|||
my $pan_degrees = shift || 0;
|
||||
my $tilt_degrees = shift || 0;
|
||||
my $speed = shift || 1;
|
||||
Debug( "Move ABS" );
|
||||
Debug('Move ABS');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed);
|
||||
}
|
||||
|
||||
sub moveConUp
|
||||
{
|
||||
sub moveConUp {
|
||||
my $self = shift;
|
||||
Debug( "Move Up" );
|
||||
Debug('Move Up');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Up&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
usleep(500); ##XXX Should this be passed in as a "speed" parameter?
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Up&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConDown
|
||||
{
|
||||
sub moveConDown {
|
||||
my $self = shift;
|
||||
Debug( "Move Down" );
|
||||
Debug('Move Down');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
usleep(500);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConLeft
|
||||
{
|
||||
sub moveConLeft {
|
||||
my $self = shift;
|
||||
Debug( "Move Left" );
|
||||
Debug('Move Left');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
usleep(500);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConRight
|
||||
{
|
||||
sub moveConRight {
|
||||
my $self = shift;
|
||||
Debug( "Move Right" );
|
||||
Debug('Move Right');
|
||||
# $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=270&arg2=5&arg3=0' );
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
usleep(500);
|
||||
Debug( "Move Right Stop" );
|
||||
Debug('Move Right Stop');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Right&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConUpRight
|
||||
{
|
||||
sub moveConUpRight {
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Up Right" );
|
||||
Debug('Move Diagonally Up Right');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0');
|
||||
usleep(500);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConDownRight
|
||||
{
|
||||
sub moveConDownRight {
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Down Right" );
|
||||
Debug('Move Diagonally Down Right');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0');
|
||||
usleep(500);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConUpLeft
|
||||
{
|
||||
sub moveConUpLeft {
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Up Left" );
|
||||
Debug('Move Diagonally Up Left');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0');
|
||||
usleep(500);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
}
|
||||
|
||||
sub moveConDownLeft
|
||||
{
|
||||
sub moveConDownLeft {
|
||||
my $self = shift;
|
||||
Debug( "Move Diagonally Down Left" );
|
||||
Debug('Move Diagonally Down Left');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0');
|
||||
usleep (500);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0');
|
||||
|
@ -270,10 +254,9 @@ sub moveConDownLeft
|
|||
# So we'll just send the camera to 0* Horz, 0* Vert, zoom out; Also, Amcrest does not seem to
|
||||
# support a generic stop-all-current-action command.
|
||||
|
||||
sub moveStop
|
||||
{
|
||||
sub moveStop {
|
||||
my $self = shift;
|
||||
Debug( "Move Stop/Center" );
|
||||
Debug('Move Stop/Center');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1=0&arg2=0&arg3=0&arg4=1');
|
||||
}
|
||||
|
||||
|
@ -281,15 +264,13 @@ sub moveStop
|
|||
# The current API does not support a Home per se, so we'll just send the camera to preset #1
|
||||
# NOTE: It goes without saying that the user must have set up preset #1 for this to work.
|
||||
|
||||
sub presetHome
|
||||
{
|
||||
sub presetHome {
|
||||
my $self = shift;
|
||||
Debug( "Home Preset" );
|
||||
Debug('Home Preset');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2=1&arg3=0&arg4=0');
|
||||
}
|
||||
|
||||
sub presetGoto
|
||||
{
|
||||
sub presetGoto {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
|
@ -297,19 +278,17 @@ sub presetGoto
|
|||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=GotoPreset&&arg1=0&arg2='.$preset.'&arg3=0&arg4=0');
|
||||
}
|
||||
|
||||
sub presetSet
|
||||
{
|
||||
sub presetSet {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
Debug( "Set Preset" );
|
||||
Debug('Set Preset');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=SetPreset&arg1=0&arg2='.$preset.'&arg3=0&arg4=0');
|
||||
}
|
||||
|
||||
# NOTE: This does not appear to be implemented in the classic skin. But we'll leave it here for later.
|
||||
|
||||
sub moveMap
|
||||
{
|
||||
sub moveMap {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
|
@ -325,19 +304,17 @@ sub moveMap
|
|||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan.'&arg2='.$tilt.'&arg3=1&arg4=1');
|
||||
}
|
||||
|
||||
sub zoomConTele
|
||||
{
|
||||
sub zoomConTele {
|
||||
my $self = shift;
|
||||
Debug( "Zoom continuous tele" );
|
||||
Debug('Zoom continuous tele');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||
usleep(100000);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||
}
|
||||
|
||||
sub zoomConWide
|
||||
{
|
||||
sub zoomConWide {
|
||||
my $self = shift;
|
||||
Debug( "Zoom continuous wide" );
|
||||
Debug('Zoom continuous wide');
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||
usleep (100000);
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Axis version 2 API Control Protocol Module, $Date$, $Revision$
|
||||
# ZoneMinder Axis version 2 API Control Protocol Module
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@ -43,179 +43,206 @@ use ZoneMinder::Logger qw(:all);
|
|||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
use URI;
|
||||
|
||||
sub open
|
||||
{
|
||||
our $ADDRESS;
|
||||
|
||||
sub open {
|
||||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||
# Has no scheme at the beginning, so won't parse as a URI
|
||||
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
|
||||
}
|
||||
my $uri = URI->new($self->{Monitor}->{ControlAddress});
|
||||
$ADDRESS = $uri->scheme.'://'.$uri->authority().$uri->path().($uri->port()?':'.$uri->port():'');
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
$self->{ua}->cookie_jar( {} );
|
||||
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||
$self->{state} = 'closed';
|
||||
|
||||
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
|
||||
my $realm = $self->{Monitor}->{ControlDevice};
|
||||
|
||||
$self->{ua}->credentials($ADDRESS, $realm, $username, $password);
|
||||
|
||||
# test auth
|
||||
my $res = $self->{ua}->get($ADDRESS.'/cgi/ptdc.cgi');
|
||||
|
||||
if ( $res->is_success ) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
my $headers = $res->headers();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
if ( $$headers{'www-authenticate'} ) {
|
||||
Debug('Authenticating');
|
||||
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||
if ( $tokens =~ /\w+="([^"]+)"/i ) {
|
||||
if ( $realm ne $1 ) {
|
||||
$realm = $1;
|
||||
Debug("Changing REALM to $realm");
|
||||
$self->{ua}->credentials($host, $realm, $username, $password);
|
||||
$res = $self->{ua}->get($ADDRESS);
|
||||
if ( $res->is_success() ) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
}
|
||||
Error('Authentication still failed after updating REALM'.$res->status_line);
|
||||
$headers = $res->headers();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
} # end foreach
|
||||
} else {
|
||||
Error('Authentication failed, not a REALM problem');
|
||||
}
|
||||
} else {
|
||||
Error('Failed to match realm in tokens');
|
||||
} # end if
|
||||
} else {
|
||||
Debug('No headers line');
|
||||
} # end if headers
|
||||
} # end if $res->status_line() eq '401 Unauthorized'
|
||||
} # end sub open
|
||||
|
||||
sub sendCmd {
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
|
||||
my $result = undef;
|
||||
$self->printMsg($cmd, 'Tx');
|
||||
|
||||
printMsg( $cmd, "Tx" );
|
||||
my $url = $ADDRESS.$cmd;
|
||||
my $res = $self->{ua}->get($url);
|
||||
|
||||
#print( "http://$address/$cmd\n" );
|
||||
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success )
|
||||
{
|
||||
$result = !undef;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Error check failed: '".$res->status_line()."'" );
|
||||
if ( $res->is_success ) {
|
||||
Debug('sndCmd command: ' . $url . ' content: '.$res->content);
|
||||
return !undef;
|
||||
}
|
||||
|
||||
return( $result );
|
||||
Error("Error cmd $url failed: '".$res->status_line()."'");
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub cameraReset
|
||||
{
|
||||
sub cameraReset {
|
||||
my $self = shift;
|
||||
Debug( "Camera Reset" );
|
||||
my $cmd = "/axis-cgi/admin/restart.cgi";
|
||||
Debug('Camera Reset');
|
||||
my $cmd = '/axis-cgi/admin/restart.cgi';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConUp
|
||||
{
|
||||
sub moveConUp {
|
||||
my $self = shift;
|
||||
Debug( "Move Up" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=up";
|
||||
Debug('Move Up');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=up';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConDown
|
||||
{
|
||||
sub moveConDown {
|
||||
my $self = shift;
|
||||
Debug( "Move Down" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=down";
|
||||
Debug('Move Down');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=down';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConLeft
|
||||
{
|
||||
sub moveConLeft {
|
||||
my $self = shift;
|
||||
Debug( "Move Left" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=left";
|
||||
Debug('Move Left');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=left';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConRight
|
||||
{
|
||||
sub moveConRight {
|
||||
my $self = shift;
|
||||
Debug( "Move Right" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=right";
|
||||
Debug('Move Right');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=right';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConUpRight
|
||||
{
|
||||
sub moveConUpRight {
|
||||
my $self = shift;
|
||||
Debug( "Move Up/Right" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=upright";
|
||||
Debug('Move Up/Right');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=upright';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConUpLeft
|
||||
{
|
||||
sub moveConUpLeft {
|
||||
my $self = shift;
|
||||
Debug( "Move Up/Left" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=upleft";
|
||||
Debug('Move Up/Left');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=upleft';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveConDownRight
|
||||
{
|
||||
sub moveConDownRight {
|
||||
my $self = shift;
|
||||
Debug( "Move Down/Right" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=downright";
|
||||
Debug('Move Down/Right');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=downright';
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
sub moveConDownLeft
|
||||
{
|
||||
sub moveConDownLeft {
|
||||
my $self = shift;
|
||||
Debug( "Move Down/Left" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=downleft";
|
||||
Debug('Move Down/Left');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=downleft';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveMap
|
||||
{
|
||||
sub moveMap {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $xcoord = $self->getParam($params, 'xcoord');
|
||||
my $ycoord = $self->getParam($params, 'ycoord');
|
||||
Debug("Move Map to $xcoord,$ycoord");
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}."&imageheight=".$self->{Monitor}->{Height};
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}.'&imageheight='.$self->{Monitor}->{Height};
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelUp
|
||||
{
|
||||
sub moveRelUp {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'tiltstep');
|
||||
Debug("Step Up $step");
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=$step";
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?rtilt='.$step;
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelDown
|
||||
{
|
||||
sub moveRelDown {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'tiltstep');
|
||||
Debug("Step Down $step");
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=-$step";
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?rtilt=-'.$step;
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelLeft
|
||||
{
|
||||
sub moveRelLeft {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'panstep');
|
||||
Debug("Step Left $step");
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$step";
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?rpan=-'.$step;
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelRight
|
||||
{
|
||||
sub moveRelRight {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'panstep');
|
||||
Debug("Step Right $step");
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$step";
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?rpan='.$step;
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelUpRight
|
||||
{
|
||||
sub moveRelUpRight {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $panstep = $self->getParam($params, 'panstep');
|
||||
|
@ -225,8 +252,7 @@ sub moveRelUpRight
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelUpLeft
|
||||
{
|
||||
sub moveRelUpLeft {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $panstep = $self->getParam($params, 'panstep');
|
||||
|
@ -236,8 +262,7 @@ sub moveRelUpLeft
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelDownRight
|
||||
{
|
||||
sub moveRelDownRight {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $panstep = $self->getParam($params, 'panstep');
|
||||
|
@ -247,8 +272,7 @@ sub moveRelDownRight
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub moveRelDownLeft
|
||||
{
|
||||
sub moveRelDownLeft {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $panstep = $self->getParam($params, 'panstep');
|
||||
|
@ -258,100 +282,89 @@ sub moveRelDownLeft
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub zoomRelTele
|
||||
{
|
||||
sub zoomRelTele {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'step');
|
||||
Debug( "Zoom Tele" );
|
||||
Debug('Zoom Tele');
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step";
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub zoomRelWide
|
||||
{
|
||||
sub zoomRelWide {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'step');
|
||||
Debug( "Zoom Wide" );
|
||||
Debug('Zoom Wide');
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step";
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub focusRelNear
|
||||
{
|
||||
sub focusRelNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'step');
|
||||
Debug( "Focus Near" );
|
||||
Debug('Focus Near');
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step";
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub focusRelFar
|
||||
{
|
||||
sub focusRelFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'step');
|
||||
Debug( "Focus Far" );
|
||||
Debug('Focus Far');
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step";
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub focusAuto
|
||||
{
|
||||
sub focusAuto {
|
||||
my $self = shift;
|
||||
Debug( "Focus Auto" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=on";
|
||||
Debug('Focus Auto');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=on';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub focusMan
|
||||
{
|
||||
sub focusMan {
|
||||
my $self = shift;
|
||||
Debug( "Focus Manual" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=off";
|
||||
Debug('Focus Manual');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=off';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub irisRelOpen
|
||||
{
|
||||
sub irisRelOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'step');
|
||||
Debug( "Iris Open" );
|
||||
Debug('Iris Open');
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step";
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub irisRelClose
|
||||
{
|
||||
sub irisRelClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'step');
|
||||
Debug( "Iris Close" );
|
||||
Debug('Iris Close');
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step";
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub irisAuto
|
||||
{
|
||||
sub irisAuto {
|
||||
my $self = shift;
|
||||
Debug( "Iris Auto" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=on";
|
||||
Debug('Iris Auto');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=on';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub irisMan
|
||||
{
|
||||
sub irisMan {
|
||||
my $self = shift;
|
||||
Debug( "Iris Manual" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=off";
|
||||
Debug('Iris Manual');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=off';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub presetClear
|
||||
{
|
||||
sub presetClear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
|
@ -360,8 +373,7 @@ sub presetClear
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub presetSet
|
||||
{
|
||||
sub presetSet {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
|
@ -370,8 +382,7 @@ sub presetSet
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub presetGoto
|
||||
{
|
||||
sub presetGoto {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
|
@ -380,11 +391,10 @@ sub presetGoto
|
|||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub presetHome
|
||||
{
|
||||
sub presetHome {
|
||||
my $self = shift;
|
||||
Debug( "Home Preset" );
|
||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=home";
|
||||
Debug('Home Preset');
|
||||
my $cmd = '/axis-cgi/com/ptz.cgi?move=home';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
# =========================================================================r
|
||||
#
|
||||
# ZoneMinder D-Link DCS-5020L IP Control Protocol Module, $Date: $, $Revision: $
|
||||
# Copyright (C) 2013 Art Scheel
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# This module contains the implementation of the D-Link DCS-5020L IP camera control
|
||||
# protocol.
|
||||
#
|
||||
package ZoneMinder::Control::DCS5020L;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
|
||||
our @ISA = qw(ZoneMinder::Control);
|
||||
|
||||
our $VERSION = $ZoneMinder::Base::VERSION;
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# D-Link DCS-5020L Control Protocol
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/" . ZoneMinder::Base::ZM_VERSION );
|
||||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $cgi = shift;
|
||||
|
||||
my $result = undef;
|
||||
|
||||
printMsg( $cmd, "Tx" );
|
||||
|
||||
my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" );
|
||||
$req->content($cmd);
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success )
|
||||
{
|
||||
$result = !undef;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Error check failed: '".$res->status_line()."'" );
|
||||
}
|
||||
|
||||
return( $result );
|
||||
}
|
||||
|
||||
sub move
|
||||
{
|
||||
my $self = shift;
|
||||
my $dir = shift;
|
||||
my $panStep = shift;
|
||||
my $tiltStep = shift;
|
||||
my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir";
|
||||
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||
}
|
||||
|
||||
sub moveRel
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $panStep = $self->getParam($params, 'panstep', 0);
|
||||
my $tiltStep = $self->getParam($params, 'tiltstep', 0);
|
||||
my $dir = shift;
|
||||
$self->move( $dir, $panStep, $tiltStep );
|
||||
}
|
||||
|
||||
sub moveRelUpLeft
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 0 );
|
||||
}
|
||||
|
||||
sub moveRelUp
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 1 );
|
||||
}
|
||||
|
||||
sub moveRelUpRight
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 2 );
|
||||
}
|
||||
|
||||
sub moveRelLeft
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 3 );
|
||||
}
|
||||
|
||||
sub moveRelRight
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 5 );
|
||||
}
|
||||
|
||||
sub moveRelDownLeft
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 6 );
|
||||
}
|
||||
|
||||
sub moveRelDown
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 7 );
|
||||
}
|
||||
|
||||
sub moveRelDownRight
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->moveRel( $params, 8 );
|
||||
}
|
||||
|
||||
# moves the camera to center on the point that the user clicked on in the video image.
|
||||
# This isn't extremely accurate but good enough for most purposes
|
||||
sub moveMap
|
||||
{
|
||||
# if the camera moves too much or too little, try increasing or decreasing this value
|
||||
my $f = 11;
|
||||
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $xcoord = $self->getParam( $params, 'xcoord' );
|
||||
my $ycoord = $self->getParam( $params, 'ycoord' );
|
||||
|
||||
my $hor = $xcoord * 100 / $self->{Monitor}->{Width};
|
||||
my $ver = $ycoord * 100 / $self->{Monitor}->{Height};
|
||||
|
||||
my $direction;
|
||||
my $horSteps;
|
||||
my $verSteps;
|
||||
if ($hor < 50 && $ver < 50) {
|
||||
# up left
|
||||
$horSteps = (50 - $hor) / $f;
|
||||
$verSteps = (50 - $ver) / $f;
|
||||
$direction = 0;
|
||||
} elsif ($hor >= 50 && $ver < 50) {
|
||||
# up right
|
||||
$horSteps = ($hor - 50) / $f;
|
||||
$verSteps = (50 - $ver) / $f;
|
||||
$direction = 2;
|
||||
} elsif ($hor < 50 && $ver >= 50) {
|
||||
# down left
|
||||
$horSteps = (50 - $hor) / $f;
|
||||
$verSteps = ($ver - 50) / $f;
|
||||
$direction = 6;
|
||||
} elsif ($hor >= 50 && $ver >= 50) {
|
||||
# down right
|
||||
$horSteps = ($hor - 50) / $f;
|
||||
$verSteps = ($ver - 50) / $f;
|
||||
$direction = 8;
|
||||
}
|
||||
my $v = int($verSteps + .5);
|
||||
my $h = int($horSteps + .5);
|
||||
Debug( "Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction" );
|
||||
$self->move( $direction, $h, $v );
|
||||
}
|
||||
|
||||
sub presetClear
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
Debug( "Clear Preset $preset" );
|
||||
my $cmd = "ClearPosition=$preset";
|
||||
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||
}
|
||||
|
||||
sub presetSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
Debug( "Set Preset $preset" );
|
||||
my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset";
|
||||
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||
}
|
||||
|
||||
sub presetGoto
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
Debug( "Goto Preset $preset" );
|
||||
my $cmd = "PanTiltPresetPositionMove=$preset";
|
||||
$self->sendCmd( $cmd, 'pantiltcontrol' );
|
||||
}
|
||||
|
||||
sub presetHome
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Home Preset" );
|
||||
$self->move( 4, 0, 0 );
|
||||
}
|
||||
|
||||
|
||||
# IR Controls
|
||||
#
|
||||
# wake = IR on
|
||||
# sleep = IR off
|
||||
# reset = IR auto
|
||||
|
||||
sub setDayNightMode {
|
||||
my $self = shift;
|
||||
my $mode = shift;
|
||||
my $cmd = "DayNightMode=$mode&ConfigReboot=No";
|
||||
$self->sendCmd( $cmd, 'daynight' );
|
||||
}
|
||||
|
||||
sub wake
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Wake - IR on" );
|
||||
$self->setDayNightMode(2);
|
||||
}
|
||||
|
||||
sub sleep
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Sleep - IR off" );
|
||||
$self->setDayNightMode(3);
|
||||
}
|
||||
|
||||
sub reset
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Reset - IR auto" );
|
||||
$self->setDayNightMode(0);
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
# Below is stub documentation for your module. You'd better edit it!
|
||||
|
||||
=head1 NAME
|
||||
|
||||
ZoneMinder::Database - Perl extension for DCS-5020L
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use ZoneMinder::Database;
|
||||
DLINK DCS-5020L
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
ZoneMinder driver for the D-Link consumer camera DCS-5020L.
|
||||
|
||||
=head2 EXPORT
|
||||
|
||||
None by default.
|
||||
|
||||
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
See if there are better instructions for the DCS-5020L at
|
||||
http://www.zoneminder.com/wiki/index.php/Dlink
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Art Scheel <lt>ascheel (at) gmail<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
LGPLv3
|
||||
|
||||
=cut
|
|
@ -95,9 +95,9 @@ sub PutCmd {
|
|||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $content = shift;
|
||||
my $req = HTTP::Request->new(PUT => "$self->{BaseURL}/$cmd");
|
||||
my $req = HTTP::Request->new(PUT => $self->{BaseURL}.'/'.$cmd);
|
||||
if ( defined($content) ) {
|
||||
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
|
||||
$req->content_type('application/x-www-form-urlencoded; charset=UTF-8');
|
||||
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||
}
|
||||
my $res = $self->{UA}->request($req);
|
||||
|
@ -135,13 +135,13 @@ sub PutCmd {
|
|||
# Check for username/password
|
||||
#
|
||||
if ( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
||||
Info("Check username/password is correct");
|
||||
Info('Check username/password is correct');
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
||||
Info("No password in Control Address. Should there be one?");
|
||||
Info('No password in Control Address. Should there be one?');
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
||||
Info("Password but no username in Control Address.");
|
||||
Info('Password but no username in Control Address.');
|
||||
} else {
|
||||
Info("Missing username and password in Control Address.");
|
||||
Info('Missing username and password in Control Address.');
|
||||
}
|
||||
Fatal($res->status_line);
|
||||
}
|
||||
|
@ -382,7 +382,7 @@ sub irisRelOpen {
|
|||
sub reset {
|
||||
my $self = shift;
|
||||
|
||||
$self->PutCmd("ISAPI/System/reboot");
|
||||
$self->PutCmd('ISAPI/System/reboot');
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module, $Date: 2008-09-13 17:30:29 +0000 (Sat, 13 Sept 2008) $, $Revision: 2229 $
|
||||
# ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module
|
||||
# Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@ -43,8 +43,6 @@ our @ISA = qw(ZoneMinder::Control);
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
sub open {
|
||||
my $self = shift;
|
||||
|
||||
|
@ -52,29 +50,21 @@ sub open {
|
|||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||
|
||||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub printMsg {
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendCmd {
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
|
||||
my $result = undef;
|
||||
|
||||
printMsg( $cmd, "Tx" );
|
||||
$self->printMsg($cmd, 'Tx');
|
||||
|
||||
my $url;
|
||||
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
|
||||
if ( $self->{Monitor}->{ControlAddress} =~ /^http/i ) {
|
||||
$url = $self->{Monitor}->{ControlAddress}.$cmd;
|
||||
} else {
|
||||
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
|
||||
|
@ -86,16 +76,16 @@ sub sendCmd {
|
|||
if ( $res->is_success ) {
|
||||
$result = !undef;
|
||||
} else {
|
||||
Error( "Error check failed: '".$res->status_line()."'" );
|
||||
Error('Error check failed: \''.$res->status_line().'\'');
|
||||
}
|
||||
|
||||
return( $result );
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub reset {
|
||||
my $self = shift;
|
||||
Debug( "Camera Reset" );
|
||||
my $cmd = "/admin/ptctl.cgi?move=reset";
|
||||
Debug('Camera Reset');
|
||||
my $cmd = '/admin/ptctl.cgi?move=reset';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
|
@ -125,19 +115,19 @@ sub moveMap {
|
|||
elsif ( $hor > 50 ) {
|
||||
# right
|
||||
$horSteps = (($hor - 50) / 50) * $maxhor;
|
||||
$horDir = "right";
|
||||
$horDir = 'right';
|
||||
}
|
||||
|
||||
# Vertical movement
|
||||
if ( $ver < 50 ) {
|
||||
# up
|
||||
$verSteps = ((50 - $ver) / 50) * $maxver;
|
||||
$verDir = "up";
|
||||
$verDir = 'up';
|
||||
}
|
||||
elsif ( $ver > 50 ) {
|
||||
# down
|
||||
$verSteps = (($ver - 50) / 50) * $maxver;
|
||||
$verDir = "down";
|
||||
$verDir = 'down';
|
||||
}
|
||||
|
||||
my $v = int($verSteps);
|
||||
|
@ -155,7 +145,7 @@ sub moveRelUp {
|
|||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'tiltstep');
|
||||
Debug("Step Up $step");
|
||||
my $cmd = "/admin/ptctl.cgi?move=up";
|
||||
my $cmd = '/admin/ptctl.cgi?move=up';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
|
@ -164,7 +154,7 @@ sub moveRelDown {
|
|||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'tiltstep');
|
||||
Debug("Step Down $step");
|
||||
my $cmd = "/admin/ptctl.cgi?move=down";
|
||||
my $cmd = '/admin/ptctl.cgi?move=down';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
|
@ -173,12 +163,12 @@ sub moveRelLeft {
|
|||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'panstep');
|
||||
|
||||
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
|
||||
Debug( "Stepping Right because flipped horizontally " );
|
||||
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
|
||||
if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) {
|
||||
Debug('Stepping Right because flipped horizontally');
|
||||
$self->sendCmd('/admin/ptctl.cgi?move=right');
|
||||
} else {
|
||||
Debug( "Step Left" );
|
||||
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
|
||||
Debug('Step Left');
|
||||
$self->sendCmd('/admin/ptctl.cgi?move=left');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,12 +176,12 @@ sub moveRelRight {
|
|||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $step = $self->getParam($params, 'panstep');
|
||||
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
|
||||
Debug( "Stepping Left because flipped horizontally " );
|
||||
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
|
||||
if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) {
|
||||
Debug('Stepping Left because flipped horizontally');
|
||||
$self->sendCmd('/admin/ptctl.cgi?move=left');
|
||||
} else {
|
||||
Debug( "Step Right" );
|
||||
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
|
||||
Debug('Step Right');
|
||||
$self->sendCmd('/admin/ptctl.cgi?move=right');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +199,7 @@ sub presetSet {
|
|||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
Debug("Set Preset $preset");
|
||||
my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset";
|
||||
my $cmd = '/admin/ptctl.cgi?position=' . ($preset - 1) . "&positionname=zm$preset";
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
|
@ -218,14 +208,14 @@ sub presetGoto {
|
|||
my $params = shift;
|
||||
my $preset = $self->getParam($params, 'preset');
|
||||
Debug("Goto Preset $preset");
|
||||
my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1);
|
||||
my $cmd = '/admin/ptctl.cgi?move=p'.($preset - 1);
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
sub presetHome {
|
||||
my $self = shift;
|
||||
Debug( "Home Preset" );
|
||||
my $cmd = "/admin/ptctl.cgi?move=h";
|
||||
Debug('Home Preset');
|
||||
my $cmd = '/admin/ptctl.cgi?move=h';
|
||||
$self->sendCmd($cmd);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Filter Module, $Date$, $Revision$
|
||||
# ZoneMinder Filter Module
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@ -162,7 +162,9 @@ sub Sql {
|
|||
my $value = $term->{val};
|
||||
my @value_list;
|
||||
if ( $term->{attr} ) {
|
||||
if ( $term->{attr} =~ /^Monitor/ ) {
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$term->{op} = 'EXISTS';
|
||||
} elsif ( $term->{attr} =~ /^Monitor/ ) {
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
|
@ -214,7 +216,10 @@ sub Sql {
|
|||
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
||||
if ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')';
|
||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} =~ /ServerId/) {
|
||||
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
|
||||
|
@ -256,6 +261,8 @@ sub Sql {
|
|||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
|
||||
$value = 'to_days('.$temp_value.')';
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
|
@ -294,6 +301,8 @@ sub Sql {
|
|||
} else {
|
||||
$self->{Sql} .= " IS $value";
|
||||
}
|
||||
} elsif ( $term->{op} eq 'EXISTS' ) {
|
||||
$self->{Sql} .= " EXISTS $value";
|
||||
} elsif ( $term->{op} eq 'IS NOT' ) {
|
||||
$self->{Sql} .= " IS NOT $value";
|
||||
} elsif ( $term->{op} eq '=[]' ) {
|
||||
|
|
|
@ -503,11 +503,14 @@ sub openFile {
|
|||
$LOGFILE->autoflush() if $this->{autoFlush};
|
||||
|
||||
my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2];
|
||||
Error("Can't get uid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webUid;
|
||||
my $webGid = (getgrnam($ZoneMinder::Config::Config{ZM_WEB_GROUP}))[2];
|
||||
Error("Can't get gid for $ZoneMinder::Config::Config{ZM_WEB_USER}") if ! defined $webGid;
|
||||
if ( $> == 0 ) {
|
||||
chown( $webUid, $webGid, $this->{logFile} )
|
||||
or Fatal("Can't change permissions on log file $$this{logFile}: $!");
|
||||
}
|
||||
# If we are root, we want to make sure that www-data or whatever owns the file
|
||||
chown($webUid, $webGid, $this->{logFile} ) or
|
||||
Error("Can't change permissions on log file $$this{logFile}: $!");
|
||||
} # end if are root
|
||||
} else {
|
||||
$this->fileLevel(NOLOG);
|
||||
$this->termLevel(INFO);
|
||||
|
|
|
@ -36,9 +36,172 @@ require ZoneMinder::Server;
|
|||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
use vars qw/ $table $primary_key /;
|
||||
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||
$table = 'Monitors';
|
||||
$primary_key = 'Id';
|
||||
$serial = $primary_key = 'Id';
|
||||
%fields = map { $_ => $_ } qw(
|
||||
Id
|
||||
Name
|
||||
Notes
|
||||
ServerId
|
||||
StorageId
|
||||
Type
|
||||
Function
|
||||
Enabled
|
||||
LinkedMonitors
|
||||
Triggers
|
||||
Device
|
||||
Channel
|
||||
Format
|
||||
V4LMultiBuffer
|
||||
V4LCapturesPerFrame
|
||||
Protocol
|
||||
Method
|
||||
Host
|
||||
Port
|
||||
SubPath
|
||||
Path
|
||||
Options
|
||||
User
|
||||
Pass
|
||||
Width
|
||||
Height
|
||||
Colours
|
||||
Palette
|
||||
Orientation
|
||||
Deinterlacing
|
||||
DecoderHWAccelName
|
||||
DecoderHWAccelDevice
|
||||
SaveJPEGs
|
||||
VideoWriter
|
||||
OutputCodec
|
||||
OutputContainer
|
||||
EncoderParameters
|
||||
RecordAudio
|
||||
RTSPDescribe
|
||||
Brightness
|
||||
Contrast
|
||||
Hue
|
||||
Colour
|
||||
EventPrefix
|
||||
LabelFormat
|
||||
LabelX
|
||||
LabelY
|
||||
LabelSize
|
||||
ImageBufferCount
|
||||
WarmupCount
|
||||
PreEventCount
|
||||
PostEventCount
|
||||
StreamReplayBuffer
|
||||
AlarmFrameCount
|
||||
SectionLength
|
||||
MinSectionLength
|
||||
FrameSkip
|
||||
MotionFrameSkip
|
||||
AnalysisFPSLimit
|
||||
AnalysisUpdateDelay
|
||||
MaxFPS
|
||||
AlarmMaxFPS
|
||||
FPSReportInterval
|
||||
RefBlendPerc
|
||||
AlarmRefBlendPerc
|
||||
Controllable
|
||||
ControlId
|
||||
ControlDevice
|
||||
ControlAddress
|
||||
AutoStopTimeout
|
||||
TrackMotion
|
||||
TrackDelay
|
||||
ReturnLocation
|
||||
ReturnDelay
|
||||
DefaultRate
|
||||
DefaultScale
|
||||
SignalCheckPoints
|
||||
SignalCheckColour
|
||||
WebColour
|
||||
Exif
|
||||
Sequence
|
||||
);
|
||||
|
||||
%defaults = (
|
||||
ServerId => 0,
|
||||
StorageId => 0,
|
||||
Type => 'Ffmpeg',
|
||||
Function => 'Mocord',
|
||||
Enabled => 1,
|
||||
LinkedMonitors => undef,
|
||||
Device => '',
|
||||
Channel => 0,
|
||||
Format => 0,
|
||||
V4LMultiBuffer => undef,
|
||||
V4LCapturesPerFrame => 1,
|
||||
Protocol => undef,
|
||||
Method => '',
|
||||
Host => undef,
|
||||
Port => '',
|
||||
SubPath => '',
|
||||
Path => undef,
|
||||
Options => undef,
|
||||
User => undef,
|
||||
Pass => undef,
|
||||
Width => undef,
|
||||
Height => undef,
|
||||
Colours => 4,
|
||||
Palette => 0,
|
||||
Orientation => undef,
|
||||
Deinterlacing => 0,
|
||||
DecoderHWAccelName => undef,
|
||||
DecoderHWAccelDevice => undef,
|
||||
SaveJPEGs => 3,
|
||||
VideoWriter => 0,
|
||||
OutputCodec => undef,
|
||||
OutputContainer => undef,
|
||||
EncoderParameters => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||
RecordAudio=>0,
|
||||
RTSPDescribe=>0,
|
||||
Brightness => -1,
|
||||
Contrast => -1,
|
||||
Hue => -1,
|
||||
Colour => -1,
|
||||
EventPrefix => 'Event-',
|
||||
LabelFormat => '%N - %d/%m/%y %H:%M:%S',
|
||||
LabelX => 0,
|
||||
LabelY => 0,
|
||||
LabelSize => 1,
|
||||
ImageBufferCount => 20,
|
||||
WarmupCount => 0,
|
||||
PreEventCount => 5,
|
||||
PostEventCount => 5,
|
||||
StreamReplayBuffer => 0,
|
||||
AlarmFrameCount => 1,
|
||||
SectionLength => 600,
|
||||
MinSectionLength => 10,
|
||||
FrameSkip => 0,
|
||||
MotionFrameSkip => 0,
|
||||
AnalysisFPSLimit => undef,
|
||||
AnalysisUpdateDelay => 0,
|
||||
MaxFPS => undef,
|
||||
AlarmMaxFPS => undef,
|
||||
FPSReportInterval => 100,
|
||||
RefBlendPerc => 6,
|
||||
AlarmRefBlendPerc => 6,
|
||||
Controllable => 0,
|
||||
ControlId => undef,
|
||||
ControlDevice => undef,
|
||||
ControlAddress => undef,
|
||||
AutoStopTimeout => undef,
|
||||
TrackMotion => 0,
|
||||
TrackDelay => undef,
|
||||
ReturnLocation => -1,
|
||||
ReturnDelay => undef,
|
||||
DefaultRate => 100,
|
||||
DefaultScale => 100,
|
||||
SignalCheckPoints => 0,
|
||||
SignalCheckColour => '#0000BE',
|
||||
WebColour => '#ff0000',
|
||||
Exif => 0,
|
||||
Sequence => undef,
|
||||
);
|
||||
|
||||
sub Server {
|
||||
return new ZoneMinder::Server( $_[0]{ServerId} );
|
||||
|
|
|
@ -172,7 +172,7 @@ sub interpret_messages {
|
|||
# functions
|
||||
|
||||
sub discover {
|
||||
my ( $soap_version ) = @_;
|
||||
my ( $soap_version, $net_interface ) = @_;
|
||||
my @results;
|
||||
|
||||
## collect all responses
|
||||
|
@ -193,12 +193,17 @@ sub discover {
|
|||
my %services;
|
||||
|
||||
if ( $verbose ) {
|
||||
print "Probing for SOAP 1.1\n"
|
||||
print "Probing for SOAP 1.1\n";
|
||||
}
|
||||
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
||||
# no_dispatch => '1',
|
||||
});
|
||||
$svc_discover->set_soap_version('1.1');
|
||||
if ( $net_interface ) {
|
||||
my $transport = $svc_discover->get_transport();
|
||||
print "Setting net interface for $transport to $net_interface\n";
|
||||
$transport->set_net_interface($net_interface);
|
||||
}
|
||||
|
||||
my $uuid = $uuid_gen->create_str();
|
||||
|
||||
|
@ -222,12 +227,17 @@ sub discover {
|
|||
if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) {
|
||||
my %services;
|
||||
if ( $verbose ) {
|
||||
print "Probing for SOAP 1.2\n"
|
||||
print "Probing for SOAP 1.2\n";
|
||||
}
|
||||
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
||||
# no_dispatch => '1',
|
||||
});
|
||||
$svc_discover->set_soap_version('1.2');
|
||||
if ( $net_interface ) {
|
||||
my $transport = $svc_discover->get_transport();
|
||||
print "Setting net interface for $transport to $net_interface\n";
|
||||
$transport->set_net_interface($net_interface);
|
||||
}
|
||||
|
||||
# copies of the same Probe message must have the same MessageID.
|
||||
# This is not a copy. So we generate a new uuid.
|
||||
|
@ -250,20 +260,20 @@ sub discover {
|
|||
push @results, interpret_messages($svc_discover, \%services, @responses);
|
||||
} # end if doing soap 1.2
|
||||
return @results;
|
||||
}
|
||||
} # end sub discover
|
||||
|
||||
sub profiles {
|
||||
my ( $client ) = @_;
|
||||
|
||||
my $endpoint = $client->get_endpoint('media');
|
||||
if ( ! $endpoint ) {
|
||||
print "No media enpoint for client.\n";
|
||||
my $media = $client->get_endpoint('media');
|
||||
if ( ! $media ) {
|
||||
print "No media endpoint for client.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
my $result = $endpoint->GetProfiles( { } ,, );
|
||||
my $result = $media->GetProfiles( { } ,, );
|
||||
if ( ! $result ) {
|
||||
print "No result from GetProfiles\n";
|
||||
print "No result from GetProfiles.\n";
|
||||
return;
|
||||
}
|
||||
if ( $verbose ) {
|
||||
|
@ -275,23 +285,18 @@ sub profiles {
|
|||
foreach my $profile ( @{ $profiles } ) {
|
||||
|
||||
my $token = $profile->attr()->get_token() ;
|
||||
my $video_encoder_configuration = $profile->get_VideoEncoderConfiguration();
|
||||
if ( ! $video_encoder_configuration ) {
|
||||
print "Unknown profile $token " . $profile->get_Name()."\n";
|
||||
my $Name = $profile->get_Name();
|
||||
|
||||
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
|
||||
if ( ! $VideoEncoderConfiguration ) {
|
||||
print "Unknown profile $token $Name.\n";
|
||||
next;
|
||||
}
|
||||
print $token . ", " .
|
||||
$profile->get_Name() . ", " .
|
||||
$profile->get_VideoEncoderConfiguration()->get_Encoding() . ", " .
|
||||
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Width() . ", " .
|
||||
$profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Height() . ", " .
|
||||
$profile->get_VideoEncoderConfiguration()->get_RateControl()->get_FrameRateLimit() .
|
||||
", ";
|
||||
|
||||
# Specification gives conflicting values for unicast stream types, try both.
|
||||
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
||||
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) {
|
||||
$result = $client->get_endpoint('media')->GetStreamUri( {
|
||||
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) {
|
||||
my $StreamUri = $media->GetStreamUri( {
|
||||
StreamSetup => { # ONVIF::Media::Types::StreamSetup
|
||||
Stream => $streamtype, # StreamType
|
||||
Transport => { # ONVIF::Media::Types::Transport
|
||||
|
@ -299,21 +304,30 @@ sub profiles {
|
|||
},
|
||||
},
|
||||
ProfileToken => $token, # ReferenceToken
|
||||
} ,, );
|
||||
last if $result;
|
||||
}
|
||||
die $result if not $result;
|
||||
# print $result . "\n";
|
||||
} );
|
||||
next if ! ( $StreamUri and $StreamUri->can('get_MediaUri') );
|
||||
my $MediaUri = $StreamUri->get_MediaUri();
|
||||
next if ! $MediaUri;
|
||||
my $Uri = $MediaUri->get_Uri();
|
||||
next if ! $Uri;
|
||||
|
||||
print join(', ', $token,
|
||||
$Name,
|
||||
$VideoEncoderConfiguration->get_Encoding(),
|
||||
$VideoEncoderConfiguration->get_Resolution()->get_Width(),
|
||||
$VideoEncoderConfiguration->get_Resolution()->get_Height(),
|
||||
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
|
||||
$Uri,
|
||||
) . "\n";
|
||||
} # end foreach streamtype
|
||||
|
||||
print $result->get_MediaUri()->get_Uri() .
|
||||
"\n";
|
||||
} # end foreach profile
|
||||
|
||||
#
|
||||
# use message parser without schema validation ???
|
||||
#
|
||||
|
||||
}
|
||||
} # end sub profiles
|
||||
|
||||
sub move {
|
||||
my ($client, $dir) = @_;
|
||||
|
@ -326,13 +340,22 @@ sub move {
|
|||
|
||||
sub metadata {
|
||||
my ( $client ) = @_;
|
||||
my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, );
|
||||
die $result if not $result;
|
||||
print $result . "\n";
|
||||
my $media = $client->get_endpoint('media');
|
||||
die 'No media endpoint.' if !$media;
|
||||
|
||||
$result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, );
|
||||
die $result if not $result;
|
||||
my $result = $media->GetMetadataConfigurations( { } ,, );
|
||||
if ( ! $result ) {
|
||||
print "No MetaDataConfigurations\n" if $verbose;
|
||||
} else {
|
||||
print $result . "\n";
|
||||
}
|
||||
|
||||
$result = $media->GetVideoAnalyticsConfigurations( { } ,, );
|
||||
if ( ! $result ) {
|
||||
print "No VideoAnalyticsConfigurations\n" if $verbose;
|
||||
} else {
|
||||
print $result . "\n";
|
||||
}
|
||||
|
||||
# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, );
|
||||
# die $result if not $result;
|
||||
|
|
|
@ -420,7 +420,7 @@ MAIN: while( $loop ) {
|
|||
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
|
||||
{
|
||||
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
|
||||
Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . ' entries.' );
|
||||
Debug('glob("'.$monitor_dir.'/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned '.(scalar @event_dirs).' entries.');
|
||||
foreach my $event_dir ( @event_dirs ) {
|
||||
if ( ! -d $event_dir ) {
|
||||
Debug("$event_dir is not a dir. Skipping");
|
||||
|
@ -428,7 +428,7 @@ MAIN: while( $loop ) {
|
|||
}
|
||||
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/;
|
||||
if ( !$event_id ) {
|
||||
Debug("Unable to parse date/event_id from $event_dir");
|
||||
Debug('Unable to parse date/event_id from '.$event_dir);
|
||||
next;
|
||||
}
|
||||
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
|
||||
|
@ -438,6 +438,7 @@ MAIN: while( $loop ) {
|
|||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
$Event->Path();
|
||||
$Event->age();
|
||||
Debug("Have event $$Event{Id} at $$Event{Path}");
|
||||
$Event->StartTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path()))));
|
||||
} # end foreach event
|
||||
|
@ -466,13 +467,13 @@ MAIN: while( $loop ) {
|
|||
} # end foreach event
|
||||
chdir( $Storage->Path() );
|
||||
} # if USE_DEEP_STORAGE
|
||||
Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir" );
|
||||
Debug('Got '.int(keys(%$fs_events)).' filesystem events for monitor '.$monitor_dir);
|
||||
|
||||
delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir);
|
||||
} # end foreach monitor
|
||||
|
||||
if ( $cleaned ) {
|
||||
Debug("First stage cleaning done. Restarting.");
|
||||
Debug('First stage cleaning done. Restarting.');
|
||||
redo MAIN;
|
||||
}
|
||||
|
||||
|
@ -484,7 +485,7 @@ MAIN: while( $loop ) {
|
|||
next;
|
||||
}
|
||||
my @event_ids = keys %$fs_events;
|
||||
Debug('Have ' .scalar @event_ids . " events for monitor $monitor_id");
|
||||
Debug('Have ' .scalar @event_ids . ' events for monitor '.$monitor_id);
|
||||
|
||||
foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) {
|
||||
|
||||
|
@ -499,7 +500,7 @@ MAIN: while( $loop ) {
|
|||
}
|
||||
my $age = $Event->age();
|
||||
|
||||
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
|
||||
if ( $age and ($age > $Config{ZM_AUDIT_MIN_AGE}) ) {
|
||||
aud_print("Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old");
|
||||
if ( confirm() ) {
|
||||
$Event->delete_files();
|
||||
|
@ -586,7 +587,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
|||
} else {
|
||||
Debug("$$Event{Id} Not found at $path");
|
||||
}
|
||||
}
|
||||
} # end foreach Storage
|
||||
if ( $Event->Archived() ) {
|
||||
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
|
||||
next;
|
||||
|
@ -638,18 +639,13 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
|||
Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}");
|
||||
$Event->StorageId($$fs_events{$db_event}->StorageId());
|
||||
}
|
||||
if ( $$fs_events{$db_event}->StartTime() ne $Event->StartTime() ) {
|
||||
if ( ! $Event->StartTime() ) {
|
||||
Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}");
|
||||
if ( $$Event{Scheme} eq 'Deep' ) {
|
||||
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
||||
} else {
|
||||
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
||||
}
|
||||
$Event->save();
|
||||
}
|
||||
|
||||
$Event->save();
|
||||
}
|
||||
} # end if Event exists in db and not in filesystem
|
||||
} # end if ! in fs_events
|
||||
} # foreach db_event
|
||||
} # end foreach db_monitor
|
||||
|
@ -944,6 +940,10 @@ FROM `Frames` WHERE `EventId`=?';
|
|||
$eventcounts_day_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
|
||||
$eventcounts_week_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
|
||||
$eventcounts_month_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
|
||||
|
||||
my $storage_diskspace_sth = $dbh->prepare_cached('UPDATE Storage SET DiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE StorageId=Storage.Id)');
|
||||
$storage_diskspace_sth->execute() or Error("Can't execute: ".$storage_diskspace_sth->errstr());
|
||||
|
||||
sleep($Config{ZM_AUDIT_CHECK_INTERVAL}) if $continuous;
|
||||
};
|
||||
|
||||
|
|
|
@ -351,8 +351,14 @@ sub exportsql {
|
|||
}
|
||||
}
|
||||
|
||||
if ($ARGV[0]) {
|
||||
$command .= qq( --where="Name = '$ARGV[0]'");
|
||||
my $name = $ARGV[0];
|
||||
if ( $name ) {
|
||||
if ( $name =~ /^([A-Za-z0-9 ,.&()\/\-]+)$/ ) { # Allow alphanumeric and " ,.&()/-"
|
||||
$name = $1;
|
||||
$command .= qq( --where="Name = '$name'");
|
||||
} else {
|
||||
print "Invalid characters in Name\n";
|
||||
}
|
||||
}
|
||||
|
||||
$command .= " zm Controls MonitorPresets";
|
||||
|
|
|
@ -108,6 +108,9 @@ if ( $options{command} ) {
|
|||
Fatal("Unable to load control data for monitor $id");
|
||||
}
|
||||
my $protocol = $monitor->{Protocol};
|
||||
if ( !$protocol ) {
|
||||
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
||||
}
|
||||
|
||||
if ( -x $protocol ) {
|
||||
# Protocol is actually a script!
|
||||
|
|
|
@ -196,7 +196,7 @@ my $last_action = 0;
|
|||
while( !$zm_terminate ) {
|
||||
my $now = time;
|
||||
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
||||
Debug("Reloading filters");
|
||||
Debug('Reloading filters');
|
||||
$last_action = $now;
|
||||
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
||||
}
|
||||
|
@ -699,8 +699,10 @@ sub substituteTags {
|
|||
$text =~ s/%ESM%/$Event->{MaxScore}/g;
|
||||
|
||||
if ( $first_alarm_frame ) {
|
||||
$text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
|
||||
$text =~ s/%EPIM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
|
||||
$text =~ s/%EPF1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
|
||||
$text =~ s/%EPFM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
|
||||
$text =~ s/%EPI1%/$url?view=image&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
|
||||
$text =~ s/%EPIM%/$url?view=image&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
|
||||
if ( $attachments_ref ) {
|
||||
if ( $text =~ s/%EI1%//g ) {
|
||||
my $path = generateImage($Event, $first_alarm_frame);
|
||||
|
@ -748,13 +750,14 @@ sub substituteTags {
|
|||
}
|
||||
}
|
||||
|
||||
if ( $text =~ s/%EIMOD%//g ) {
|
||||
$text =~ s/%EIMOD%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=objdetect/g;
|
||||
if ( $text =~ s/%EIMOD%//g or $text =~ s/%EFMOD%//g ) {
|
||||
$text =~ s/%EFMOD%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=objdetect/g;
|
||||
$text =~ s/%EIMOD%/$url?view=image&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=objdetect/g;
|
||||
my $path = $Event->Path().'/objdetect.jpg';
|
||||
if ( -e $path ) {
|
||||
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
|
||||
} else {
|
||||
Warning('No image for EIMOD at ' . $path);
|
||||
Warning('No image for MOD at '.$path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -796,17 +799,17 @@ sub sendEmail {
|
|||
Error('No from email address defined, not sending email');
|
||||
return 0;
|
||||
}
|
||||
if ( ! $Config{ZM_EMAIL_ADDRESS} ) {
|
||||
if ( ! $$filter{EmailTo} ) {
|
||||
Error('No email address defined, not sending email');
|
||||
return 0;
|
||||
}
|
||||
|
||||
Info('Creating notification email');
|
||||
|
||||
my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event);
|
||||
my $subject = substituteTags($$filter{EmailSubject}, $filter, $Event);
|
||||
return 0 if !$subject;
|
||||
my @attachments;
|
||||
my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments);
|
||||
my $body = substituteTags($$filter{EmailBody}, $filter, $Event, \@attachments);
|
||||
return 0 if !$body;
|
||||
|
||||
Info("Sending notification email '$subject'");
|
||||
|
@ -816,7 +819,7 @@ sub sendEmail {
|
|||
### Create the multipart container
|
||||
my $mail = MIME::Lite->new (
|
||||
From => $Config{ZM_FROM_EMAIL},
|
||||
To => $Config{ZM_EMAIL_ADDRESS},
|
||||
To => $$filter{EmailTo},
|
||||
Subject => $subject,
|
||||
Type => 'multipart/mixed'
|
||||
);
|
||||
|
@ -849,7 +852,7 @@ sub sendEmail {
|
|||
$mail->send();
|
||||
} else {
|
||||
### Send using SSMTP
|
||||
$mail->send('sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS});
|
||||
$mail->send('sendmail', $ssmtp_location, $$filter{EmailTo});
|
||||
}
|
||||
} else {
|
||||
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
|
||||
|
@ -858,9 +861,9 @@ sub sendEmail {
|
|||
} else {
|
||||
my $mail = MIME::Entity->build(
|
||||
From => $Config{ZM_FROM_EMAIL},
|
||||
To => $Config{ZM_EMAIL_ADDRESS},
|
||||
To => $$filter{EmailTo},
|
||||
Subject => $subject,
|
||||
Type => (($body=~/<html>/)?'text/html':'text/plain'),
|
||||
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||
Data => $body
|
||||
);
|
||||
|
||||
|
@ -962,7 +965,7 @@ sub sendMessage {
|
|||
From => $Config{ZM_FROM_EMAIL},
|
||||
To => $Config{ZM_MESSAGE_ADDRESS},
|
||||
Subject => $subject,
|
||||
Type => (($body=~/<html>/)?'text/html':'text/plain'),
|
||||
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||
Data => $body
|
||||
);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ my $OPTIONS = 'v';
|
|||
|
||||
sub HELP_MESSAGE {
|
||||
my ($fh, $pkg, $ver, $opts) = @_;
|
||||
print $fh "Usage: " . __FILE__ . " [-v] probe <soap version>\n";
|
||||
print $fh "Usage: " . __FILE__ . " [-v] probe <soap version> <network interface>\n";
|
||||
print $fh " " . __FILE__ . " [-v] <command> <device URI> <soap version> <user> <password>\n";
|
||||
print $fh <<EOF
|
||||
Commands are:
|
||||
|
@ -84,7 +84,8 @@ if ( defined $opt_v ) {
|
|||
|
||||
if ( $action eq 'probe' ) {
|
||||
my $soap_version = shift;
|
||||
ZoneMinder::ONVIF::discover($soap_version);
|
||||
my $net_interface = shift;
|
||||
ZoneMinder::ONVIF::discover($soap_version, $net_interface);
|
||||
} else {
|
||||
# all other actions need URI and credentials
|
||||
my $url_svc_device = shift @ARGV;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
use strict;
|
||||
use bytes;
|
||||
use utf8;
|
||||
|
||||
@EXTRA_PERL_LIB@
|
||||
use ZoneMinder;
|
||||
|
@ -34,6 +35,7 @@ use Sys::MemInfo qw(totalmem);
|
|||
use Sys::CPU qw(cpu_count);
|
||||
use POSIX qw(strftime uname);
|
||||
use JSON::MaybeXS;
|
||||
use Encode;
|
||||
|
||||
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||
|
@ -166,7 +168,7 @@ sub sendData {
|
|||
$req->header('content-length' => length($msg));
|
||||
$req->header('connection' => 'Close');
|
||||
|
||||
$req->content($msg);
|
||||
$req->content(encode('UTF-8',$msg));
|
||||
|
||||
my $resp = $ua->request($req);
|
||||
my $resp_msg = $resp->decoded_content;
|
||||
|
@ -196,7 +198,7 @@ sub getUUID {
|
|||
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
||||
$sth->finish();
|
||||
|
||||
$sql = q`UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'`;
|
||||
$sql = q`UPDATE Config SET Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'`;
|
||||
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
$res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() );
|
||||
$sth->finish();
|
||||
|
@ -232,9 +234,9 @@ sub countQuery {
|
|||
my $dbh = shift;
|
||||
my $table = shift;
|
||||
|
||||
my $sql = "SELECT count(*) FROM $table";
|
||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||
my $sql = "SELECT count(*) FROM `$table`";
|
||||
my $sth = $dbh->prepare_cached($sql) or die "Can't prepare '$sql': ".$dbh->errstr();
|
||||
my $res = $sth->execute() or die 'Can\'t execute: '.$sth->errstr();
|
||||
my $count = $sth->fetchrow_array();
|
||||
$sth->finish();
|
||||
|
||||
|
@ -245,7 +247,7 @@ sub countQuery {
|
|||
sub getMonitorRef {
|
||||
my $dbh = shift;
|
||||
|
||||
my $sql = 'SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors';
|
||||
my $sql = 'SELECT `Id`,`Name`,`Type`,`Function`,`Width`,`Height`,`Colours`,`MaxFPS`,`AlarmMaxFPS` FROM `Monitors`';
|
||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||
my $arrayref = $sth->fetchall_arrayref({});
|
||||
|
|
|
@ -329,14 +329,13 @@ sub loadMonitor {
|
|||
} # end sub loadMonitor
|
||||
|
||||
sub loadMonitors {
|
||||
Debug('Loading monitors');
|
||||
$monitor_reload_time = time();
|
||||
|
||||
my %new_monitors = ();
|
||||
|
||||
my $sql = q`SELECT * FROM Monitors
|
||||
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )`.
|
||||
( $Config{ZM_SERVER_ID} ? ' AND ServerId=?' : '' )
|
||||
my $sql = 'SELECT * FROM `Monitors`
|
||||
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect\' )'.
|
||||
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' )
|
||||
;
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
|
|
|
@ -93,12 +93,11 @@ if ( $version ) {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
die( 'No command given' ) unless( $command );
|
||||
die( 'No unit code given' )
|
||||
die 'No command given' unless $command;
|
||||
die 'No unit code given'
|
||||
unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) );
|
||||
|
||||
if ( $command eq 'start' )
|
||||
{
|
||||
if ( $command eq 'start' ) {
|
||||
X10Server::runServer();
|
||||
exit();
|
||||
}
|
||||
|
@ -108,14 +107,12 @@ socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
|
|||
|
||||
my $saddr = sockaddr_un(SOCK_FILE);
|
||||
|
||||
if ( !connect( CLIENT, $saddr ) )
|
||||
{
|
||||
if ( !connect(CLIENT, $saddr) ) {
|
||||
# The server isn't there
|
||||
print("Unable to connect, starting server\n");
|
||||
close(CLIENT);
|
||||
|
||||
if ( my $cpid = fork() )
|
||||
{
|
||||
if ( my $cpid = fork() ) {
|
||||
# Parent process just sleep and fall through
|
||||
sleep(2);
|
||||
logReinit();
|
||||
|
@ -123,28 +120,23 @@ if ( !connect( CLIENT, $saddr ) )
|
|||
or Fatal("Can't open socket: $!");
|
||||
connect(CLIENT, $saddr)
|
||||
or Fatal("Can't connect: $!");
|
||||
}
|
||||
elsif ( defined($cpid) )
|
||||
{
|
||||
} elsif ( defined($cpid) ) {
|
||||
setpgrp();
|
||||
|
||||
logReinit();
|
||||
X10Server::runServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Fatal("Can't fork: $!");
|
||||
}
|
||||
}
|
||||
# The server is there, connect to it
|
||||
#print( "Writing commands\n" );
|
||||
CLIENT->autoflush();
|
||||
my $message = "$command";
|
||||
$message .= ";$unit_code" if ( $unit_code );
|
||||
my $message = $command;
|
||||
$message .= ';'.$unit_code if $unit_code;
|
||||
print(CLIENT $message);
|
||||
shutdown(CLIENT, 1);
|
||||
while ( my $line = <CLIENT> )
|
||||
{
|
||||
while ( my $line = <CLIENT> ) {
|
||||
chomp($line);
|
||||
print("$line\n");
|
||||
}
|
||||
|
@ -178,9 +170,8 @@ our %monitor_hash;
|
|||
our %device_hash;
|
||||
our %pending_tasks;
|
||||
|
||||
sub runServer
|
||||
{
|
||||
Info( "X10 server starting\n" );
|
||||
sub runServer {
|
||||
Info('X10 server starting');
|
||||
|
||||
socket(SERVER, PF_UNIX, SOCK_STREAM, 0)
|
||||
or Fatal("Can't open socket: $!");
|
||||
|
@ -191,7 +182,11 @@ sub runServer
|
|||
|
||||
$dbh = zmDbConnect();
|
||||
|
||||
$x10 = new X10::ActiveHome( port=>$Config{ZM_X10_DEVICE}, house_code=>$Config{ZM_X10_HOUSE_CODE}, debug=>0 );
|
||||
$x10 = new X10::ActiveHome(
|
||||
port=>$Config{ZM_X10_DEVICE},
|
||||
house_code=>$Config{ZM_X10_HOUSE_CODE},
|
||||
debug=>0
|
||||
);
|
||||
|
||||
loadTasks();
|
||||
|
||||
|
@ -205,46 +200,38 @@ sub runServer
|
|||
my $reload = undef;
|
||||
my $reload_count = 0;
|
||||
my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout;
|
||||
while( 1 )
|
||||
{
|
||||
while( 1 ) {
|
||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||
#print( "Off select, NF:$nfound, ER:$!\n" );
|
||||
#print( vec( $rout, fileno(SERVER),1)."\n" );
|
||||
#print( vec( $rout, $x10->select_fds(),1)."\n" );
|
||||
if ( $nfound > 0 )
|
||||
{
|
||||
if ( vec( $rout, fileno(SERVER),1) )
|
||||
{
|
||||
if ( $nfound > 0 ) {
|
||||
if ( vec($rout, fileno(SERVER),1) ) {
|
||||
my $paddr = accept(CLIENT, SERVER);
|
||||
my $message = <CLIENT>;
|
||||
|
||||
my ( $command, $unit_code ) = split( /;/, $message );
|
||||
my ($command, $unit_code) = split(';', $message);
|
||||
|
||||
my $device;
|
||||
if ( defined($unit_code) )
|
||||
{
|
||||
if ( $unit_code < 1 || $unit_code > 16 )
|
||||
{
|
||||
if ( defined($unit_code) ) {
|
||||
if ( $unit_code < 1 || $unit_code > 16 ) {
|
||||
dPrint(ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n");
|
||||
next;
|
||||
}
|
||||
|
||||
$device = $device_hash{$unit_code};
|
||||
if ( !$device )
|
||||
{
|
||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
||||
if ( !$device ) {
|
||||
$device = $device_hash{$unit_code} = {
|
||||
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||
status=>'unknown'
|
||||
};
|
||||
}
|
||||
}
|
||||
} # end if defined($unit_code)
|
||||
|
||||
my $result;
|
||||
if ( $command eq 'on' )
|
||||
{
|
||||
if ( $command eq 'on' ) {
|
||||
$result = $device->{appliance}->on();
|
||||
}
|
||||
elsif ( $command eq 'off' )
|
||||
{
|
||||
} elsif ( $command eq 'off' ) {
|
||||
$result = $device->{appliance}->off();
|
||||
}
|
||||
#elsif ( $command eq 'dim' )
|
||||
|
@ -255,128 +242,97 @@ sub runServer
|
|||
#{
|
||||
#$result = $device->{appliance}->bright();
|
||||
#}
|
||||
elsif ( $command eq 'status' )
|
||||
{
|
||||
if ( $device )
|
||||
{
|
||||
elsif ( $command eq 'status' ) {
|
||||
if ( $device ) {
|
||||
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach my $unit_code ( sort( keys(%device_hash) ) )
|
||||
{
|
||||
} else {
|
||||
foreach my $unit_code ( sort( keys(%device_hash) ) ) {
|
||||
my $device = $device_hash{$unit_code};
|
||||
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ( $command eq 'shutdown' )
|
||||
{
|
||||
} elsif ( $command eq 'shutdown' ) {
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n");
|
||||
}
|
||||
if ( defined($result) )
|
||||
{
|
||||
if ( 1 || $result )
|
||||
{
|
||||
if ( defined($result) ) {
|
||||
# FIXME
|
||||
if ( 1 || $result ) {
|
||||
$device->{status} = uc($command);
|
||||
dPrint(ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n");
|
||||
#x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dPrint(ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n");
|
||||
}
|
||||
}
|
||||
} # end if defined result
|
||||
close(CLIENT);
|
||||
}
|
||||
elsif ( vec( $rout, $x10->select_fds(),1) )
|
||||
{
|
||||
} elsif ( vec($rout, $x10->select_fds(),1) ) {
|
||||
$x10->handle_input();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Fatal('Bogus descriptor');
|
||||
}
|
||||
}
|
||||
elsif ( $nfound < 0 )
|
||||
{
|
||||
if ( $! != EINTR )
|
||||
{
|
||||
} elsif ( $nfound < 0 ) {
|
||||
if ( $! != EINTR ) {
|
||||
Fatal("Can't select: $!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
#print( "Select timed out\n" );
|
||||
# Check for state changes
|
||||
foreach my $monitor_id ( sort(keys(%monitor_hash) ) )
|
||||
{
|
||||
foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) {
|
||||
my $monitor = $monitor_hash{$monitor_id};
|
||||
my $state = zmGetMonitorState($monitor);
|
||||
if ( !defined($state) )
|
||||
{
|
||||
if ( !defined($state) ) {
|
||||
$reload = !undef;
|
||||
next;
|
||||
}
|
||||
if ( defined( $monitor->{LastState} ) )
|
||||
{
|
||||
if ( defined( $monitor->{LastState} ) ) {
|
||||
my $task_list;
|
||||
if ( ($state == STATE_ALARM || $state == STATE_ALERT)
|
||||
&& ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE)
|
||||
) # Gone into alarm state
|
||||
{
|
||||
Debug( "Applying ON_list for $monitor_id\n" );
|
||||
$task_list = $monitor->{'ON_list'};
|
||||
}
|
||||
elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
|
||||
Debug("Applying ON_list for $monitor_id");
|
||||
$task_list = $monitor->{ON_list};
|
||||
} elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
|
||||
|| ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE)
|
||||
) # Come out of alarm state
|
||||
{
|
||||
Debug( "Applying OFF_list for $monitor_id\n" );
|
||||
$task_list = $monitor->{'OFF_list'};
|
||||
Debug("Applying OFF_list for $monitor_id");
|
||||
$task_list = $monitor->{OFF_list};
|
||||
}
|
||||
if ( $task_list )
|
||||
{
|
||||
foreach my $task ( @$task_list )
|
||||
{
|
||||
if ( $task_list ) {
|
||||
foreach my $task ( @$task_list ) {
|
||||
processTask($task);
|
||||
}
|
||||
}
|
||||
}
|
||||
} # end if defined laststate
|
||||
$monitor->{LastState} = $state;
|
||||
}
|
||||
} # end foreach monitor
|
||||
|
||||
# Check for pending tasks
|
||||
my $now = time();
|
||||
foreach my $activation_time ( sort(keys(%pending_tasks) ) )
|
||||
{
|
||||
foreach my $activation_time ( sort(keys(%pending_tasks) ) ) {
|
||||
last if ( $activation_time > $now );
|
||||
my $pending_list = $pending_tasks{$activation_time};
|
||||
foreach my $task ( @$pending_list )
|
||||
{
|
||||
foreach my $task ( @$pending_list ) {
|
||||
processTask($task);
|
||||
}
|
||||
delete( $pending_tasks{$activation_time} );
|
||||
delete $pending_tasks{$activation_time};
|
||||
}
|
||||
if ( $reload || ++$reload_count >= $reload_limit )
|
||||
{
|
||||
if ( $reload or (++$reload_count >= $reload_limit) ) {
|
||||
loadTasks();
|
||||
$reload = undef;
|
||||
$reload_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Info( "X10 server exiting\n" );
|
||||
Info("X10 server exiting");
|
||||
close(SERVER);
|
||||
exit();
|
||||
}
|
||||
|
||||
sub addToDeviceList
|
||||
{
|
||||
sub addToDeviceList {
|
||||
my $unit_code = shift;
|
||||
my $event = shift;
|
||||
my $monitor = shift;
|
||||
|
@ -384,36 +340,35 @@ sub addToDeviceList
|
|||
my $limit = shift;
|
||||
|
||||
Debug("Adding to device list, uc:$unit_code, ev:$event, mo:"
|
||||
.$monitor->{Id}.", fu:$function, li:$limit\n"
|
||||
.$monitor->{Id}.", fu:$function, li:$limit"
|
||||
);
|
||||
my $device = $device_hash{$unit_code};
|
||||
if ( !$device )
|
||||
{
|
||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
||||
if ( !$device ) {
|
||||
$device = $device_hash{$unit_code} = {
|
||||
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||
status=>'unknown'
|
||||
};
|
||||
}
|
||||
|
||||
my $task = { type=>'device',
|
||||
my $task = {
|
||||
type=>'device',
|
||||
monitor=>$monitor,
|
||||
address=>$device->{appliance}->address(),
|
||||
function=>$function
|
||||
};
|
||||
if ( $limit )
|
||||
{
|
||||
|
||||
if ( $limit ) {
|
||||
$task->{limit} = $limit
|
||||
}
|
||||
|
||||
my $task_list = $device->{$event.'_list'};
|
||||
if ( !$task_list )
|
||||
{
|
||||
if ( !$task_list ) {
|
||||
$task_list = $device->{$event.'_list'} = [];
|
||||
}
|
||||
push( @$task_list, $task );
|
||||
}
|
||||
push @$task_list, $task;
|
||||
} # end sub addToDeviceList
|
||||
|
||||
sub addToMonitorList
|
||||
{
|
||||
sub addToMonitorList {
|
||||
my $monitor = shift;
|
||||
my $event = shift;
|
||||
my $unit_code = shift;
|
||||
|
@ -421,59 +376,54 @@ sub addToMonitorList
|
|||
my $limit = shift;
|
||||
|
||||
Debug("Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}
|
||||
.", fu:$function, li:$limit\n"
|
||||
.", fu:$function, li:$limit"
|
||||
);
|
||||
my $device = $device_hash{$unit_code};
|
||||
if ( !$device )
|
||||
{
|
||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
||||
if ( !$device ) {
|
||||
$device = $device_hash{$unit_code} = {
|
||||
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||
status=>'unknown'
|
||||
};
|
||||
}
|
||||
|
||||
my $task = { type=>'monitor',
|
||||
my $task = {
|
||||
type=>'monitor',
|
||||
device=>$device,
|
||||
id=>$monitor->{Id},
|
||||
function=>$function
|
||||
};
|
||||
if ( $limit )
|
||||
{
|
||||
if ( $limit ) {
|
||||
$task->{limit} = $limit;
|
||||
}
|
||||
|
||||
my $task_list = $monitor->{$event.'_list'};
|
||||
if ( !$task_list )
|
||||
{
|
||||
if ( !$task_list ) {
|
||||
$task_list = $monitor->{$event.'_list'} = [];
|
||||
}
|
||||
push( @$task_list, $task );
|
||||
}
|
||||
push @$task_list, $task;
|
||||
} # end sub addToMonitorList
|
||||
|
||||
sub loadTasks
|
||||
{
|
||||
sub loadTasks {
|
||||
%monitor_hash = ();
|
||||
|
||||
Debug( "Loading tasks\n" );
|
||||
Debug('Loading tasks');
|
||||
# Clear out all old device task lists
|
||||
foreach my $unit_code ( sort( keys(%device_hash) ) )
|
||||
{
|
||||
foreach my $unit_code ( sort keys(%device_hash) ) {
|
||||
my $device = $device_hash{$unit_code};
|
||||
$device->{ON_list} = [];
|
||||
$device->{OFF_list} = [];
|
||||
}
|
||||
|
||||
my $sql = "SELECT M.*,T.* from Monitors as M
|
||||
my $sql = 'SELECT M.*,T.* FROM Monitors as M
|
||||
INNER JOIN TriggersX10 as T on (M.Id = T.MonitorId)
|
||||
WHERE find_in_set( M.Function, 'Modect,Record,Mocord,Nodect' )
|
||||
AND M.Enabled = 1
|
||||
AND find_IN_set( 'X10', M.Triggers )"
|
||||
;
|
||||
WHERE find_in_set(M.`Function`, \'Modect,Record,Mocord,Nodect\')
|
||||
AND M.`Enabled` = 1
|
||||
AND find_IN_set(\'X10\', M.Triggers)';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute()
|
||||
or Fatal("Can't execute: ".$sth->errstr());
|
||||
while( my $monitor = $sth->fetchrow_hashref() )
|
||||
{
|
||||
while( my $monitor = $sth->fetchrow_hashref() ) {
|
||||
# Check shared memory ok
|
||||
if ( !zmMemVerify($monitor) ) {
|
||||
zmMemInvalidate($monitor);
|
||||
|
@ -482,301 +432,235 @@ sub loadTasks
|
|||
|
||||
$monitor_hash{$monitor->{Id}} = $monitor;
|
||||
|
||||
if ( $monitor->{Activation} )
|
||||
{
|
||||
Debug( "$monitor->{Name} has active string '$monitor->{Activation}'\n" );
|
||||
foreach my $code_string ( split( /,/, $monitor->{Activation} ) )
|
||||
{
|
||||
if ( $monitor->{Activation} ) {
|
||||
Debug("$monitor->{Name} has active string '$monitor->{Activation}'");
|
||||
foreach my $code_string ( split(',', $monitor->{Activation}) ) {
|
||||
#Debug( "Code string: $code_string\n" );
|
||||
my ( $invert, $unit_code, $modifier, $limit )
|
||||
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
||||
$limit = 0 if ( !$limit );
|
||||
if ( $unit_code )
|
||||
{
|
||||
if ( !$modifier || $modifier eq '+' )
|
||||
{
|
||||
$limit = 0 if !$limit;
|
||||
if ( $unit_code ) {
|
||||
if ( !$modifier || $modifier eq '+' ) {
|
||||
addToDeviceList( $unit_code,
|
||||
'ON',
|
||||
$monitor,
|
||||
!$invert ? 'start_active'
|
||||
: 'stop_active',
|
||||
(!$invert ? 'start_active' : 'stop_active'),
|
||||
$limit
|
||||
);
|
||||
}
|
||||
if ( !$modifier || $modifier eq '-' )
|
||||
{
|
||||
if ( !$modifier || $modifier eq '-' ) {
|
||||
addToDeviceList( $unit_code,
|
||||
'OFF',
|
||||
$monitor,
|
||||
!$invert ? 'stop_active'
|
||||
: 'start_active',
|
||||
(!$invert ? 'stop_active' : 'start_active'),
|
||||
$limit
|
||||
);
|
||||
}
|
||||
} # end if unit_code
|
||||
} # end foreach code_string
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $monitor->{AlarmInput} )
|
||||
{
|
||||
Debug( "$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'\n" );
|
||||
foreach my $code_string ( split( /,/, $monitor->{AlarmInput} ) )
|
||||
{
|
||||
if ( $monitor->{AlarmInput} ) {
|
||||
Debug("$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'");
|
||||
foreach my $code_string ( split(',', $monitor->{AlarmInput}) ) {
|
||||
#Debug( "Code string: $code_string\n" );
|
||||
my ( $invert, $unit_code, $modifier, $limit )
|
||||
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
||||
$limit = 0 if ( !$limit );
|
||||
if ( $unit_code )
|
||||
{
|
||||
if ( !$modifier || $modifier eq '+' )
|
||||
{
|
||||
$limit = 0 if !$limit;
|
||||
if ( $unit_code ) {
|
||||
if ( !$modifier || $modifier eq '+' ) {
|
||||
addToDeviceList( $unit_code,
|
||||
'ON',
|
||||
$monitor,
|
||||
!$invert ? 'start_alarm'
|
||||
: 'stop_alarm',
|
||||
(!$invert ? 'start_alarm' : 'stop_alarm'),
|
||||
$limit
|
||||
);
|
||||
}
|
||||
if ( !$modifier || $modifier eq '-' )
|
||||
{
|
||||
if ( !$modifier || $modifier eq '-' ) {
|
||||
addToDeviceList( $unit_code,
|
||||
'OFF',
|
||||
$monitor,
|
||||
!$invert ? 'stop_alarm'
|
||||
: 'start_alarm',
|
||||
(!$invert ? 'stop_alarm' : 'start_alarm'),
|
||||
$limit
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $monitor->{AlarmOutput} )
|
||||
{
|
||||
Debug( "$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'\n" );
|
||||
foreach my $code_string ( split( /,/, $monitor->{AlarmOutput} ) )
|
||||
{
|
||||
} # end if unit_code
|
||||
} # end foreach code_string
|
||||
} # end if AlarmInput
|
||||
if ( $monitor->{AlarmOutput} ) {
|
||||
Debug("$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'");
|
||||
foreach my $code_string ( split( ',', $monitor->{AlarmOutput} ) ) {
|
||||
#Debug( "Code string: $code_string\n" );
|
||||
my ( $invert, $unit_code, $modifier, $limit )
|
||||
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
||||
$limit = 0 if ( !$limit );
|
||||
if ( $unit_code )
|
||||
{
|
||||
if ( !$modifier || $modifier eq '+' )
|
||||
{
|
||||
$limit = 0 if !$limit;
|
||||
if ( $unit_code ) {
|
||||
if ( !$modifier || $modifier eq '+' ) {
|
||||
addToMonitorList( $monitor,
|
||||
'ON',
|
||||
$unit_code,
|
||||
!$invert ? 'on'
|
||||
: 'off',
|
||||
(!$invert ? 'on' : 'off'),
|
||||
$limit
|
||||
);
|
||||
}
|
||||
if ( !$modifier || $modifier eq '-' )
|
||||
{
|
||||
if ( !$modifier || $modifier eq '-' ) {
|
||||
addToMonitorList( $monitor,
|
||||
'OFF',
|
||||
$unit_code,
|
||||
!$invert ? 'off'
|
||||
: 'on',
|
||||
(!$invert ? 'off' : 'on'),
|
||||
$limit
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} # end if unit_code
|
||||
} # end foreach code_string
|
||||
} # end if AlarmOutput
|
||||
zmMemInvalidate($monitor);
|
||||
}
|
||||
}
|
||||
} # end sub loadTasks
|
||||
|
||||
sub addPendingTask
|
||||
{
|
||||
sub addPendingTask {
|
||||
my $task = shift;
|
||||
|
||||
# Check whether we are just extending a previous pending task
|
||||
# and remove it if it's there
|
||||
foreach my $activation_time ( sort(keys(%pending_tasks) ) )
|
||||
{
|
||||
foreach my $activation_time ( sort keys(%pending_tasks) ) {
|
||||
my $pending_list = $pending_tasks{$activation_time};
|
||||
my $new_pending_list = [];
|
||||
foreach my $pending_task ( @$pending_list )
|
||||
{
|
||||
if ( $task->{type} ne $pending_task->{type} )
|
||||
{
|
||||
foreach my $pending_task ( @$pending_list ) {
|
||||
if ( $task->{type} ne $pending_task->{type} ) {
|
||||
push( @$new_pending_list, $pending_task )
|
||||
}
|
||||
elsif ( $task->{type} eq 'device' )
|
||||
{
|
||||
} elsif ( $task->{type} eq 'device' ) {
|
||||
if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} )
|
||||
|| ( $task->{function} ne $pending_task->{function} ))
|
||||
{
|
||||
push( @$new_pending_list, $pending_task )
|
||||
push @$new_pending_list, $pending_task;
|
||||
}
|
||||
}
|
||||
elsif ( $task->{type} eq 'monitor' )
|
||||
{
|
||||
} elsif ( $task->{type} eq 'monitor' ) {
|
||||
if (( $task->{device}->{appliance}->unit_code()
|
||||
!= $pending_task->{device}->{appliance}->unit_code()
|
||||
)
|
||||
|| ( $task->{function} ne $pending_task->{function} )
|
||||
)
|
||||
{
|
||||
push( @$new_pending_list, $pending_task )
|
||||
) {
|
||||
push @$new_pending_list, $pending_task;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( @$new_pending_list )
|
||||
{
|
||||
} # end switch task->type
|
||||
} # end foreach pending_task
|
||||
|
||||
if ( @$new_pending_list ) {
|
||||
$pending_tasks{$activation_time} = $new_pending_list;
|
||||
} else {
|
||||
delete $pending_tasks{$activation_time};
|
||||
}
|
||||
else
|
||||
{
|
||||
delete( $pending_tasks{$activation_time} );
|
||||
}
|
||||
}
|
||||
} # end foreach activation_time
|
||||
|
||||
my $end_time = time() + $task->{limit};
|
||||
my $pending_list = $pending_tasks{$end_time};
|
||||
if ( !$pending_list )
|
||||
{
|
||||
if ( !$pending_list ) {
|
||||
$pending_list = $pending_tasks{$end_time} = [];
|
||||
}
|
||||
my $pending_task;
|
||||
if ( $task->{type} eq 'device' )
|
||||
{
|
||||
$pending_task = { type=>$task->{type},
|
||||
if ( $task->{type} eq 'device' ) {
|
||||
$pending_task = {
|
||||
type=>$task->{type},
|
||||
monitor=>$task->{monitor},
|
||||
function=>$task->{function}
|
||||
};
|
||||
$pending_task->{function} =~ s/start/stop/;
|
||||
}
|
||||
elsif ( $task->{type} eq 'monitor' )
|
||||
{
|
||||
$pending_task = { type=>$task->{type},
|
||||
} elsif ( $task->{type} eq 'monitor' ) {
|
||||
$pending_task = {
|
||||
type=>$task->{type},
|
||||
device=>$task->{device},
|
||||
function=>$task->{function}
|
||||
};
|
||||
$pending_task->{function} =~ s/on/off/;
|
||||
}
|
||||
push( @$pending_list, $pending_task );
|
||||
}
|
||||
push @$pending_list, $pending_task;
|
||||
} # end sub addPendingTask
|
||||
|
||||
sub processTask
|
||||
{
|
||||
sub processTask {
|
||||
my $task = shift;
|
||||
|
||||
if ( $task->{type} eq 'device' )
|
||||
{
|
||||
if ( $task->{type} eq 'device' ) {
|
||||
my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ );
|
||||
|
||||
if ( $class eq 'active' )
|
||||
{
|
||||
if ( $instruction eq 'start' )
|
||||
{
|
||||
if ( $class eq 'active' ) {
|
||||
if ( $instruction eq 'start' ) {
|
||||
zmMonitorEnable($task->{monitor});
|
||||
if ( $task->{limit} )
|
||||
{
|
||||
if ( $task->{limit} ) {
|
||||
addPendingTask($task);
|
||||
}
|
||||
}
|
||||
elsif( $instruction eq 'stop' )
|
||||
{
|
||||
} elsif( $instruction eq 'stop' ) {
|
||||
zmMonitorDisable($task->{monitor});
|
||||
}
|
||||
}
|
||||
elsif( $class eq 'alarm' )
|
||||
{
|
||||
if ( $instruction eq 'start' )
|
||||
{
|
||||
zmTriggerEventOn( $task->{monitor},
|
||||
} elsif( $class eq 'alarm' ) {
|
||||
if ( $instruction eq 'start' ) {
|
||||
zmTriggerEventOn(
|
||||
$task->{monitor},
|
||||
0,
|
||||
main::CAUSE_STRING,
|
||||
$task->{address}
|
||||
);
|
||||
if ( $task->{limit} )
|
||||
{
|
||||
if ( $task->{limit} ) {
|
||||
addPendingTask($task);
|
||||
}
|
||||
}
|
||||
elsif( $instruction eq 'stop' )
|
||||
{
|
||||
} elsif( $instruction eq 'stop' ) {
|
||||
zmTriggerEventCancel($task->{monitor});
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif( $task->{type} eq 'monitor' )
|
||||
{
|
||||
if ( $task->{function} eq 'on' )
|
||||
{
|
||||
} # end switch class
|
||||
} elsif( $task->{type} eq 'monitor' ) {
|
||||
if ( $task->{function} eq 'on' ) {
|
||||
$task->{device}->{appliance}->on();
|
||||
if ( $task->{limit} )
|
||||
{
|
||||
if ( $task->{limit} ) {
|
||||
addPendingTask($task);
|
||||
}
|
||||
}
|
||||
elsif ( $task->{function} eq 'off' )
|
||||
{
|
||||
} elsif ( $task->{function} eq 'off' ) {
|
||||
$task->{device}->{appliance}->off();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub dPrint
|
||||
{
|
||||
sub dPrint {
|
||||
my $dbg_level = shift;
|
||||
if ( fileno(CLIENT) )
|
||||
{
|
||||
if ( fileno(CLIENT) ) {
|
||||
print CLIENT @_
|
||||
}
|
||||
if ( $dbg_level == ZoneMinder::Logger::DEBUG )
|
||||
{
|
||||
if ( $dbg_level == ZoneMinder::Logger::DEBUG ) {
|
||||
Debug(@_);
|
||||
}
|
||||
elsif ( $dbg_level == ZoneMinder::Logger::INFO )
|
||||
{
|
||||
} elsif ( $dbg_level == ZoneMinder::Logger::INFO ) {
|
||||
Info(@_);
|
||||
}
|
||||
elsif ( $dbg_level == ZoneMinder::Logger::WARNING )
|
||||
{
|
||||
} elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) {
|
||||
Warning(@_);
|
||||
}
|
||||
elsif ( $dbg_level == ZoneMinder::Logger::ERROR )
|
||||
{
|
||||
elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) {
|
||||
Error( @_ );
|
||||
}
|
||||
elsif ( $dbg_level == ZoneMinder::Logger::FATAL )
|
||||
{
|
||||
} elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) {
|
||||
Fatal( @_ );
|
||||
}
|
||||
}
|
||||
|
||||
sub x10listen
|
||||
{
|
||||
foreach my $event ( @_ )
|
||||
{
|
||||
sub x10listen {
|
||||
foreach my $event ( @_ ) {
|
||||
#print( Data::Dumper( $_ )."\n" );
|
||||
if ( $event->house_code() eq $Config{ZM_X10_HOUSE_CODE} )
|
||||
{
|
||||
if ( $event->house_code() eq $Config{ZM_X10_HOUSE_CODE} ) {
|
||||
my $unit_code = $event->unit_code();
|
||||
my $device = $device_hash{$unit_code};
|
||||
if ( !$device )
|
||||
{
|
||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
||||
if ( !$device ) {
|
||||
$device = $device_hash{$unit_code} = {
|
||||
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||
status=>'unknown'
|
||||
};
|
||||
}
|
||||
next if ( $event->func() !~ /(?:ON|OFF)/ );
|
||||
$device->{status} = $event->func();
|
||||
my $task_list = $device->{$event->func().'_list'};
|
||||
if ( $task_list )
|
||||
{
|
||||
foreach my $task ( @$task_list )
|
||||
{
|
||||
if ( $task_list ) {
|
||||
foreach my $task ( @$task_list ) {
|
||||
processTask($task);
|
||||
}
|
||||
}
|
||||
} # end if correct house code
|
||||
Info('Got event - '.$event->as_string());
|
||||
}
|
||||
Info( "Got event - ".$event->as_string()."\n" );
|
||||
}
|
||||
}
|
||||
} # end sub x10listen
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
|
32
src/zm_box.h
32
src/zm_box.h
|
@ -33,37 +33,33 @@
|
|||
// Class used for storing a box, which is defined as a region
|
||||
// defined by two coordinates
|
||||
//
|
||||
class Box
|
||||
{
|
||||
class Box {
|
||||
private:
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
|
||||
public:
|
||||
inline Box()
|
||||
{
|
||||
}
|
||||
inline Box() { }
|
||||
explicit inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||
|
||||
inline const Coord &Lo() const { return( lo ); }
|
||||
inline int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
inline const Coord &Lo() const { return lo; }
|
||||
inline int LoX() const { return lo.X(); }
|
||||
inline int LoY() const { return lo.Y(); }
|
||||
inline const Coord &Hi() const { return hi; }
|
||||
inline int HiX() const { return hi.X(); }
|
||||
inline int HiY() const { return hi.Y(); }
|
||||
inline const Coord &Size() const { return size; }
|
||||
inline int Width() const { return size.X(); }
|
||||
inline int Height() const { return size.Y(); }
|
||||
inline int Area() const { return size.X()*size.Y(); }
|
||||
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
inline const Coord Centre() const {
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
return Coord( mid_x, mid_y );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
|
|
|
@ -64,7 +64,9 @@ void zmLoadConfig() {
|
|||
closedir(configSubFolder);
|
||||
}
|
||||
|
||||
zmDbConnect();
|
||||
if ( !zmDbConnect() ) {
|
||||
Fatal("Can't connect to db. Can't continue.");
|
||||
}
|
||||
config.Load();
|
||||
config.Assign();
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
//
|
||||
// Class used for storing an x,y pair, i.e. a coordinate
|
||||
//
|
||||
class Coord
|
||||
{
|
||||
class Coord {
|
||||
private:
|
||||
int x, y;
|
||||
|
||||
|
@ -44,8 +43,7 @@ public:
|
|||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 ) {
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
return( result );
|
||||
}
|
||||
|
|
|
@ -259,6 +259,20 @@ Event::~Event() {
|
|||
if ( frame_data.size() )
|
||||
WriteDbFrames();
|
||||
|
||||
// update frame deltas to refer to video start time which may be a few frames before event start
|
||||
struct timeval video_offset = {0};
|
||||
struct timeval video_start_time = monitor->GetVideoWriterStartTime();
|
||||
if (video_start_time.tv_sec > 0) {
|
||||
timersub(&video_start_time, &start_time, &video_offset);
|
||||
Debug(1, "Updating frames delta by %d sec %d usec",
|
||||
video_offset.tv_sec, video_offset.tv_usec);
|
||||
UpdateFramesDelta(video_offset.tv_sec + video_offset.tv_usec*1e-6);
|
||||
}
|
||||
else {
|
||||
Debug(3, "Video start_time %d sec %d usec not valid -- frame deltas not updated",
|
||||
video_start_time.tv_sec, video_start_time.tv_usec);
|
||||
}
|
||||
|
||||
// Should not be static because we might be multi-threaded
|
||||
char sql[ZM_SQL_LGE_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql),
|
||||
|
@ -472,7 +486,7 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
|
|||
// neccessarily be of the motion. But some events are less than 10 frames,
|
||||
// so I am changing this to 1, but we should overwrite it later with a better snapshot.
|
||||
if ( frames == 1 ) {
|
||||
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file);
|
||||
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file.c_str());
|
||||
}
|
||||
|
||||
struct DeltaTimeval delta_time;
|
||||
|
@ -553,6 +567,27 @@ void Event::WriteDbFrames() {
|
|||
db_mutex.unlock();
|
||||
} // end void Event::WriteDbFrames()
|
||||
|
||||
// Subtract an offset time from frames deltas to match with video start time
|
||||
void Event::UpdateFramesDelta(double offset) {
|
||||
char sql[ZM_SQL_MED_BUFSIZ];
|
||||
|
||||
if (offset == 0.0) return;
|
||||
// the table is set to auto update timestamp so we force it to keep current value
|
||||
snprintf(sql, sizeof(sql),
|
||||
"UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64,
|
||||
offset, id);
|
||||
|
||||
db_mutex.lock();
|
||||
if (mysql_query(&dbconn, sql)) {
|
||||
db_mutex.unlock();
|
||||
Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql);
|
||||
return;
|
||||
}
|
||||
db_mutex.unlock();
|
||||
Info("Updating frames delta by %0.2f sec to match video file", offset);
|
||||
}
|
||||
|
||||
|
||||
void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) {
|
||||
if ( !timestamp.tv_sec ) {
|
||||
Debug(1, "Not adding new frame, zero timestamp");
|
||||
|
@ -562,10 +597,6 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
frames++;
|
||||
|
||||
bool write_to_db = false;
|
||||
FrameType frame_type = score>0?ALARM:(score<0?BULK:NORMAL);
|
||||
// < 0 means no motion detection is being done.
|
||||
if ( score < 0 )
|
||||
score = 0;
|
||||
|
||||
if ( save_jpegs & 1 ) {
|
||||
static char event_file[PATH_MAX];
|
||||
|
@ -579,9 +610,14 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
// If this is the first frame, we should add a thumbnail to the event directory
|
||||
if ( (frames == 1) || (score > (int)max_score) ) {
|
||||
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
|
||||
WriteFrameImage(image, timestamp, snapshot_file);
|
||||
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
||||
}
|
||||
|
||||
FrameType frame_type = score>0?ALARM:(score<0?BULK:NORMAL);
|
||||
// < 0 means no motion detection is being done.
|
||||
if ( score < 0 )
|
||||
score = 0;
|
||||
|
||||
// We are writing an Alarm frame
|
||||
if ( frame_type == ALARM ) {
|
||||
// The first frame with a score will be the frame that alarmed the event
|
||||
|
@ -590,17 +626,30 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
alarm_frame_written = true;
|
||||
WriteFrameImage(image, timestamp, alarm_file.c_str());
|
||||
}
|
||||
if ( videowriter != NULL ) {
|
||||
WriteFrameVideo(image, timestamp, videowriter);
|
||||
}
|
||||
alarm_frames++;
|
||||
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL(delta_time, timestamp, start_time, DT_PREC_2);
|
||||
tot_score += score;
|
||||
if ( score > (int)max_score )
|
||||
max_score = score;
|
||||
|
||||
if ( alarm_image ) {
|
||||
if ( save_jpegs & 2 ) {
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path.c_str(), frames);
|
||||
Debug(1, "Writing analysis frame %d", frames);
|
||||
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
|
||||
Error("Failed to write analysis frame image");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool db_frame = ( frame_type != BULK ) || (frames==1) || ((frames%config.bulk_frame_interval)==0) ;
|
||||
if ( db_frame ) {
|
||||
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL(delta_time, timestamp, start_time, DT_PREC_2);
|
||||
|
||||
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
|
||||
if ( write_to_db || ( frame_data.size() > 20 ) ) {
|
||||
|
@ -635,23 +684,4 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
|
||||
end_time = timestamp;
|
||||
|
||||
// We are writing an Alarm frame
|
||||
if ( frame_type == ALARM ) {
|
||||
alarm_frames++;
|
||||
|
||||
tot_score += score;
|
||||
if ( score > (int)max_score )
|
||||
max_score = score;
|
||||
|
||||
if ( alarm_image ) {
|
||||
if ( save_jpegs & 2 ) {
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path.c_str(), frames);
|
||||
Debug(1, "Writing analysis frame %d", frames);
|
||||
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
|
||||
Error("Failed to write analysis frame image");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end if frame_type == ALARM
|
||||
} // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image)
|
||||
|
|
|
@ -94,8 +94,6 @@ class Event {
|
|||
std::string snapshot_file;
|
||||
std::string alarm_file;
|
||||
VideoStore *videoStore;
|
||||
std::string snapshot_file;
|
||||
std::string alarm_file;
|
||||
|
||||
VideoWriter* videowriter;
|
||||
char video_name[PATH_MAX];
|
||||
|
@ -137,6 +135,7 @@ class Event {
|
|||
private:
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
void WriteDbFrames();
|
||||
void UpdateFramesDelta(double offset);
|
||||
|
||||
public:
|
||||
static const char *getSubPath( struct tm *time ) {
|
||||
|
|
|
@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
snprintf(sql, sizeof(sql),
|
||||
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
|
||||
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
|
||||
"`DefaultVideo`, `Scheme`, `SaveJPEGs` FROM `Events` WHERE `Id` = %" PRIu64, event_id);
|
||||
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
|
||||
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
|
@ -160,6 +160,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
event_data->scheme = Storage::SHALLOW;
|
||||
}
|
||||
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
|
||||
event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8]));
|
||||
mysql_free_result(result);
|
||||
|
||||
Storage * storage = new Storage(event_data->storage_id);
|
||||
|
@ -703,6 +704,34 @@ Debug(1, "Loading image");
|
|||
Error("Failed getting a frame.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// when stored as an mp4, we just have the rotation as a flag in the headers
|
||||
// so we need to rotate it before outputting
|
||||
if (
|
||||
(monitor->GetOptVideoWriter() == Monitor::H264PASSTHROUGH)
|
||||
and
|
||||
(event_data->Orientation != Monitor::ROTATE_0)
|
||||
) {
|
||||
Debug(2, "Rotating image %d", event_data->Orientation);
|
||||
switch ( event_data->Orientation ) {
|
||||
case Monitor::ROTATE_0 :
|
||||
// No action required
|
||||
break;
|
||||
case Monitor::ROTATE_90 :
|
||||
case Monitor::ROTATE_180 :
|
||||
case Monitor::ROTATE_270 :
|
||||
image->Rotate((event_data->Orientation-1)*90);
|
||||
break;
|
||||
case Monitor::FLIP_HORI :
|
||||
case Monitor::FLIP_VERT :
|
||||
image->Flip(event_data->Orientation==Monitor::FLIP_HORI);
|
||||
break;
|
||||
default:
|
||||
Error("Invalid Orientation: %d", event_data->Orientation);
|
||||
}
|
||||
} else {
|
||||
Debug(2, "Not Rotating image %d", event_data->Orientation);
|
||||
} // end if have rotation
|
||||
} else {
|
||||
Error("Unable to get a frame");
|
||||
return false;
|
||||
|
|
|
@ -66,6 +66,7 @@ class EventStream : public StreamBase {
|
|||
char video_file[PATH_MAX];
|
||||
Storage::Schemes scheme;
|
||||
int SaveJPEGs;
|
||||
Monitor::Orientation Orientation;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
|
|
@ -81,7 +81,7 @@ void FFMPEGInit() {
|
|||
av_log_set_callback(log_libav_callback);
|
||||
Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options");
|
||||
} else {
|
||||
Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor not part of your debug targets");
|
||||
Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor is not part of your debug targets");
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
}
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)
|
||||
|
@ -291,8 +291,9 @@ static void zm_log_fps(double d, const char *postfix) {
|
|||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
||||
Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d %s) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)",
|
||||
Debug(1, "Dumping codecpar codec_type(%d %s) codec_id(%d %s) codec_tag(%" PRIu32 ") width(%d) height(%d) bit_rate(%" PRIu64 ") format(%d %s)",
|
||||
par->codec_type,
|
||||
av_get_media_type_string(par->codec_type),
|
||||
par->codec_id,
|
||||
avcodec_get_name(par->codec_id),
|
||||
par->codec_tag,
|
||||
|
@ -300,7 +301,7 @@ void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
|||
par->height,
|
||||
par->bit_rate,
|
||||
par->format,
|
||||
((AVPixelFormat)par->format == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format))
|
||||
(((AVPixelFormat)par->format == AV_PIX_FMT_NONE) ? "none" : av_get_pix_fmt_name((AVPixelFormat)par->format))
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -402,19 +402,19 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
Debug(1, "Selected hw_pix_fmt %d %s",
|
||||
hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt));
|
||||
|
||||
mVideoCodecContext->get_format = get_hw_format;
|
||||
|
||||
ret = av_hwdevice_ctx_create(&hw_device_ctx, type,
|
||||
(hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0);
|
||||
if ( ret < 0 ) {
|
||||
Error("Failed to create hwaccel device.");
|
||||
return -1;
|
||||
}
|
||||
Error("Failed to create hwaccel device. %s",av_make_error_string(ret).c_str());
|
||||
hw_pix_fmt = AV_PIX_FMT_NONE;
|
||||
} else {
|
||||
Debug(1, "Created hwdevice for %s", hwaccel_device.c_str());
|
||||
mVideoCodecContext->get_format = get_hw_format;
|
||||
mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx);
|
||||
hwFrame = zm_av_frame_alloc();
|
||||
}
|
||||
} else {
|
||||
Debug(1, "Failed to setup hwaccel.");
|
||||
Debug(1, "Failed to find suitable hw_pix_fmt.");
|
||||
}
|
||||
#else
|
||||
Debug(1, "AVCodec not new enough for hwaccel");
|
||||
|
@ -422,7 +422,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
#else
|
||||
Warning("HWAccel support not compiled in.");
|
||||
#endif
|
||||
} // end if hwacel_name
|
||||
} // end if hwaccel_name
|
||||
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||
ret = avcodec_open(mVideoCodecContext, mVideoCodec);
|
||||
|
@ -542,7 +542,6 @@ int FfmpegCamera::Close() {
|
|||
return 0;
|
||||
} // end FfmpegCamera::Close
|
||||
|
||||
|
||||
int FfmpegCamera::transfer_to_image(
|
||||
Image &image,
|
||||
AVFrame *output_frame,
|
||||
|
@ -557,9 +556,12 @@ int FfmpegCamera::transfer_to_image(
|
|||
return -1;
|
||||
}
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
// From what I've read, we should align the linesizes to 32bit so that ffmpeg can use SIMD instructions too.
|
||||
int size = av_image_fill_arrays(
|
||||
output_frame->data, output_frame->linesize,
|
||||
directbuffer, imagePixFormat, width, height, 32);
|
||||
directbuffer, imagePixFormat, width, height,
|
||||
(AV_PIX_FMT_RGBA == imagePixFormat ? 32 : 1)
|
||||
);
|
||||
if ( size < 0 ) {
|
||||
Error("Problem setting up data pointers into image %s",
|
||||
av_make_error_string(size).c_str());
|
||||
|
@ -598,19 +600,31 @@ int FfmpegCamera::transfer_to_image(
|
|||
mConvertContext, input_frame->data, input_frame->linesize,
|
||||
0, mVideoCodecContext->height,
|
||||
output_frame->data, output_frame->linesize);
|
||||
if ( ret <= 0 ) {
|
||||
Error("Unable to convert format %u %s linesize %d height %d to format %u %s linesize %d at frame %d codec %u %s : code: %d",
|
||||
if ( ret < 0 ) {
|
||||
Error("Unable to convert format %u %s linesize %d,%d height %d to format %u %s linesize %d,%d at frame %d codec %u %s lines %d: code: %d",
|
||||
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
|
||||
input_frame->linesize, mVideoCodecContext->height,
|
||||
input_frame->linesize[0], input_frame->linesize[1], mVideoCodecContext->height,
|
||||
imagePixFormat,
|
||||
av_get_pix_fmt_name(imagePixFormat),
|
||||
output_frame->linesize,
|
||||
output_frame->linesize[0], output_frame->linesize[1],
|
||||
frameCount,
|
||||
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
||||
mVideoCodecContext->height,
|
||||
ret
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
Debug(4, "Able to convert format %u %s linesize %d,%d height %d to format %u %s linesize %d,%d at frame %d codec %u %s %dx%d ",
|
||||
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
|
||||
input_frame->linesize[0], input_frame->linesize[1], mVideoCodecContext->height,
|
||||
imagePixFormat,
|
||||
av_get_pix_fmt_name(imagePixFormat),
|
||||
output_frame->linesize[0], output_frame->linesize[1],
|
||||
frameCount,
|
||||
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
||||
output_frame->width,
|
||||
output_frame->height
|
||||
);
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal("You must compile ffmpeg with the --enable-swscale "
|
||||
"option to use ffmpeg cameras");
|
||||
|
|
|
@ -53,6 +53,7 @@ class FfmpegCamera : public Camera {
|
|||
AVCodec *mAudioCodec;
|
||||
AVFrame *mRawFrame;
|
||||
AVFrame *mFrame;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
|
||||
AVFrame *input_frame; // Use to point to mRawFrame or hwFrame;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ FFmpeg_Input::FFmpeg_Input() {
|
|||
FFMPEGInit();
|
||||
streams = NULL;
|
||||
frame = NULL;
|
||||
last_seek_request = -1;
|
||||
}
|
||||
|
||||
FFmpeg_Input::~FFmpeg_Input() {
|
||||
|
@ -22,6 +23,17 @@ FFmpeg_Input::~FFmpeg_Input() {
|
|||
}
|
||||
}
|
||||
|
||||
int FFmpeg_Input::Open( AVStream * video_in_stream, AVStream * audio_in_stream ) {
|
||||
video_stream_id = video_in_stream->index;
|
||||
int max_stream_index = video_in_stream->index;
|
||||
|
||||
if ( audio_in_stream ) {
|
||||
max_stream_index = video_in_stream->index > audio_in_stream->index ? video_in_stream->index : audio_in_stream->index;
|
||||
audio_stream_id = audio_in_stream->index;
|
||||
}
|
||||
streams = new stream[max_stream_index];
|
||||
}
|
||||
|
||||
int FFmpeg_Input::Open(const char *filepath) {
|
||||
|
||||
int error;
|
||||
|
@ -127,7 +139,6 @@ int FFmpeg_Input::Close( ) {
|
|||
} // end int FFmpeg_Input::Close()
|
||||
|
||||
AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
||||
Debug(1, "Getting frame from stream %d", stream_id);
|
||||
|
||||
int frameComplete = false;
|
||||
AVPacket packet;
|
||||
|
@ -166,12 +177,14 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
|||
frame = zm_av_frame_alloc();
|
||||
}
|
||||
ret = zm_send_packet_receive_frame(context, frame, packet);
|
||||
if ( ret <= 0 ) {
|
||||
if ( ret < 0 ) {
|
||||
Error("Unable to decode frame at frame %d: %s, continuing",
|
||||
streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str());
|
||||
zm_av_packet_unref(&packet);
|
||||
av_frame_free(&frame);
|
||||
continue;
|
||||
} else {
|
||||
zm_dump_frame(frame, "resulting frame");
|
||||
}
|
||||
|
||||
frameComplete = 1;
|
||||
|
@ -203,7 +216,18 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
|
|||
get_frame(stream_id);
|
||||
} // end if ! frame
|
||||
|
||||
if ( frame->pts > seek_target ) {
|
||||
if ( !frame ) {
|
||||
Warning("Unable to get frame.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (
|
||||
(last_seek_request >= 0)
|
||||
&&
|
||||
(last_seek_request > seek_target )
|
||||
&&
|
||||
(frame->pts > seek_target)
|
||||
) {
|
||||
zm_dump_frame(frame, "frame->pts > seek_target, seek backwards");
|
||||
// our frame must be beyond our seek target. so go backwards to before it
|
||||
if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target,
|
||||
|
@ -217,6 +241,8 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
|
|||
zm_dump_frame(frame, "frame->pts > seek_target, got");
|
||||
} // end if frame->pts > seek_target
|
||||
|
||||
last_seek_request = seek_target;
|
||||
|
||||
// Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want.
|
||||
if ( frame->pts <= seek_target ) {
|
||||
zm_dump_frame(frame, "pts <= seek_target");
|
||||
|
|
|
@ -42,6 +42,7 @@ class FFmpeg_Input {
|
|||
int audio_stream_id;
|
||||
AVFormatContext *input_format_context;
|
||||
AVFrame *frame;
|
||||
int64_t last_seek_request;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -165,8 +165,10 @@ Image::Image( const AVFrame *frame ) {
|
|||
width = frame->width;
|
||||
height = frame->height;
|
||||
pixels = width*height;
|
||||
|
||||
colours = ZM_COLOUR_RGB32;
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
|
||||
size = pixels*colours;
|
||||
buffer = 0;
|
||||
holdbuffer = 0;
|
||||
|
|
|
@ -157,6 +157,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
|
|||
if ( options.mTerminalLevel != NOOPT )
|
||||
tempTerminalLevel = options.mTerminalLevel;
|
||||
|
||||
// DEBUG1 == 1. So >= DEBUG1, we set to DEBUG9?! Why?
|
||||
if ( options.mDatabaseLevel != NOOPT )
|
||||
tempDatabaseLevel = options.mDatabaseLevel;
|
||||
else
|
||||
|
@ -358,7 +359,7 @@ Logger::Level Logger::databaseLevel(Logger::Level databaseLevel) {
|
|||
if ( databaseLevel > NOOPT ) {
|
||||
databaseLevel = limit(databaseLevel);
|
||||
if ( mDatabaseLevel != databaseLevel ) {
|
||||
if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) {
|
||||
if ( (databaseLevel > NOLOG) && (mDatabaseLevel <= NOLOG) ) { // <= NOLOG would be NOOPT
|
||||
if ( !zmDbConnect() ) {
|
||||
databaseLevel = NOLOG;
|
||||
}
|
||||
|
@ -534,8 +535,11 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
|||
fflush(stdout);
|
||||
}
|
||||
if ( level <= mFileLevel ) {
|
||||
if ( !mLogFileFP )
|
||||
if ( !mLogFileFP ) {
|
||||
log_mutex.unlock();
|
||||
openFile();
|
||||
log_mutex.lock();
|
||||
}
|
||||
if ( mLogFileFP ) {
|
||||
fputs(logString, mLogFileFP);
|
||||
if ( mFlush )
|
||||
|
@ -553,8 +557,10 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
|||
if ( level <= mDatabaseLevel ) {
|
||||
|
||||
if ( !db_mutex.trylock() ) {
|
||||
char escapedString[(strlen(syslogStart)*2)+1];
|
||||
mysql_real_escape_string(&dbconn, escapedString, syslogStart, strlen(syslogStart));
|
||||
|
||||
char sql[ZM_SQL_MED_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO `Logs` "
|
||||
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
|
||||
|
@ -572,7 +578,7 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
|||
} else {
|
||||
Level tempDatabaseLevel = mDatabaseLevel;
|
||||
databaseLevel(NOLOG);
|
||||
Error("Can't insert log entry: sql(%s) error(%s)", sql, mysql_error(&dbconn));
|
||||
Error("Can't insert log entry: sql(%s) error(%s)", syslogStart, mysql_error(&dbconn));
|
||||
databaseLevel(tempDatabaseLevel);
|
||||
}
|
||||
db_mutex.unlock();
|
||||
|
|
|
@ -288,6 +288,8 @@ Monitor::Monitor()
|
|||
orientation(ROTATE_0),
|
||||
deinterlacing(0),
|
||||
deinterlacing_value(0),
|
||||
decoder_hwaccel_name(""),
|
||||
decoder_hwaccel_device(""),
|
||||
videoRecording(0),
|
||||
rtsp_describe(0),
|
||||
|
||||
|
@ -410,6 +412,12 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++;
|
||||
alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if ( dbrow[col] )
|
||||
strncpy(device, dbrow[col], sizeof(device)-1);
|
||||
else
|
||||
|
@ -545,6 +553,8 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
last_signal = false;
|
||||
|
||||
camera = NULL;
|
||||
uint64_t image_size = width * height * colours;
|
||||
|
||||
mem_size = sizeof(SharedData)
|
||||
+ sizeof(TriggerData)
|
||||
+ sizeof(VideoStoreData) //Information to pass back to the capture process
|
||||
|
@ -556,7 +566,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
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()),
|
||||
image_buffer_count, image_size, (image_buffer_count*image_size),
|
||||
mem_size);
|
||||
mem_ptr = NULL;
|
||||
|
||||
|
@ -582,7 +592,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
}
|
||||
}
|
||||
|
||||
//this0>delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ),
|
||||
//this->delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ),
|
||||
//ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),
|
||||
Debug(1, "Loaded monitor %d(%s), %d zones", id, name, n_zones);
|
||||
getCamera();
|
||||
|
|
|
@ -257,10 +257,10 @@ protected:
|
|||
Orientation orientation; // Whether the image has to be rotated at all
|
||||
unsigned int deinterlacing;
|
||||
unsigned int deinterlacing_value;
|
||||
bool videoRecording;
|
||||
bool rtsp_describe;
|
||||
std::string decoder_hwaccel_name;
|
||||
std::string decoder_hwaccel_device;
|
||||
bool videoRecording;
|
||||
bool rtsp_describe;
|
||||
|
||||
int savejpegs;
|
||||
int colours;
|
||||
|
@ -297,6 +297,7 @@ protected:
|
|||
int frame_skip; // How many frames to skip in continuous modes
|
||||
int motion_frame_skip; // How many frames to skip in motion detection
|
||||
double analysis_fps_limit; // Target framerate for video analysis
|
||||
struct timeval video_buffer_duration; // How long a video segment to keep in buffer (set only if analysis fps != 0 )
|
||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||
int capture_delay; // How long we wait between capture frames
|
||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||
|
@ -461,7 +462,11 @@ public:
|
|||
uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; }
|
||||
void SetVideoWriterEventId( uint64_t p_event_id ) { video_store_data->current_event = p_event_id; }
|
||||
|
||||
struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; }
|
||||
void SetVideoWriterStartTime(struct timeval &t) { video_store_data->recording = t; }
|
||||
|
||||
unsigned int GetPreEventCount() const { return pre_event_count; };
|
||||
struct timeval GetVideoBufferDuration() const { return video_buffer_duration; };
|
||||
int GetImageBufferCount() const { return image_buffer_count; };
|
||||
State GetState() const;
|
||||
int GetImage( int index=-1, int scale=100 );
|
||||
|
|
|
@ -432,7 +432,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
|||
Warning("Frame send time %d msec too slow, throttling maxfps to %.2f",
|
||||
frameSendTime, maxfps);
|
||||
}
|
||||
}
|
||||
} // Not mpeg
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
return true;
|
||||
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
||||
|
@ -478,6 +478,7 @@ void MonitorStream::runStream() {
|
|||
Image *paused_image = NULL;
|
||||
struct timeval paused_timestamp;
|
||||
|
||||
if ( connkey && ( playback_buffer > 0 ) ) {
|
||||
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
|
||||
const int max_swap_len_suffix = 15;
|
||||
|
||||
|
@ -486,8 +487,6 @@ void MonitorStream::runStream() {
|
|||
int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
|
||||
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
|
||||
|
||||
if ( connkey && ( playback_buffer > 0 ) ) {
|
||||
|
||||
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
|
||||
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
|
||||
} else {
|
||||
|
@ -612,8 +611,9 @@ void MonitorStream::runStream() {
|
|||
if ( temp_index%frame_mod == 0 ) {
|
||||
Debug(2, "Sending delayed frame %d", temp_index);
|
||||
// Send the next frame
|
||||
if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) )
|
||||
if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
|
||||
// frame_sent = true;
|
||||
}
|
||||
|
@ -626,9 +626,17 @@ void MonitorStream::runStream() {
|
|||
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
||||
|
||||
// Send the next frame
|
||||
if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) )
|
||||
if ( !sendFrame(
|
||||
temp_image_buffer[temp_read_index].file_name,
|
||||
&temp_image_buffer[temp_read_index].timestamp
|
||||
) ) {
|
||||
zm_terminate = true;
|
||||
memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) );
|
||||
}
|
||||
memcpy(
|
||||
&last_frame_timestamp,
|
||||
&(swap_image->timestamp),
|
||||
sizeof(last_frame_timestamp)
|
||||
);
|
||||
// frame_sent = true;
|
||||
step = 0;
|
||||
} else {
|
||||
|
@ -636,12 +644,13 @@ void MonitorStream::runStream() {
|
|||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||
|
||||
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||
if ( got_command || actual_delta_time > 5 ) {
|
||||
if ( got_command || (actual_delta_time > 5) ) {
|
||||
// Send keepalive
|
||||
Debug(2, "Sending keepalive frame %d", temp_index);
|
||||
// Send the next frame
|
||||
if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) )
|
||||
if ( !sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) ) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
// frame_sent = true;
|
||||
}
|
||||
} // end if (!paused) or step or paused
|
||||
|
@ -658,17 +667,9 @@ void MonitorStream::runStream() {
|
|||
}
|
||||
} // end if ( buffered_playback && delayed )
|
||||
|
||||
if ( last_read_index != monitor->shared_data->last_write_index ) {
|
||||
// have a new image to send
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
|
||||
#if 0
|
||||
// I don't know what this is about
|
||||
ZMPacket *snap = &monitor->image_buffer[index];
|
||||
if ( tvCmp(last_frame_time, *(snap->timestamp)) ) {
|
||||
|
||||
last_read_index = monitor->shared_data->last_write_index;
|
||||
Debug(2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
||||
index, frame_mod, frame_count, paused, delayed );
|
||||
#endif
|
||||
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
|
||||
if ( !paused && !delayed ) {
|
||||
last_read_index = monitor->shared_data->last_write_index;
|
||||
|
@ -840,8 +841,10 @@ void MonitorStream::SingleImage( int scale ) {
|
|||
}
|
||||
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||
|
||||
fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size );
|
||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||
fprintf(stdout,
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: image/jpeg\r\n\r\n",
|
||||
img_buffer_size);
|
||||
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
||||
}
|
||||
|
||||
|
@ -859,12 +862,13 @@ void MonitorStream::SingleImageRaw( int scale ) {
|
|||
monitor->TimestampImage(snap_image, snap->timestamp);
|
||||
}
|
||||
|
||||
fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() );
|
||||
fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" );
|
||||
fprintf(stdout,
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: image/x-rgb\r\n\r\n",
|
||||
snap_image->Size());
|
||||
fwrite(snap_image->Buffer(), snap_image->Size(), 1, stdout);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
void MonitorStream::SingleImageZip(int scale) {
|
||||
unsigned long img_buffer_size = 0;
|
||||
|
@ -884,8 +888,10 @@ void MonitorStream::SingleImageZip( int scale ) {
|
|||
}
|
||||
snap_image->Zip(img_buffer, &img_buffer_size);
|
||||
|
||||
fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size );
|
||||
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
|
||||
fprintf(stdout,
|
||||
"Content-Length: %ld\r\n"
|
||||
"Content-Type: image/x-rgbz\r\n\r\n",
|
||||
img_buffer_size);
|
||||
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
||||
}
|
||||
#endif // HAVE_ZLIB_H
|
||||
|
|
|
@ -235,6 +235,73 @@ void zm_packetqueue::clearQueue() {
|
|||
mutex.unlock();
|
||||
}
|
||||
|
||||
// clear queue keeping only specified duration of video -- return number of pkts removed
|
||||
unsigned int zm_packetqueue::clearQueue(struct timeval *duration, int streamId) {
|
||||
|
||||
if ( pktQueue.empty() ) {
|
||||
return 0;
|
||||
}
|
||||
struct timeval keep_from;
|
||||
std::list<ZMPacket *>::reverse_iterator it;
|
||||
it = pktQueue.rbegin();
|
||||
|
||||
struct timeval *t = (*it)->timestamp;
|
||||
timersub(t, duration, &keep_from);
|
||||
++it;
|
||||
|
||||
Debug(3, "Looking for frame before queue keep time with stream id (%d), queue has %d packets",
|
||||
streamId, pktQueue.size());
|
||||
for ( ; it != pktQueue.rend(); ++it) {
|
||||
ZMPacket *zm_packet = *it;
|
||||
AVPacket *av_packet = &(zm_packet->packet);
|
||||
if (av_packet->stream_index == streamId
|
||||
&& timercmp( zm_packet->timestamp, &keep_from, <= )) {
|
||||
Debug(3, "Found frame before keep time with stream index %d at %d.%d",
|
||||
av_packet->stream_index,
|
||||
zm_packet->timestamp->tv_sec,
|
||||
zm_packet->timestamp->tv_usec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == pktQueue.rend()) {
|
||||
Debug(1, "Didn't find a frame before queue preserve time. keeping all");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Debug(3, "Looking for keyframe");
|
||||
for ( ; it != pktQueue.rend(); ++it) {
|
||||
ZMPacket *zm_packet = *it;
|
||||
AVPacket *av_packet = &(zm_packet->packet);
|
||||
if (av_packet->flags & AV_PKT_FLAG_KEY
|
||||
&& av_packet->stream_index == streamId) {
|
||||
Debug(3, "Found keyframe before start with stream index %d at %d.%d",
|
||||
av_packet->stream_index,
|
||||
zm_packet->timestamp->tv_sec,
|
||||
zm_packet->timestamp->tv_usec );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( it == pktQueue.rend() ) {
|
||||
Debug(1, "Didn't find a keyframe before event starttime. keeping all" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int deleted_frames = 0;
|
||||
ZMPacket *zm_packet = NULL;
|
||||
while (distance(it, pktQueue.rend()) > 1) {
|
||||
zm_packet = pktQueue.front();
|
||||
pktQueue.pop_front();
|
||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||
delete zm_packet;
|
||||
deleted_frames += 1;
|
||||
}
|
||||
zm_packet = NULL;
|
||||
Debug(3, "Deleted %d frames", deleted_frames);
|
||||
|
||||
return deleted_frames;
|
||||
}
|
||||
|
||||
unsigned int zm_packetqueue::size() {
|
||||
return pktQueue.size();
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ class zm_packetqueue {
|
|||
bool popVideoPacket(ZMPacket* packet);
|
||||
bool popAudioPacket(ZMPacket* packet);
|
||||
unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id);
|
||||
unsigned int clearQueue(struct timeval *duration, int streamid);
|
||||
void clearQueue();
|
||||
void dumpQueue();
|
||||
unsigned int size();
|
||||
|
|
|
@ -26,11 +26,9 @@
|
|||
#include <cmath>
|
||||
#endif
|
||||
|
||||
void Polygon::calcArea()
|
||||
{
|
||||
void Polygon::calcArea() {
|
||||
double float_area = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) {
|
||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
|
@ -38,13 +36,11 @@ void Polygon::calcArea()
|
|||
area = (int)round(fabs(float_area));
|
||||
}
|
||||
|
||||
void Polygon::calcCentre()
|
||||
{
|
||||
void Polygon::calcCentre() {
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) {
|
||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||
}
|
||||
|
@ -54,16 +50,14 @@ void Polygon::calcCentre()
|
|||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||
}
|
||||
|
||||
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
||||
{
|
||||
Polygon::Polygon(int p_n_coords, const Coord *p_coords) : n_coords( p_n_coords ) {
|
||||
coords = new Coord[n_coords];
|
||||
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
for ( int i = 0; i < n_coords; i++ ) {
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
|
@ -79,38 +73,36 @@ Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords
|
|||
calcCentre();
|
||||
}
|
||||
|
||||
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
||||
Polygon::Polygon( const Polygon &p_polygon ) :
|
||||
n_coords(p_polygon.n_coords),
|
||||
extent(p_polygon.extent),
|
||||
area(p_polygon.area),
|
||||
centre(p_polygon.centre)
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
for( int i = 0; i < n_coords; i++ ) {
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
}
|
||||
|
||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
||||
{
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
Polygon &Polygon::operator=( const Polygon &p_polygon ) {
|
||||
if ( n_coords < p_polygon.n_coords ) {
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
for ( int i = 0; i < n_coords; i++ ) {
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
return *this ;
|
||||
}
|
||||
|
||||
bool Polygon::isInside( const Coord &coord ) const
|
||||
{
|
||||
bool Polygon::isInside( const Coord &coord ) const {
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) {
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||
|
@ -118,5 +110,5 @@ bool Polygon::isInside( const Coord &coord ) const
|
|||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
return inside;
|
||||
}
|
||||
|
|
|
@ -41,13 +41,13 @@ protected:
|
|||
static int CompareYX( const void *p1, const void *p2 ) {
|
||||
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
return int(e1->min_x - e2->min_x);
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
return int(e1->min_y - e2->min_y);
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 ) {
|
||||
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
return int(e1->min_x - e2->min_x);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,22 +93,22 @@ public:
|
|||
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline int getNumCoords() const { return n_coords; }
|
||||
inline const Coord &getCoord( int index ) const {
|
||||
return( coords[index] );
|
||||
return coords[index];
|
||||
}
|
||||
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
inline const Box &Extent() const { return extent; }
|
||||
inline int LoX() const { return extent.LoX(); }
|
||||
inline int HiX() const { return extent.HiX(); }
|
||||
inline int LoY() const { return extent.LoY(); }
|
||||
inline int HiY() const { return extent.HiY(); }
|
||||
inline int Width() const { return extent.Width(); }
|
||||
inline int Height() const { return extent.Height(); }
|
||||
|
||||
inline int Area() const { return( area ); }
|
||||
inline int Area() const { return area; }
|
||||
inline const Coord &Centre() const {
|
||||
return( centre );
|
||||
return centre;
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
};
|
||||
|
|
|
@ -235,7 +235,7 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
|||
} else {
|
||||
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) {
|
||||
Error( "Can't ioctl(): %s", strerror(errno) );
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( total_bytes_to_read == 0 ) {
|
||||
|
@ -250,13 +250,13 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
|||
return -1;
|
||||
}
|
||||
// Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close.
|
||||
return( 0 );
|
||||
return 0;
|
||||
}
|
||||
// If socket is closed locally, then select will fail, but if it is closed remotely
|
||||
// then we have an exception on our socket.. but no data.
|
||||
Debug(3, "Socket closed remotely");
|
||||
//Disconnect(); // Disconnect is done outside of ReadData now.
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
// There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily.
|
||||
|
@ -293,6 +293,18 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
|||
return total_bytes_read;
|
||||
}
|
||||
|
||||
int RemoteCameraHttp::GetData() {
|
||||
time_t start_time = time(NULL);
|
||||
int buffer_len = 0;
|
||||
while ( !( buffer_len = ReadData(buffer) ) ) {
|
||||
if ( zm_terminate || ( start_time - time(NULL) < ZM_WATCH_MAX_DELAY ))
|
||||
return -1;
|
||||
Debug(4, "Timeout waiting for REGEXP HEADER");
|
||||
usleep(100000);
|
||||
}
|
||||
return buffer_len;
|
||||
}
|
||||
|
||||
int RemoteCameraHttp::GetResponse() {
|
||||
int buffer_len;
|
||||
#if HAVE_LIBPCRE
|
||||
|
@ -315,9 +327,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
switch( state ) {
|
||||
case HEADER :
|
||||
{
|
||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
||||
Debug(4, "Timeout waiting for REGEXP HEADER");
|
||||
}
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error("Unable to read header data");
|
||||
return -1;
|
||||
|
@ -457,9 +467,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
state = CONTENT;
|
||||
} else {
|
||||
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
||||
Debug(4, "Timeout waiting to extract subheader");
|
||||
}
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to extract subheader data" );
|
||||
return( -1 );
|
||||
|
@ -491,7 +499,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
if ( content_length ) {
|
||||
while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) {
|
||||
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
|
||||
int bytes_read = ReadData( buffer );
|
||||
int bytes_read = GetData();
|
||||
|
||||
if ( bytes_read < 0 ) {
|
||||
Error( "Unable to read content" );
|
||||
|
@ -502,9 +510,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
Debug( 3, "Got end of image by length, content-length = %d", content_length );
|
||||
} else {
|
||||
while ( !content_length ) {
|
||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
||||
Debug(4, "Timeout waiting for content");
|
||||
}
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read content" );
|
||||
return( -1 );
|
||||
|
@ -616,9 +622,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
}
|
||||
case HEADERCONT :
|
||||
{
|
||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
||||
Debug(1, "Timeout waiting for HEADERCONT");
|
||||
}
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error("Unable to read header");
|
||||
return -1;
|
||||
|
@ -903,9 +907,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
state = CONTENT;
|
||||
} else {
|
||||
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
||||
while ( !( buffer_len = ReadData(buffer) ) &&!zm_terminate ) {
|
||||
Debug(1, "Timeout waiting to extra subheader non regexp");
|
||||
}
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read subheader" );
|
||||
return( -1 );
|
||||
|
@ -945,7 +947,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
if ( content_length ) {
|
||||
while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) {
|
||||
Debug(4, "getting more data");
|
||||
int bytes_read = ReadData(buffer);
|
||||
int bytes_read = GetData();
|
||||
if ( bytes_read < 0 ) {
|
||||
Error("Unable to read content");
|
||||
return -1;
|
||||
|
@ -958,8 +960,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
while ( !content_length && !zm_terminate ) {
|
||||
Debug(4, "!content_length, ReadData");
|
||||
buffer_len = ReadData( buffer );
|
||||
if ( buffer_len < 0 )
|
||||
{
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read content" );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -1024,7 +1025,6 @@ int RemoteCameraHttp::PreCapture() {
|
|||
if ( sd < 0 ) {
|
||||
Connect();
|
||||
if ( sd < 0 ) {
|
||||
Error("Unable to connect to camera");
|
||||
return -1;
|
||||
}
|
||||
mode = SINGLE_IMAGE;
|
||||
|
|
|
@ -68,6 +68,7 @@ public:
|
|||
int Disconnect();
|
||||
int SendRequest();
|
||||
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
|
||||
int GetData();
|
||||
int GetResponse();
|
||||
int PreCapture();
|
||||
int Capture( ZMPacket &p );
|
||||
|
|
|
@ -342,7 +342,7 @@ int RemoteCameraRtsp::Capture( ZMPacket &zm_packet ) {
|
|||
while ( !frameComplete && (buffer.size() > 0) ) {
|
||||
packet->data = buffer.head();
|
||||
packet->size = buffer.size();
|
||||
bytes += packet.size;
|
||||
bytes += packet->size;
|
||||
|
||||
// So I think this is the magic decode step. Result is a raw image?
|
||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||
|
|
|
@ -203,7 +203,7 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Debug (1,"Got stored expiry time of %u",stored_iat);
|
||||
Debug (1,"Got last token revoke time of: %u",stored_iat);
|
||||
Debug (1,"Authenticated user '%s' via token", username.c_str());
|
||||
mysql_free_result(result);
|
||||
return user;
|
||||
|
|
|
@ -58,6 +58,7 @@ VideoStore::VideoStore(
|
|||
packets_written = 0;
|
||||
frame_count = 0;
|
||||
in_frame = NULL;
|
||||
|
||||
video_in_frame = NULL;
|
||||
video_in_ctx = NULL;
|
||||
// In future, we should just pass in the codec context instead of the stream. Don't really need the stream.
|
||||
|
@ -85,10 +86,8 @@ VideoStore::VideoStore(
|
|||
|
||||
video_start_pts = 0;
|
||||
audio_next_pts = 0;
|
||||
audio_next_dts = 0;
|
||||
out_format = NULL;
|
||||
oc = NULL;
|
||||
ret = 0;
|
||||
} // VideoStore::VideoStore
|
||||
|
||||
bool VideoStore::open() {
|
||||
|
@ -100,8 +99,6 @@ bool VideoStore::open() {
|
|||
"Could not create video storage stream %s as no out ctx"
|
||||
" could be assigned based on filename: %s",
|
||||
filename, av_make_error_string(ret).c_str());
|
||||
} else {
|
||||
Debug(4, "Success allocating out format ctx");
|
||||
}
|
||||
|
||||
// Couldn't deduce format from filename, trying from format name
|
||||
|
@ -113,11 +110,8 @@ bool VideoStore::open() {
|
|||
" could not be assigned based on filename or format %s",
|
||||
filename, format);
|
||||
return false;
|
||||
} else {
|
||||
Debug(4, "Success allocating out ctx");
|
||||
}
|
||||
} // end if ! oc
|
||||
Debug(2, "Success opening output contect");
|
||||
|
||||
AVDictionary *pmetadata = NULL;
|
||||
ret = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0);
|
||||
|
@ -145,6 +139,7 @@ bool VideoStore::open() {
|
|||
zm_dump_codec(video_in_ctx);
|
||||
#endif
|
||||
} else {
|
||||
// FIXME delete?
|
||||
Debug(2, "No input ctx");
|
||||
video_in_ctx = avcodec_alloc_context3(NULL);
|
||||
video_in_stream_index = 0;
|
||||
|
@ -170,14 +165,14 @@ bool VideoStore::open() {
|
|||
}
|
||||
max_stream_index = video_out_stream->index;
|
||||
|
||||
// FIXME SHould check that we are set to passthrough. Might be same codec, but want privacy overlays
|
||||
// FIXME Should check that we are set to passthrough. Might be same codec, but want privacy overlays
|
||||
if ( video_in_stream && ( video_in_ctx->codec_id == wanted_codec ) ) {
|
||||
// Copy params from instream to ctx
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar);
|
||||
#else
|
||||
ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
||||
#endif
|
||||
// Copy params from instream to ctx
|
||||
if ( ret < 0 ) {
|
||||
Error("Could not initialize ctx parameteres");
|
||||
return false;
|
||||
|
@ -219,7 +214,7 @@ bool VideoStore::open() {
|
|||
Warning("Unsupported Orientation(%d)", orientation);
|
||||
}
|
||||
} // end if orientation
|
||||
} else {
|
||||
} else { // Either no video in or not the desired codec
|
||||
for ( unsigned int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) {
|
||||
if ( codec_data[i].codec_id != monitor->OutputCodec() )
|
||||
continue;
|
||||
|
@ -244,7 +239,7 @@ bool VideoStore::open() {
|
|||
Debug(2,"No timebase found in video in context, defaulting to Q");
|
||||
video_out_ctx->time_base = AV_TIME_BASE_Q;
|
||||
}
|
||||
video_out_stream->time_base = video_in_stream->time_base;
|
||||
video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q;
|
||||
|
||||
if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) {
|
||||
video_out_ctx->max_b_frames = 1;
|
||||
|
@ -327,6 +322,7 @@ bool VideoStore::open() {
|
|||
}
|
||||
#else
|
||||
avcodec_copy_context(video_out_stream->codec, video_out_ctx);
|
||||
#endif
|
||||
|
||||
converted_in_samples = NULL;
|
||||
audio_out_codec = NULL;
|
||||
|
@ -458,10 +454,7 @@ bool VideoStore::open() {
|
|||
for ( int i = 0; i <= max_stream_index; i++ ) {
|
||||
next_dts[i] = 0;
|
||||
}
|
||||
} // VideoStore::VideoStore
|
||||
|
||||
bool VideoStore::open() {
|
||||
int ret;
|
||||
/* open the out file, if needed */
|
||||
if ( !(out_format->flags & AVFMT_NOFILE) ) {
|
||||
if ( (ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL) ) < 0 ) {
|
||||
|
@ -501,27 +494,8 @@ bool VideoStore::open() {
|
|||
return true;
|
||||
} // end bool VideoStore::open()
|
||||
|
||||
void VideoStore::write_audio_packet( AVPacket &pkt ) {
|
||||
//Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
||||
//pkt.dts, pkt.duration);
|
||||
pkt.pts = audio_next_pts;
|
||||
pkt.dts = audio_next_dts;
|
||||
|
||||
Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration);
|
||||
if ( pkt.duration > 0 ) {
|
||||
pkt.duration =
|
||||
av_rescale_q(pkt.duration, audio_out_ctx->time_base,
|
||||
audio_out_stream->time_base);
|
||||
}
|
||||
audio_next_pts += pkt.duration;
|
||||
audio_next_dts += pkt.duration;
|
||||
|
||||
Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration);
|
||||
pkt.stream_index = audio_out_stream->index;
|
||||
av_interleaved_write_frame(oc, &pkt);
|
||||
} // end void VideoStore::Write_audio_packet( AVPacket &pkt )
|
||||
|
||||
void VideoStore::flush_codecs() {
|
||||
int ret;
|
||||
// The codec queues data. We need to send a flush command and out
|
||||
// whatever we get. Failures are not fatal.
|
||||
|
||||
|
@ -560,7 +534,7 @@ void VideoStore::flush_codecs() {
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
write_video_packet(pkt);
|
||||
write_packet(&pkt, video_out_stream);
|
||||
zm_av_packet_unref(&pkt);
|
||||
} // while have buffered frames
|
||||
} // end if have delay capability
|
||||
|
@ -635,8 +609,6 @@ void VideoStore::flush_codecs() {
|
|||
zm_av_packet_unref(&pkt);
|
||||
} // while have buffered frames
|
||||
} // end if audio_out_codec
|
||||
|
||||
} // end if audio_out_codec
|
||||
} // end flush_codecs
|
||||
|
||||
VideoStore::~VideoStore() {
|
||||
|
@ -1057,6 +1029,7 @@ int VideoStore::writePacket( ZMPacket *ipkt ) {
|
|||
}
|
||||
|
||||
int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
||||
int ret;
|
||||
frame_count += 1;
|
||||
|
||||
// if we have to transcode
|
||||
|
@ -1191,7 +1164,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
|||
video_in_stream->time_base,
|
||||
video_out_stream->time_base);
|
||||
Debug(1, "duration from ipkt: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ") (%d/%d) (%d/%d)",
|
||||
zm_packet->in_frame->pkt_pts,
|
||||
zm_packet->in_frame->pts,
|
||||
video_last_pts,
|
||||
zm_packet->in_frame->pkt_duration,
|
||||
duration,
|
||||
|
@ -1203,13 +1176,13 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
|||
} else {
|
||||
duration =
|
||||
av_rescale_q(
|
||||
zm_packet->in_frame->pkt_pts - video_last_pts,
|
||||
zm_packet->in_frame->pts - video_last_pts,
|
||||
video_in_stream->time_base,
|
||||
video_out_stream->time_base);
|
||||
Debug(1, "duration calc: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ")",
|
||||
zm_packet->in_frame->pkt_pts,
|
||||
zm_packet->in_frame->pts,
|
||||
video_last_pts,
|
||||
zm_packet->in_frame->pkt_pts - video_last_pts,
|
||||
zm_packet->in_frame->pts - video_last_pts,
|
||||
duration
|
||||
);
|
||||
if ( duration <= 0 ) {
|
||||
|
@ -1226,7 +1199,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
|||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
opkt.flags = ipkt->flags;
|
||||
opkt.duration = pkt->duration;
|
||||
opkt.duration = ipkt->duration;
|
||||
|
||||
if ( ipkt->dts != AV_NOPTS_VALUE ) {
|
||||
if ( !video_first_dts ) {
|
||||
|
@ -1394,7 +1367,6 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) {
|
|||
av_make_error_string(ret).c_str());
|
||||
} else {
|
||||
Debug(2, "Success writing packet");
|
||||
>>>>>>> master
|
||||
}
|
||||
return ret;
|
||||
} // end int VideoStore::write_packet(AVPacket *pkt, AVStream *stream)
|
||||
|
|
|
@ -41,11 +41,17 @@ static struct CodecData codec_data[];
|
|||
int audio_in_stream_index;
|
||||
|
||||
AVCodec *video_out_codec;
|
||||
AVCodecContext *video_in_ctx;
|
||||
AVCodecContext *video_out_ctx;
|
||||
|
||||
AVStream *video_in_stream;
|
||||
AVStream *audio_in_stream;
|
||||
|
||||
const AVCodec *audio_in_codec;
|
||||
AVCodecContext *audio_in_ctx;
|
||||
// The following are used when encoding the audio stream to AAC
|
||||
AVCodec *audio_out_codec;
|
||||
AVCodecContext *audio_out_ctx;
|
||||
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||
AVPacket opkt;
|
||||
// we are transcoding
|
||||
|
@ -53,18 +59,10 @@ static struct CodecData codec_data[];
|
|||
AVFrame *in_frame;
|
||||
AVFrame *out_frame;
|
||||
|
||||
AVCodecContext *video_in_ctx;
|
||||
const AVCodec *audio_in_codec;
|
||||
AVCodecContext *audio_in_ctx;
|
||||
|
||||
SWScale swscale;
|
||||
unsigned int packets_written;
|
||||
unsigned int frame_count;
|
||||
|
||||
// The following are used when encoding the audio stream to AAC
|
||||
AVStream *audio_out_stream;
|
||||
AVCodec *audio_out_codec;
|
||||
AVCodecContext *audio_out_ctx;
|
||||
#ifdef HAVE_LIBSWRESAMPLE
|
||||
SwrContext *resample_ctx;
|
||||
#else
|
||||
|
|
|
@ -349,7 +349,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
if ( result < 0 ) {
|
||||
// Failure, try reconnecting
|
||||
sleep(1);
|
||||
sleep(5);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
20
src/zms.cpp
20
src/zms.cpp
|
@ -106,9 +106,9 @@ int main(int argc, const char *argv[]) {
|
|||
|
||||
for ( int p = 0; p < parm_no; p++ ) {
|
||||
char *name = strtok(parms[p], "=");
|
||||
char *value = strtok(NULL, "=");
|
||||
char const *value = strtok(NULL, "=");
|
||||
if ( !value )
|
||||
value = (char *)"";
|
||||
value = "";
|
||||
if ( !strcmp(name, "source") ) {
|
||||
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
|
||||
if ( !strcmp(value, "fifo") )
|
||||
|
@ -127,10 +127,10 @@ int main(int argc, const char *argv[]) {
|
|||
} else if ( !strcmp(name, "time") ) {
|
||||
event_time = atoi(value);
|
||||
} else if ( !strcmp(name, "event") ) {
|
||||
event_id = strtoull(value, (char **)NULL, 10);
|
||||
event_id = strtoull(value, NULL, 10);
|
||||
source = ZMS_EVENT;
|
||||
} else if ( !strcmp(name, "frame") ) {
|
||||
frame_id = strtoull(value, (char **)NULL, 10);
|
||||
frame_id = strtoull(value, NULL, 10);
|
||||
source = ZMS_EVENT;
|
||||
} else if ( !strcmp(name, "scale") ) {
|
||||
scale = atoi(value);
|
||||
|
@ -237,11 +237,13 @@ int main(int argc, const char *argv[]) {
|
|||
|
||||
time_t now = time(0);
|
||||
char date_string[64];
|
||||
strftime(date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||
strftime(date_string, sizeof(date_string)-1,
|
||||
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||
|
||||
fprintf(stdout, "Last-Modified: %s\r\n", date_string);
|
||||
fputs("Last-Modified: ", stdout);
|
||||
fputs(date_string, stdout);
|
||||
fputs(
|
||||
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
||||
"\r\nExpires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
||||
"Cache-Control: no-store, no-cache, must-revalidate\r\n"
|
||||
"Cache-Control: post-check=0, pre-check=0\r\n"
|
||||
"Pragma: no-cache\r\n",
|
||||
|
@ -279,7 +281,9 @@ int main(int argc, const char *argv[]) {
|
|||
stream.setStreamType(MonitorStream::STREAM_MPEG);
|
||||
#else // HAVE_LIBAVCODEC
|
||||
Error("MPEG streaming of '%s' attempted while disabled", query);
|
||||
fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n");
|
||||
fprintf(stderr, "MPEG streaming is disabled.\n"
|
||||
"You should configure with the --with-ffmpeg"
|
||||
" option and rebuild to use this functionality.\n");
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return -1;
|
||||
|
|
|
@ -430,7 +430,9 @@ int main(int argc, char *argv[]) {
|
|||
User *user = 0;
|
||||
|
||||
if ( config.opt_use_auth ) {
|
||||
if ( strcmp(config.auth_relay, "none") == 0 ) {
|
||||
if ( jwt_token_str != "" ) {
|
||||
user = zmLoadTokenUser(jwt_token_str, false);
|
||||
} else if ( strcmp(config.auth_relay, "none") == 0 ) {
|
||||
if ( !username ) {
|
||||
Error("Username must be supplied");
|
||||
exit_zmu(-1);
|
||||
|
@ -444,13 +446,10 @@ int main(int argc, char *argv[]) {
|
|||
user = zmLoadUser(username);
|
||||
} else {
|
||||
|
||||
if ( !(username && password) && !auth && (jwt_token_str=="")) {
|
||||
if ( !(username && password) && !auth ) {
|
||||
Error("Username and password or auth/token string must be supplied");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
if (jwt_token_str != "") {
|
||||
user = zmLoadTokenUser(jwt_token_str, false);
|
||||
}
|
||||
if ( auth ) {
|
||||
user = zmLoadAuthUser(auth, false);
|
||||
}
|
||||
|
|
|
@ -128,14 +128,14 @@ else
|
|||
fi;
|
||||
fi
|
||||
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||
if [ "$PPA" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
||||
IFS='.' read -r -a VERSION <<< "$RELEASE"
|
||||
if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then
|
||||
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-stable"
|
||||
else
|
||||
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
|
||||
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
|
||||
fi;
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
|
@ -175,7 +175,7 @@ cd ../
|
|||
|
||||
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
|
||||
|
||||
if [ $VERSION == "" ]; then
|
||||
if [ -z "$VERSION" ]; then
|
||||
exit 1;
|
||||
fi;
|
||||
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
|
||||
|
@ -316,7 +316,7 @@ EOF
|
|||
read -p "Do you want to upload this binary to zmrepo? (y/N)"
|
||||
if [[ $REPLY == [yY] ]]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/"
|
||||
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming/"
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/"
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
# We don't deploy during eslint checks, so exit immediately
|
||||
if [ "${DIST}" == "eslint" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check to see if this script has access to all the commands it needs
|
||||
for CMD in sshfs rsync find fusermount mkdir; do
|
||||
type $CMD 2>&1 > /dev/null
|
||||
|
@ -12,12 +17,17 @@ for CMD in sshfs rsync find fusermount mkdir; do
|
|||
fi
|
||||
done
|
||||
|
||||
# We only want to deploy packages during cron events
|
||||
# See https://docs.travis-ci.com/user/cron-jobs/
|
||||
if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
||||
|
||||
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
||||
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||
if [ "${RELEASE}" != "" ]; then
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||
targetfolder="debian/release/mini-dinstall/incoming"
|
||||
else
|
||||
targetfolder="debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming"
|
||||
fi
|
||||
else
|
||||
targetfolder="debian/master/mini-dinstall/incoming"
|
||||
fi
|
||||
else
|
||||
targetfolder="travis"
|
||||
fi
|
||||
|
@ -25,40 +35,17 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}"
|
|||
echo
|
||||
echo "Target subfolder set to $targetfolder"
|
||||
echo
|
||||
if [ "${USE_SFTP}" == "yes" ]; then
|
||||
echo "Running \$(rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)"
|
||||
rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
|
||||
echo "Running \$(rsync -v -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)"
|
||||
rsync -v --ignore-missing-args --exclude 'external-repo.noarch.rpm' -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo
|
||||
echo "Files copied successfully."
|
||||
echo
|
||||
else
|
||||
echo
|
||||
echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!"
|
||||
echo "See log output for details."
|
||||
echo
|
||||
exit 99
|
||||
fi
|
||||
else
|
||||
mkdir -p ./zmrepo
|
||||
ssh_mntchk="$(sshfs zmrepo@zmrepo.zoneminder.com:./ ./zmrepo -o workaround=rename,reconnect 2>&1)"
|
||||
|
||||
if [ -z "$ssh_mntchk" ]; then
|
||||
echo
|
||||
echo "Remote filesystem mounted successfully."
|
||||
echo "Begin transfering files..."
|
||||
echo
|
||||
|
||||
# Don't keep packages older than 5 days
|
||||
find ./zmrepo/$targetfolder/ -maxdepth 1 -type f,l -mtime +5 -delete
|
||||
rsync -vzlh --ignore-errors build/* zmrepo/$targetfolder/
|
||||
fusermount -zu zmrepo
|
||||
else
|
||||
echo
|
||||
echo "ERROR: Attempt to mount zmrepo.zoneminder.com failed!"
|
||||
echo "sshfs gave the following error message:"
|
||||
echo \"$ssh_mntchk\"
|
||||
echo
|
||||
exit 99
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# General sanity checks
|
||||
checksanity () {
|
||||
# Check to see if this script has access to all the commands it needs
|
||||
for CMD in set echo curl git ln mkdir rmdir cat patch; do
|
||||
for CMD in set echo curl git ln mkdir rmdir cat patch sed; do
|
||||
type $CMD 2>&1 > /dev/null
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
|
@ -30,7 +30,7 @@ checksanity () {
|
|||
ARCH="x86_64"
|
||||
fi
|
||||
|
||||
if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" ]]; then
|
||||
if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" && "${ARCH}" != "aarch64" ]]; then
|
||||
echo
|
||||
echo "ERROR: Unsupported architecture specified \"${ARCH}\"."
|
||||
echo
|
||||
|
@ -150,7 +150,7 @@ install_deb () {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Install and test the zoneminder package (only) for Ubuntu Trusty
|
||||
# Install and test the zoneminder package (only) for Ubuntu Xenial
|
||||
pkgname="build/zoneminder_${VERSION}-${RELEASE}_amd64.deb"
|
||||
|
||||
if [ -e $pkgname ]; then
|
||||
|
@ -275,6 +275,8 @@ checkdeploytarget () {
|
|||
echo "*** TRACEROUTE ***"
|
||||
echo
|
||||
traceroute -w 2 -m 15 ${DEPLOYTARGET}
|
||||
|
||||
exit 97
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -291,16 +293,16 @@ if [ "${TRAVIS}" == "true" ]; then
|
|||
fi
|
||||
checksanity
|
||||
|
||||
# We don't want to build packages for all supported distros after every commit
|
||||
# Only build all packages when executed via cron
|
||||
# See https://docs.travis-ci.com/user/cron-jobs/
|
||||
|
||||
# Steps common to Redhat distros
|
||||
if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
||||
if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
|
||||
commonprep
|
||||
echo "Begin Redhat build..."
|
||||
|
||||
# Newer Redhat distros use dnf package manager rather than yum
|
||||
if [ "${DIST}" -gt "7" ]; then
|
||||
sed -i 's\yum\dnf\' utils/packpack/redhat_package.mk
|
||||
fi
|
||||
|
||||
setrpmpkgname
|
||||
|
||||
ln -sfT distros/redhat rpm
|
||||
|
@ -326,7 +328,7 @@ if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
|||
|
||||
echo "Starting packpack..."
|
||||
execpackpack
|
||||
fi;
|
||||
|
||||
# Steps common to Debian based distros
|
||||
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||
commonprep
|
||||
|
@ -348,14 +350,27 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
|
|||
echo "Starting packpack..."
|
||||
execpackpack
|
||||
|
||||
# We were not triggered via cron so just build and test trusty
|
||||
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ]; then
|
||||
# If we are running inside Travis then attempt to install the deb we just built
|
||||
if [ "${TRAVIS}" == "true" ]; then
|
||||
# Try to install and run the newly built zoneminder package
|
||||
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then
|
||||
echo "Begin Deb package installation..."
|
||||
install_deb
|
||||
fi
|
||||
fi
|
||||
|
||||
# Steps common to eslint checks
|
||||
elif [ "${OS}" == "eslint" ] || [ "${DIST}" == "eslint" ]; then
|
||||
|
||||
# Check we've got npm installed
|
||||
type npm 2>&1 > /dev/null
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo
|
||||
echo "ERROR: The script cannot find the required command \"npm\"."
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
npm install -g eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5
|
||||
echo "Begin eslint checks..."
|
||||
eslint --ext .php,.js .
|
||||
fi
|
||||
|
||||
|
|
|
@ -54,18 +54,18 @@ if ( 0 ) {
|
|||
SOL_SOCKET, // socket level
|
||||
SO_SNDTIMEO, // timeout option
|
||||
array(
|
||||
"sec"=>0, // Timeout in seconds
|
||||
"usec"=>500 // I assume timeout in microseconds
|
||||
'sec'=>0, // Timeout in seconds
|
||||
'usec'=>500 // I assume timeout in microseconds
|
||||
)
|
||||
);
|
||||
$new_stream = null;
|
||||
Info("Testing connection to " . $url_bits['host'].':'.$port);
|
||||
Info('Testing connection to '.$url_bits['host'].':'.$port);
|
||||
if ( socket_connect( $socket, $url_bits['host'], $port ) ) {
|
||||
$new_stream = $url_bits; // make a copy
|
||||
$new_stream['port'] = $port;
|
||||
} else {
|
||||
socket_close($socket);
|
||||
ZM\Info("No connection to ".$url_bits['host'] . " on port $port");
|
||||
ZM\Info('No connection to '.$url_bits['host'].' on port '.$port);
|
||||
continue;
|
||||
}
|
||||
if ( $new_stream ) {
|
||||
|
@ -103,7 +103,7 @@ Info("Testing connection to " . $url_bits['host'].':'.$port);
|
|||
$stream['url'] = unparse_url($stream, array('path'=>'/','query'=>'action=stream'));
|
||||
$monitors = ZM\Monitor::find(array('Path'=>$stream['url']));
|
||||
if ( count($monitors) ) {
|
||||
ZM\Info("Found monitors matching " . $stream['url'] );
|
||||
ZM\Info('Found monitors matching ' . $stream['url'] );
|
||||
$stream['Monitor'] = $monitors[0];
|
||||
if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) {
|
||||
$stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n";
|
||||
|
@ -155,7 +155,7 @@ if ( 0 ) {
|
|||
}
|
||||
|
||||
if ( ! $url_bits ) {
|
||||
ajaxError("The given URL was too malformed to parse.");
|
||||
ajaxError('The given URL was too malformed to parse.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -169,16 +169,16 @@ if ( 0 ) {
|
|||
|
||||
$file = $_FILES['import_file'];
|
||||
|
||||
if ($file["error"] > 0) {
|
||||
ajaxError($file["error"]);
|
||||
if ( $file['error'] > 0 ) {
|
||||
ajaxError($file['error']);
|
||||
return;
|
||||
} else {
|
||||
$filename = $file["name"];
|
||||
$filename = $file['name'];
|
||||
|
||||
$available_streams = array();
|
||||
$row = 1;
|
||||
if ( ($handle = fopen($file['tmp_name'], 'r')) !== FALSE ) {
|
||||
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
|
||||
while ( ($data = fgetcsv($handle, 1000, ',')) !== FALSE ) {
|
||||
$name = $data[0];
|
||||
$url = $data[1];
|
||||
$group = $data[2];
|
||||
|
@ -207,21 +207,17 @@ if ( 0 ) {
|
|||
fclose($handle);
|
||||
ajaxResponse(array('Streams'=>$available_streams));
|
||||
} else {
|
||||
ajaxError("Uploaded file does not exist");
|
||||
ajaxError('Uploaded file does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
} // end case import
|
||||
default:
|
||||
{
|
||||
ZM\Warning("unknown action " . $_REQUEST['action'] );
|
||||
} // end ddcase default
|
||||
}
|
||||
ZM\Warning('unknown action '.$_REQUEST['action']);
|
||||
} // end switch action
|
||||
} else {
|
||||
ZM\Warning("Cannot edit monitors" );
|
||||
ZM\Warning('Cannot edit monitors');
|
||||
}
|
||||
|
||||
ajaxError( 'Unrecognised action or insufficient permissions' );
|
||||
|
||||
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
|
||||
?>
|
||||
|
|
|
@ -12,7 +12,7 @@ if ( canEdit('Monitors') ) {
|
|||
$monitor_id = $monitor_ids[$i];
|
||||
$monitor_id = preg_replace('/^monitor_id-/', '', $monitor_id);
|
||||
if ( ( !$monitor_id ) or ! ( is_integer($monitor_id) or ctype_digit($monitor_id) ) ) {
|
||||
Warning("Got $monitor_id from " . $monitor_ids[$i]);
|
||||
Warning('Got '.$monitor_id.' from '.$monitor_ids[$i]);
|
||||
continue;
|
||||
}
|
||||
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id));
|
||||
|
@ -23,13 +23,11 @@ if ( canEdit('Monitors') ) {
|
|||
return;
|
||||
} // end case sort
|
||||
default:
|
||||
{
|
||||
ZM\Warning('unknown action '.$_REQUEST['action']);
|
||||
} // end ddcase default
|
||||
}
|
||||
} else {
|
||||
ZM\Warning('Cannot edit monitors');
|
||||
}
|
||||
|
||||
ajaxError('Unrecognised action or insufficient permissions');
|
||||
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
|
||||
?>
|
||||
|
|
|
@ -155,5 +155,5 @@ if ( canEdit('Events') ) {
|
|||
} // end switch action
|
||||
} // end if canEdit('Events')
|
||||
|
||||
ajaxError('Unrecognised action or insufficient permissions');
|
||||
ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']);
|
||||
?>
|
||||
|
|
|
@ -58,7 +58,7 @@ function buildLogQuery($action) {
|
|||
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
|
||||
|
||||
return array('sql'=>$sql, 'values'=>$values);
|
||||
}
|
||||
} # function buildLogQuery($action)
|
||||
|
||||
switch ( $_REQUEST['task'] ) {
|
||||
case 'create' :
|
||||
|
@ -70,14 +70,16 @@ switch ( $_REQUEST['task'] ) {
|
|||
$string = $_POST['message'];
|
||||
|
||||
$file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : '';
|
||||
if ( !empty( $_POST['line'] ) )
|
||||
if ( !empty( $_POST['line'] ) ) {
|
||||
$line = validInt($_POST['line']);
|
||||
else
|
||||
} else {
|
||||
$line = NULL;
|
||||
}
|
||||
|
||||
$levels = array_flip(ZM\Logger::$codes);
|
||||
if ( !isset($levels[$_POST['level']]) )
|
||||
if ( !isset($levels[$_POST['level']]) ) {
|
||||
ZM\Panic('Unexpected logger level '.$_POST['level']);
|
||||
}
|
||||
$level = $levels[$_POST['level']];
|
||||
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
|
||||
}
|
||||
|
@ -141,6 +143,10 @@ switch ( $_REQUEST['task'] ) {
|
|||
$logs[] = $log;
|
||||
}
|
||||
|
||||
foreach ( $options as $field => $values ) {
|
||||
asort($options[$field]);
|
||||
}
|
||||
|
||||
$available = count($logs);
|
||||
ajaxResponse( array(
|
||||
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG),
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<?php
|
||||
if ( $_REQUEST['entity'] == 'navBar' ) {
|
||||
$data = array();
|
||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$data['auth'] = generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||
$data['auth'] = $auth_hash;
|
||||
}
|
||||
}
|
||||
$data['message'] = getNavBarHtml('reload');
|
||||
ajaxResponse($data);
|
||||
|
@ -83,8 +86,8 @@ $statusData = array(
|
|||
'MinEventId' => array( 'sql' => '(SELECT min(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||
'MaxEventId' => array( 'sql' => '(SELECT max(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||
'TotalEvents' => array( 'sql' => '(SELECT count(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||
'Status' => array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -s' ),
|
||||
'FrameRate' => array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -f' ),
|
||||
'Status' => (isset($_REQUEST['id'])?array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -s' ):null),
|
||||
'FrameRate' => (isset($_REQUEST['id'])?array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -f' ):null),
|
||||
),
|
||||
),
|
||||
'events' => array(
|
||||
|
@ -204,6 +207,7 @@ function collectData() {
|
|||
$fieldSql = array();
|
||||
$joinSql = array();
|
||||
$groupSql = array();
|
||||
$values = array();
|
||||
|
||||
$elements = &$entitySpec['elements'];
|
||||
$lc_elements = array_change_key_case( $elements );
|
||||
|
@ -258,7 +262,6 @@ function collectData() {
|
|||
if ( $id && !empty($entitySpec['selector']) ) {
|
||||
$index = 0;
|
||||
$where = array();
|
||||
$values = array();
|
||||
foreach( $entitySpec['selector'] as $selIndex => $selector ) {
|
||||
$selectorParamName = ':selector' . $selIndex;
|
||||
if ( is_array( $selector ) ) {
|
||||
|
|
|
@ -86,10 +86,8 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
$numSockets = socket_select($rSockets, $wSockets, $eSockets, intval($timeout/1000), ($timeout%1000)*1000);
|
||||
|
||||
if ( $numSockets === false ) {
|
||||
ZM\Error('socket_select failed: ' . socket_strerror(socket_last_error()));
|
||||
ajaxError('socket_select failed: '.socket_strerror(socket_last_error()));
|
||||
} else if ( $numSockets < 0 ) {
|
||||
ZM\Error("Socket closed $remSockFile");
|
||||
ajaxError("Socket closed $remSockFile");
|
||||
} else if ( $numSockets == 0 ) {
|
||||
ZM\Error("Timed out waiting for msg $remSockFile");
|
||||
|
@ -97,7 +95,6 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
#ajaxError("Timed out waiting for msg $remSockFile");
|
||||
} else if ( $numSockets > 0 ) {
|
||||
if ( count($rSockets) != 1 ) {
|
||||
ZM\Error('Bogus return from select, '.count($rSockets).' sockets available');
|
||||
ajaxError('Bogus return from select, '.count($rSockets).' sockets available');
|
||||
}
|
||||
}
|
||||
|
@ -119,17 +116,17 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
switch ( $data['type'] ) {
|
||||
case MSG_DATA_WATCH :
|
||||
$data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg);
|
||||
ZM\Logger::Debug('FPS: ' . $data['fps']);
|
||||
$data['fps'] = round( $data['fps'], 2 );
|
||||
ZM\Logger::Debug('FPS: ' . $data['fps'] );
|
||||
$data['rate'] /= RATE_BASE;
|
||||
$data['delay'] = round( $data['delay'], 2 );
|
||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$time = time();
|
||||
// Regenerate auth hash after half the lifetime of the hash
|
||||
if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
|
||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||
$data['auth'] = $auth_hash;
|
||||
ZM\Logger::Debug("including nw auth hash " . $data['auth']);
|
||||
} else {
|
||||
ZM\Logger::Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash);
|
||||
}
|
||||
}
|
||||
ajaxResponse(array('status'=>$data));
|
||||
|
@ -144,11 +141,10 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
}
|
||||
$data['rate'] /= RATE_BASE;
|
||||
$data['zoom'] = round($data['zoom']/SCALE_BASE, 1);
|
||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$time = time();
|
||||
// Regenerate auth hash after half the lifetime of the hash
|
||||
if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
|
||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||
$data['auth'] = $auth_hash;
|
||||
}
|
||||
}
|
||||
ajaxResponse(array('status'=>$data));
|
||||
|
|
|
@ -15,6 +15,10 @@ class EventsController extends AppController {
|
|||
*/
|
||||
public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
|
||||
|
||||
public function beforeRender() {
|
||||
$this->set($this->Event->enumValues());
|
||||
}
|
||||
|
||||
public function beforeFilter() {
|
||||
parent::beforeFilter();
|
||||
global $user;
|
||||
|
|
|
@ -77,16 +77,24 @@ class GroupsController extends AppController {
|
|||
}
|
||||
|
||||
$this->Group->create();
|
||||
if ( $this->Group->save($this->request->data) ) {
|
||||
|
||||
if ( $this->request->data['Group']['MonitorIds'] and ! isset($this->request->data['Monitor']) ) {
|
||||
$this->request->data['Monitor'] = explode(',', $this->request->data['Group']['MonitorIds']);
|
||||
unset($this->request->data['Group']['MonitorIds']);
|
||||
}
|
||||
if ( $this->Group->saveAssociated($this->request->data, array('atomic'=>true)) ) {
|
||||
return $this->flash(
|
||||
__('The group has been saved.'),
|
||||
array('action' => 'index')
|
||||
);
|
||||
} else {
|
||||
ZM\Error("Failed to save Group");
|
||||
debug($this->Group->invalidFields());
|
||||
}
|
||||
}
|
||||
} # end if post
|
||||
$monitors = $this->Group->Monitor->find('list');
|
||||
$this->set(compact('monitors'));
|
||||
}
|
||||
} # end add
|
||||
|
||||
/**
|
||||
* edit method
|
||||
|
|
|
@ -50,8 +50,10 @@ class HostController extends AppController {
|
|||
$cred_depr = [];
|
||||
|
||||
if ( $username && $password ) {
|
||||
ZM\Logger::Debug('Username and password provided, generating access and refresh tokens');
|
||||
$cred = $this->_getCredentials(true, '', $username); // generate refresh
|
||||
} else {
|
||||
ZM\Logger::Debug('Only generating access token');
|
||||
$cred = $this->_getCredentials(false, $token); // don't generate refresh
|
||||
}
|
||||
|
||||
|
@ -69,6 +71,8 @@ class HostController extends AppController {
|
|||
$cred_depr = $this->_getCredentialsDeprecated();
|
||||
$login_array['credentials'] = $cred_depr[0];
|
||||
$login_array['append_password'] = $cred_depr[1];
|
||||
} else {
|
||||
ZM\Logger::Debug('Legacy Auth is disabled, not generating auth= credentials');
|
||||
}
|
||||
|
||||
$login_array['version'] = $ver[0];
|
||||
|
@ -108,8 +112,11 @@ class HostController extends AppController {
|
|||
|
||||
private function _getCredentials($generate_refresh_token=false, $token='', $username='') {
|
||||
|
||||
if ( !ZM_OPT_USE_AUTH )
|
||||
if ( !ZM_OPT_USE_AUTH ) {
|
||||
ZM\Error('OPT_USE_AUTH is turned off. Tokens will be null');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( !ZM_AUTH_HASH_SECRET )
|
||||
throw new ForbiddenException(__('Please create a valid AUTH_HASH_SECRET in ZoneMinder'));
|
||||
|
@ -136,7 +143,7 @@ class HostController extends AppController {
|
|||
}*/
|
||||
|
||||
$access_issued_at = time();
|
||||
$access_ttl = (ZM_AUTH_HASH_TTL || 2) * 3600;
|
||||
$access_ttl = max(ZM_AUTH_HASH_TTL,1) * 3600;
|
||||
|
||||
// by default access token will expire in 2 hrs
|
||||
// you can change it by changing the value of ZM_AUTH_HASH_TLL
|
||||
|
|
|
@ -44,33 +44,26 @@ class MonitorsController extends AppController {
|
|||
} else {
|
||||
$conditions = array();
|
||||
}
|
||||
|
||||
global $user;
|
||||
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
|
||||
if ( $allowedMonitors ) {
|
||||
$conditions['Monitor.Id' ] = $allowedMonitors;
|
||||
}
|
||||
$find_array = array('conditions'=>$conditions,'contain'=>array('Group'));
|
||||
|
||||
if ( isset($conditions['GroupId']) ) {
|
||||
$find_array['joins'] = array(
|
||||
$find_array = array(
|
||||
'conditions' => &$conditions,
|
||||
'contain' => array('Group'),
|
||||
'joins' => array(
|
||||
array(
|
||||
'table' => 'Groups_Monitors',
|
||||
'type' => 'inner',
|
||||
'type' => 'left',
|
||||
'conditions' => array(
|
||||
'Groups_Monitors.MonitorId = Monitor.Id'
|
||||
'Groups_Monitors.MonitorId = Monitor.Id',
|
||||
),
|
||||
),
|
||||
//array(
|
||||
//'table' => 'Groups',
|
||||
//'type' => 'inner',
|
||||
//'conditions' => array(
|
||||
//'Groups.Id = Groups_Monitors.GroupId',
|
||||
//'Groups.Id' => $this->request->params['GroupId'],
|
||||
//),
|
||||
//)
|
||||
),
|
||||
'group' => '`Monitor`.`Id`',
|
||||
);
|
||||
}
|
||||
$monitors = $this->Monitor->find('all',$find_array);
|
||||
$this->set(array(
|
||||
'monitors' => $monitors,
|
||||
|
@ -249,16 +242,11 @@ class MonitorsController extends AppController {
|
|||
// where C=on|off|status
|
||||
public function alarm() {
|
||||
$id = $this->request->params['named']['id'];
|
||||
$cmd = strtolower($this->request->params['named']['command']);
|
||||
if ( !$this->Monitor->exists($id) ) {
|
||||
throw new NotFoundException(__('Invalid monitor'));
|
||||
}
|
||||
if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status' ) {
|
||||
throw new BadRequestException(__('Invalid command'));
|
||||
}
|
||||
$zm_path_bin = Configure::read('ZM_PATH_BIN');
|
||||
$mToken = $this->request->query('token') ? $this->request->query('token') : null;
|
||||
|
||||
$cmd = strtolower($this->request->params['named']['command']);
|
||||
switch ($cmd) {
|
||||
case 'on':
|
||||
$q = '-a';
|
||||
|
@ -272,42 +260,43 @@ class MonitorsController extends AppController {
|
|||
$verbose = ''; // zmu has a bug - gives incorrect verbose output in this case
|
||||
$q = '-s';
|
||||
break;
|
||||
default :
|
||||
throw new BadRequestException(__('Invalid command'));
|
||||
}
|
||||
|
||||
// form auth key based on auth credentials
|
||||
$this->loadModel('Config');
|
||||
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH'));
|
||||
$config = $this->Config->find('first', $options);
|
||||
$zmOptAuth = $config['Config']['Value'];
|
||||
|
||||
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY'));
|
||||
$config = $this->Config->find('first', $options);
|
||||
$zmAuthRelay = $config['Config']['Value'];
|
||||
|
||||
$auth = '';
|
||||
|
||||
if ( $zmOptAuth ) {
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
global $user;
|
||||
$mToken = $this->request->query('token') ? $this->request->query('token') : $this->request->data('token');;
|
||||
if ( $mToken ) {
|
||||
$auth = ' -T '.$mToken;
|
||||
} else if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$auth = ' -A '.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
# Plain requires the plain text password which must either be in request or stored in session
|
||||
$password = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');;
|
||||
if ( !$password )
|
||||
$password = $this->request->query('password') ? $this->request->query('password') : $this->request->data('password');
|
||||
|
||||
if ( ! $password ) {
|
||||
# during auth the session will have been populated with the plaintext password
|
||||
$stateful = $this->request->query('stateful') ? $this->request->query('stateful') : $this->request->data('stateful');
|
||||
if ( $stateful ) {
|
||||
$password = $_SESSION['password'];
|
||||
}
|
||||
} else if ( $_COOKIE['ZMSESSID'] ) {
|
||||
$password = $_SESSION['password'];
|
||||
}
|
||||
elseif ( $zmAuthRelay == 'hashed' ) {
|
||||
$options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET'));
|
||||
$config = $this->Config->find('first', $options);
|
||||
$zmAuthHashSecret = $config['Config']['Value'];
|
||||
|
||||
$time = localtime();
|
||||
$ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5];
|
||||
$ak = md5($ak);
|
||||
$auth = ' -A '.$ak;
|
||||
} else if ( $zmAuthRelay == 'plain' ) {
|
||||
$auth = ' -U ' .$this->Session->Read('username').' -P '.$this->Session->Read('password');
|
||||
|
||||
} else if ( $zmAuthRelay == 'none' ) {
|
||||
$auth = ' -U ' .$this->Session->Read('username');
|
||||
$auth = ' -U ' .$user['Username'].' -P '.$password;
|
||||
} else if ( ZM_AUTH_RELAY == 'none' ) {
|
||||
$auth = ' -U ' .$user['Username'];
|
||||
}
|
||||
}
|
||||
|
||||
$shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth");
|
||||
$shellcmd = escapeshellcmd(ZM_PATH_BIN."/zmu $verbose -m$id $q $auth");
|
||||
$status = exec ($shellcmd);
|
||||
|
||||
$this->set(array(
|
||||
|
|
|
@ -17,12 +17,17 @@ class ServersController extends AppController {
|
|||
|
||||
public function beforeFilter() {
|
||||
parent::beforeFilter();
|
||||
/*
|
||||
* A user needs the server data to calculate how to view a monitor, and there really isn't anything sensitive in this data.
|
||||
* So it has been decided for now to just let everyone read it.
|
||||
|
||||
global $user;
|
||||
$canView = (!$user) || ($user['System'] != 'None');
|
||||
if ( !$canView ) {
|
||||
throw new UnauthorizedException(__('Insufficient Privileges'));
|
||||
return;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,7 +59,7 @@ class Group extends AppModel {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
public $hasMany = array(
|
||||
public $hasAndBelongsToMany = array(
|
||||
'Monitor' => array(
|
||||
'className' => 'Monitor',
|
||||
'joinTable' => 'Groups_Monitors',
|
||||
|
@ -77,4 +77,5 @@ class Group extends AppModel {
|
|||
'counterQuery' => ''
|
||||
),
|
||||
);
|
||||
var $actsAs = array( 'Containable' );
|
||||
}
|
||||
|
|
|
@ -128,12 +128,16 @@ class Control extends ZM_Object {
|
|||
$cmds['PresetHome'] = 'presetHome';
|
||||
|
||||
if ( $this->CanZoom() ) {
|
||||
if ( $this->CanZoomCon() )
|
||||
if ( $this->CanZoomCon() ) {
|
||||
$cmds['ZoomRoot'] = 'zoomCon';
|
||||
elseif ( $this->CanZoomRel() )
|
||||
} else if ( $this->CanZoomRel() ) {
|
||||
$cmds['ZoomRoot'] = 'zoomRel';
|
||||
elseif ( $this->CanZoomAbs() )
|
||||
} else if ( $this->CanZoomAbs() ) {
|
||||
$cmds['ZoomRoot'] = 'zoomAbs';
|
||||
} else {
|
||||
$cmds['ZoomRoot'] = '';
|
||||
Error('No zoom type selected. Please select Continuous, Relative, Absolute');
|
||||
}
|
||||
$cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele';
|
||||
$cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide';
|
||||
$cmds['ZoomStop'] = 'zoomStop';
|
||||
|
@ -142,12 +146,16 @@ class Control extends ZM_Object {
|
|||
}
|
||||
|
||||
if ( $this->CanFocus() ) {
|
||||
if ( $this->CanFocusCon() )
|
||||
if ( $this->CanFocusCon() ) {
|
||||
$cmds['FocusRoot'] = 'focusCon';
|
||||
elseif ( $this->CanFocusRel() )
|
||||
} else if ( $this->CanFocusRel() ) {
|
||||
$cmds['FocusRoot'] = 'focusRel';
|
||||
elseif ( $this->CanFocusAbs() )
|
||||
} else if ( $this->CanFocusAbs() ) {
|
||||
$cmds['FocusRoot'] = 'focusAbs';
|
||||
} else {
|
||||
$cmds['FocusRoot'] = '';
|
||||
Error('No focus type selected. Please select Continuous, Relative, Absolute');
|
||||
}
|
||||
$cmds['FocusFar'] = $cmds['FocusRoot'].'Far';
|
||||
$cmds['FocusNear'] = $cmds['FocusRoot'].'Near';
|
||||
$cmds['FocusStop'] = 'focusStop';
|
||||
|
@ -156,12 +164,16 @@ class Control extends ZM_Object {
|
|||
}
|
||||
|
||||
if ( $this->CanIris() ) {
|
||||
if ( $this->CanIrisCon() )
|
||||
if ( $this->CanIrisCon() ) {
|
||||
$cmds['IrisRoot'] = 'irisCon';
|
||||
elseif ( $this->CanIrisRel() )
|
||||
} else if ( $this->CanIrisRel() ) {
|
||||
$cmds['IrisRoot'] = 'irisRel';
|
||||
elseif ( $this->CanIrisAbs() )
|
||||
} else if ( $this->CanIrisAbs() ) {
|
||||
$cmds['IrisRoot'] = 'irisAbs';
|
||||
} else {
|
||||
$cmds['IrisRoot'] = '';
|
||||
Error('No iris type selected. Please select Continuous, Relative, Absolute');
|
||||
}
|
||||
$cmds['IrisOpen'] = $cmds['IrisRoot'].'Open';
|
||||
$cmds['IrisClose'] = $cmds['IrisRoot'].'Close';
|
||||
$cmds['IrisStop'] = 'irisStop';
|
||||
|
@ -170,12 +182,16 @@ class Control extends ZM_Object {
|
|||
}
|
||||
|
||||
if ( $this->CanWhite() ) {
|
||||
if ( $this->CanWhiteCon() )
|
||||
if ( $this->CanWhiteCon() ) {
|
||||
$cmds['WhiteRoot'] = 'whiteCon';
|
||||
elseif ( $this->CanWhiteRel() )
|
||||
} else if ( $this->CanWhiteRel() ) {
|
||||
$cmds['WhiteRoot'] = 'whiteRel';
|
||||
elseif ( $this->CanWhiteAbs() )
|
||||
} else if ( $this->CanWhiteAbs() ) {
|
||||
$cmds['WhiteRoot'] = 'whiteAbs';
|
||||
} else {
|
||||
Error('No White type selected. Please select Continuous, Relative, Absolute');
|
||||
$cmds['WhiteRoot'] = '';
|
||||
}
|
||||
$cmds['WhiteIn'] = $cmds['WhiteRoot'].'In';
|
||||
$cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out';
|
||||
$cmds['WhiteAuto'] = 'whiteAuto';
|
||||
|
@ -183,12 +199,16 @@ class Control extends ZM_Object {
|
|||
}
|
||||
|
||||
if ( $this->CanGain() ) {
|
||||
if ( $this->CanGainCon() )
|
||||
if ( $this->CanGainCon() ) {
|
||||
$cmds['GainRoot'] = 'gainCon';
|
||||
elseif ( $this->CanGainRel() )
|
||||
} else if ( $this->CanGainRel() ) {
|
||||
$cmds['GainRoot'] = 'gainRel';
|
||||
elseif ( $this->CanGainAbs() )
|
||||
} else if ( $this->CanGainAbs() ) {
|
||||
$cmds['GainRoot'] = 'gainAbs';
|
||||
} else {
|
||||
Error('No Gain type selected');
|
||||
$cmds['GainRoot'] = '';
|
||||
}
|
||||
$cmds['GainUp'] = $cmds['GainRoot'].'Up';
|
||||
$cmds['GainDown'] = $cmds['GainRoot'].'Down';
|
||||
$cmds['GainAuto'] = 'gainAuto';
|
||||
|
@ -207,6 +227,7 @@ class Control extends ZM_Object {
|
|||
$cmds['Center'] = $cmds['PresetHome'];
|
||||
} else {
|
||||
$cmds['MoveRoot'] = '';
|
||||
Error('No move type selected. Please select Continuous, Relative, Absolute');
|
||||
}
|
||||
|
||||
$cmds['MoveUp'] = $cmds['MoveRoot'].'Up';
|
||||
|
|
|
@ -47,14 +47,18 @@ class Event extends ZM_Object {
|
|||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
public static function clear_cache() {
|
||||
return ZM_Object::_clear_cache(get_class());
|
||||
}
|
||||
|
||||
public function Storage( $new = null ) {
|
||||
if ( $new ) {
|
||||
$this->{'Storage'} = $new;
|
||||
}
|
||||
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) {
|
||||
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) {
|
||||
if ( isset($this->{'StorageId'}) and $this->{'StorageId'} )
|
||||
$this->{'Storage'} = Storage::find_one(array('Id'=>$this->{'StorageId'}));
|
||||
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) )
|
||||
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) )
|
||||
$this->{'Storage'} = new Storage(NULL);
|
||||
}
|
||||
return $this->{'Storage'};
|
||||
|
@ -64,10 +68,10 @@ class Event extends ZM_Object {
|
|||
if ( $new ) {
|
||||
$this->{'SecondaryStorage'} = $new;
|
||||
}
|
||||
if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) ) {
|
||||
if ( ! ( property_exists($this, 'SecondaryStorage') and $this->{'SecondaryStorage'} ) ) {
|
||||
if ( isset($this->{'SecondaryStorageId'}) and $this->{'SecondaryStorageId'} )
|
||||
$this->{'SecondaryStorage'} = Storage::find_one(array('Id'=>$this->{'SecondaryStorageId'}));
|
||||
if ( ! ( array_key_exists('SecondaryStorage', $this) and $this->{'SecondaryStorage'} ) )
|
||||
if ( ! ( property_exists($this, 'SecondaryStorage') and $this->{'SecondaryStorage'} ) )
|
||||
$this->{'SecondaryStorage'} = new Storage(NULL);
|
||||
}
|
||||
return $this->{'SecondaryStorage'};
|
||||
|
@ -262,7 +266,7 @@ class Event extends ZM_Object {
|
|||
if ( is_null($new) or ( $new != '' ) ) {
|
||||
$this->{'DiskSpace'} = $new;
|
||||
}
|
||||
if ( (!array_key_exists('DiskSpace',$this)) or (null === $this->{'DiskSpace'}) ) {
|
||||
if ( (!property_exists($this, 'DiskSpace')) or (null === $this->{'DiskSpace'}) ) {
|
||||
$this->{'DiskSpace'} = folder_size($this->Path());
|
||||
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
|
||||
}
|
||||
|
@ -298,7 +302,7 @@ class Event extends ZM_Object {
|
|||
} // end function createListThumbnail
|
||||
|
||||
function ThumbnailWidth( ) {
|
||||
if ( ! ( array_key_exists('ThumbnailWidth', $this) ) ) {
|
||||
if ( ! ( property_exists($this, 'ThumbnailWidth') ) ) {
|
||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
||||
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
||||
|
@ -315,7 +319,7 @@ class Event extends ZM_Object {
|
|||
} // end function ThumbnailWidth
|
||||
|
||||
function ThumbnailHeight( ) {
|
||||
if ( ! ( array_key_exists('ThumbnailHeight', $this) ) ) {
|
||||
if ( ! ( property_exists($this, 'ThumbnailHeight') ) ) {
|
||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
||||
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
||||
|
@ -413,26 +417,22 @@ class Event extends ZM_Object {
|
|||
}
|
||||
} // end if capture file exists
|
||||
} // end if analyze file exists
|
||||
}
|
||||
} // end if frame or snapshot
|
||||
|
||||
$captPath = $eventPath.'/'.$captImage;
|
||||
if ( ! file_exists($captPath) ) {
|
||||
Error("Capture file does not exist at $captPath");
|
||||
}
|
||||
|
||||
//echo "CI:$captImage, CP:$captPath, TCP:$captPath<br>";
|
||||
|
||||
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']);
|
||||
$analPath = $eventPath.'/'.$analImage;
|
||||
|
||||
//echo "AI:$analImage, AP:$analPath, TAP:$analPath<br>";
|
||||
|
||||
$alarmFrame = $frame['Type']=='Alarm';
|
||||
|
||||
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
||||
$isAnalImage = $hasAnalImage && !$captureOnly;
|
||||
|
||||
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists('imagecreatefromjpeg') ) {
|
||||
if ( !ZM_WEB_SCALE_THUMBS || ($scale >= SCALE_BASE) || !function_exists('imagecreatefromjpeg') ) {
|
||||
$imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath;
|
||||
$imageFile = $imagePath;
|
||||
$thumbFile = $thumbPath;
|
||||
|
@ -508,10 +508,11 @@ class Event extends ZM_Object {
|
|||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url = '?pass='.$_SESSION['password'];
|
||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
$url .= '?pass='.$_SESSION['password'];
|
||||
} else {
|
||||
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||
return;
|
||||
}
|
||||
}
|
||||
Logger::Debug("sending command to $url");
|
||||
|
@ -555,10 +556,11 @@ class Event extends ZM_Object {
|
|||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url = '?pass='.$_SESSION['password'];
|
||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
$url .= '?pass='.$_SESSION['password'];
|
||||
} else {
|
||||
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||
return;
|
||||
}
|
||||
}
|
||||
Logger::Debug("sending command to $url");
|
||||
|
|
|
@ -11,6 +11,9 @@ class Filter extends ZM_Object {
|
|||
'AutoExecute' => 0,
|
||||
'AutoExecuteCmd' => 0,
|
||||
'AutoEmail' => 0,
|
||||
'EmailTo' => '',
|
||||
'EmailSubject' => '',
|
||||
'EmailBody' => '',
|
||||
'AutoDelete' => 0,
|
||||
'AutoArchive' => 0,
|
||||
'AutoVideo' => 0,
|
||||
|
@ -39,8 +42,8 @@ class Filter extends ZM_Object {
|
|||
$this->{'Query'} = func_get_arg(0);;
|
||||
$this->{'Query_json'} = jsonEncode($this->{'Query'});
|
||||
}
|
||||
if ( !array_key_exists('Query', $this) ) {
|
||||
if ( array_key_exists('Query_json', $this) and $this->{'Query_json'} ) {
|
||||
if ( !property_exists($this, 'Query') ) {
|
||||
if ( property_exists($this, 'Query_json') and $this->{'Query_json'} ) {
|
||||
$this->{'Query'} = jsonDecode($this->{'Query_json'});
|
||||
} else {
|
||||
$this->{'Query'} = array();
|
||||
|
@ -115,13 +118,17 @@ class Filter extends ZM_Object {
|
|||
|
||||
public function control($command, $server_id=null) {
|
||||
$Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find(array('Status'=>'Running'));
|
||||
if ( !count($Servers) and !$server_id ) {
|
||||
if ( !count($Servers) ) {
|
||||
if ( !$server_id ) {
|
||||
# This will be the non-multi-server case
|
||||
$Servers = array(new Server());
|
||||
} else {
|
||||
Warning("Server not found for id $server_id");
|
||||
}
|
||||
}
|
||||
foreach ( $Servers as $Server ) {
|
||||
|
||||
if ( !defined('ZM_SERVER_ID') or !$Server->Id() or ZM_SERVER_ID==$Server->Id() ) {
|
||||
if ( (!defined('ZM_SERVER_ID')) or (!$Server->Id()) or (ZM_SERVER_ID==$Server->Id()) ) {
|
||||
# Local
|
||||
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
|
||||
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
|
||||
|
@ -139,7 +146,7 @@ class Filter extends ZM_Object {
|
|||
$url = '?user='.$_SESSION['username'];
|
||||
}
|
||||
}
|
||||
$url .= '&view=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id();
|
||||
$url .= '&view=filter&object=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id();
|
||||
Logger::Debug("sending command to $url");
|
||||
$data = array();
|
||||
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
||||
|
|
|
@ -18,7 +18,7 @@ class Group extends ZM_Object {
|
|||
}
|
||||
|
||||
public function delete() {
|
||||
if ( array_key_exists('Id', $this) ) {
|
||||
if ( property_exists($this, 'Id') ) {
|
||||
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}));
|
||||
dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'}));
|
||||
dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'}));
|
||||
|
@ -35,7 +35,7 @@ class Group extends ZM_Object {
|
|||
if ( isset($new) ) {
|
||||
$this->{'depth'} = $new;
|
||||
}
|
||||
if ( !array_key_exists('depth', $this) or ($this->{'depth'} === null) ) {
|
||||
if ( !property_exists($this, 'depth') or ($this->{'depth'} === null) ) {
|
||||
$this->{'depth'} = 0;
|
||||
if ( $this->{'ParentId'} != null ) {
|
||||
$Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
|
||||
|
@ -46,7 +46,7 @@ class Group extends ZM_Object {
|
|||
} // end public function depth
|
||||
|
||||
public function MonitorIds( ) {
|
||||
if ( ! array_key_exists('MonitorIds', $this) ) {
|
||||
if ( ! property_exists($this, 'MonitorIds') ) {
|
||||
$this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}));
|
||||
}
|
||||
return $this->{'MonitorIds'};
|
||||
|
|
|
@ -12,6 +12,7 @@ class Monitor extends ZM_Object {
|
|||
protected $defaults = array(
|
||||
'Id' => null,
|
||||
'Name' => '',
|
||||
'Notes' => '',
|
||||
'ServerId' => 0,
|
||||
'StorageId' => 0,
|
||||
'Type' => 'Ffmpeg',
|
||||
|
@ -45,6 +46,7 @@ protected $defaults = array(
|
|||
'SaveJPEGs' => 3,
|
||||
'VideoWriter' => '0',
|
||||
'OutputCodec' => null,
|
||||
'Encoder' => 'auto',
|
||||
'OutputContainer' => null,
|
||||
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||
'RecordAudio' => array('type'=>'boolean', 'default'=>0),
|
||||
|
@ -58,10 +60,10 @@ protected $defaults = array(
|
|||
'LabelX' => 0,
|
||||
'LabelY' => 0,
|
||||
'LabelSize' => 1,
|
||||
'ImageBufferCount' => 100,
|
||||
'ImageBufferCount' => 20,
|
||||
'WarmupCount' => 0,
|
||||
'PreEventCount' => 0,
|
||||
'PostEventCount' => 0,
|
||||
'PreEventCount' => 5,
|
||||
'PostEventCount' => 5,
|
||||
'StreamReplayBuffer' => 0,
|
||||
'AlarmFrameCount' => 1,
|
||||
'SectionLength' => 600,
|
||||
|
@ -69,10 +71,6 @@ protected $defaults = array(
|
|||
'FrameSkip' => 0,
|
||||
'MotionFrameSkip' => 0,
|
||||
'AnalysisFPSLimit' => null,
|
||||
'OutputCodec' => '0',
|
||||
'Encoder' => 'auto',
|
||||
'OutputContainer' => 'auto',
|
||||
'Triggers' => null,
|
||||
'AnalysisUpdateDelay' => 0,
|
||||
'MaxFPS' => null,
|
||||
'AlarmMaxFPS' => null,
|
||||
|
@ -92,7 +90,7 @@ protected $defaults = array(
|
|||
'DefaultScale' => 100,
|
||||
'SignalCheckPoints' => 0,
|
||||
'SignalCheckColour' => '#0000BE',
|
||||
'WebColour' => 'red',
|
||||
'WebColour' => '#ff0000',
|
||||
'Exif' => array('type'=>'boolean','default'=>0),
|
||||
'Sequence' => null,
|
||||
'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||
|
@ -120,18 +118,25 @@ private $status_fields = array(
|
|||
);
|
||||
|
||||
public function Control() {
|
||||
if ( !array_key_exists('Control', $this) ) {
|
||||
if ( !property_exists($this, 'Control') ) {
|
||||
if ( $this->ControlId() )
|
||||
$this->{'Control'} = Control::find_one(array('Id'=>$this->{'ControlId'}));
|
||||
|
||||
if ( !(array_key_exists('Control', $this) and $this->{'Control'}) )
|
||||
if ( !(property_exists($this, 'Control') and $this->{'Control'}) )
|
||||
$this->{'Control'} = new Control();
|
||||
}
|
||||
return $this->{'Control'};
|
||||
}
|
||||
|
||||
public function Server() {
|
||||
return new Server($this->{'ServerId'});
|
||||
if ( !property_exists($this, 'Server') ) {
|
||||
if ( $this->ServerId() )
|
||||
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
||||
if ( !property_exists($this, 'Server') ) {
|
||||
$this->{'Server'} = new Server();
|
||||
}
|
||||
}
|
||||
return $this->{'Server'};
|
||||
}
|
||||
|
||||
public function __call($fn, array $args){
|
||||
|
@ -142,7 +147,7 @@ private $status_fields = array(
|
|||
$this->{$fn} = $args[0];
|
||||
}
|
||||
}
|
||||
if ( array_key_exists($fn, $this) ) {
|
||||
if ( property_exists($this, $fn) ) {
|
||||
return $this->{$fn};
|
||||
} else if ( array_key_exists($fn, $this->defaults) ) {
|
||||
if ( is_array($this->defaults[$fn]) ) {
|
||||
|
@ -215,9 +220,9 @@ private $status_fields = array(
|
|||
$this->{'Width'} = $new;
|
||||
|
||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Height' : 'Width';
|
||||
if ( array_key_exists($field, $this) )
|
||||
if ( property_exists($this, $field) )
|
||||
return $this->{$field};
|
||||
return $this->defaults{$field};
|
||||
return $this->defaults[$field];
|
||||
} // end function Width
|
||||
|
||||
public function ViewHeight($new=null) {
|
||||
|
@ -225,9 +230,9 @@ private $status_fields = array(
|
|||
$this->{'Height'} = $new;
|
||||
|
||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Width' : 'Height';
|
||||
if ( array_key_exists($field, $this) )
|
||||
if ( property_exists($this, $field) )
|
||||
return $this->{$field};
|
||||
return $this->defaults{$field};
|
||||
return $this->defaults[$field];
|
||||
} // end function Height
|
||||
|
||||
public function SignalCheckColour($new=null) {
|
||||
|
@ -238,10 +243,10 @@ private $status_fields = array(
|
|||
|
||||
// Validate that it's a valid colour (we seem to allow color names, not just hex).
|
||||
// This also helps prevent XSS.
|
||||
if (array_key_exists($field, $this) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) {
|
||||
if ( property_exists($this, $field) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) {
|
||||
return $this->{$field};
|
||||
}
|
||||
return $this->defaults{$field};
|
||||
return $this->defaults[$field];
|
||||
} // end function SignalCheckColour
|
||||
|
||||
public static function find( $parameters = array(), $options = array() ) {
|
||||
|
@ -257,7 +262,7 @@ private $status_fields = array(
|
|||
Warning('Attempt to control a monitor with no Id');
|
||||
return;
|
||||
}
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ( $this->Type() == 'Local' ) {
|
||||
$zmcArgs = '-d '.$this->{'Device'};
|
||||
} else {
|
||||
|
@ -282,10 +287,11 @@ private $status_fields = array(
|
|||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url = '?pass='.$_SESSION['password'];
|
||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
$url .= '?pass='.$_SESSION['password'];
|
||||
} else {
|
||||
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||
return;
|
||||
}
|
||||
}
|
||||
Logger::Debug("sending command to $url");
|
||||
|
@ -310,7 +316,7 @@ private $status_fields = array(
|
|||
return;
|
||||
}
|
||||
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) {
|
||||
if ( ZM_OPT_CONTROL ) {
|
||||
daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'});
|
||||
|
@ -340,10 +346,11 @@ private $status_fields = array(
|
|||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url = '?pass='.$_SESSION['password'];
|
||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
$url .= '?pass='.$_SESSION['password'];
|
||||
} else {
|
||||
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||
return;
|
||||
}
|
||||
}
|
||||
Logger::Debug("sending command to $url");
|
||||
|
@ -371,8 +378,8 @@ private $status_fields = array(
|
|||
}
|
||||
}
|
||||
|
||||
if ( !array_key_exists('GroupIds', $this) ) {
|
||||
if ( array_key_exists('Id', $this) and $this->{'Id'} ) {
|
||||
if ( !property_exists($this, 'GroupIds') ) {
|
||||
if ( property_exists($this, 'Id') and $this->{'Id'} ) {
|
||||
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) );
|
||||
if ( ! $this->{'GroupIds'} )
|
||||
$this->{'GroupIds'} = array();
|
||||
|
@ -421,7 +428,7 @@ private $status_fields = array(
|
|||
if ( $new ) {
|
||||
$this->{'Storage'} = $new;
|
||||
}
|
||||
if ( ! ( array_key_exists('Storage', $this) and $this->{'Storage'} ) ) {
|
||||
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) {
|
||||
$this->{'Storage'} = isset($this->{'StorageId'}) ?
|
||||
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
|
||||
new Storage(NULL);
|
||||
|
@ -471,8 +478,8 @@ private $status_fields = array(
|
|||
return $source;
|
||||
} // end function Source
|
||||
|
||||
public function UrlToIndex() {
|
||||
return $this->Server()->UrlToIndex();
|
||||
public function UrlToIndex($port=null) {
|
||||
return $this->Server()->UrlToIndex($port);
|
||||
//ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
||||
}
|
||||
|
||||
|
@ -497,7 +504,7 @@ public function sendControlCommand($command) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( array_key_exists('ServerId', $this) and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
# Local
|
||||
Logger::Debug('Trying to send options ' . print_r($options, true));
|
||||
|
||||
|
|
|
@ -1,133 +1,22 @@
|
|||
<?php
|
||||
namespace ZM;
|
||||
require_once('Object.php');
|
||||
|
||||
require_once('database.php');
|
||||
|
||||
class MontageLayout {
|
||||
|
||||
private $defaults = array(
|
||||
class MontageLayout extends ZM_Object {
|
||||
protected static $table = 'MontageLayouts';
|
||||
protected $defaults = array(
|
||||
'Id' => null,
|
||||
'Name' => '',
|
||||
'Positions' => 0,
|
||||
);
|
||||
|
||||
public function __construct( $IdOrRow = NULL ) {
|
||||
if ( $IdOrRow ) {
|
||||
$row = NULL;
|
||||
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
|
||||
$row = dbFetchOne( 'SELECT * FROM MontageLayouts WHERE Id=?', NULL, array( $IdOrRow ) );
|
||||
if ( ! $row ) {
|
||||
Error("Unable to load MontageLayout record for Id=" . $IdOrRow );
|
||||
}
|
||||
} else if ( is_array( $IdOrRow ) ) {
|
||||
$row = $IdOrRow;
|
||||
} else {
|
||||
Error("Unknown argument passed to MontageLayout Constructor ($IdOrRow)");
|
||||
return;
|
||||
public static function find( $parameters = array(), $options = array() ) {
|
||||
return ZM_Object::_find(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
if ( $row ) {
|
||||
foreach ($row as $k => $v) {
|
||||
$this->{$k} = $v;
|
||||
public static function find_one( $parameters = array(), $options = array() ) {
|
||||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||
}
|
||||
} else {
|
||||
Error('No row for MontageLayout ' . $IdOrRow );
|
||||
}
|
||||
} # end if isset($IdOrRow)
|
||||
} // end function __construct
|
||||
|
||||
public function __call($fn, array $args){
|
||||
if ( count($args) ) {
|
||||
$this->{$fn} = $args[0];
|
||||
}
|
||||
if ( array_key_exists($fn, $this) ) {
|
||||
return $this->{$fn};
|
||||
} else {
|
||||
if ( array_key_exists( $fn, $this->defaults ) ) {
|
||||
return $this->defaults{$fn};
|
||||
} else {
|
||||
$backTrace = debug_backtrace();
|
||||
$file = $backTrace[1]['file'];
|
||||
$line = $backTrace[1]['line'];
|
||||
Warning( "Unknown function call MontageLayout->$fn from $file:$line" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function set( $data ) {
|
||||
foreach ($data as $k => $v) {
|
||||
if ( is_array( $v ) ) {
|
||||
# perhaps should turn into a comma-separated string
|
||||
$this->{$k} = implode(',',$v);
|
||||
} else if ( is_string( $v ) ) {
|
||||
$this->{$k} = trim( $v );
|
||||
} else if ( is_integer( $v ) ) {
|
||||
$this->{$k} = $v;
|
||||
} else if ( is_bool( $v ) ) {
|
||||
$this->{$k} = $v;
|
||||
} else {
|
||||
Error( "Unknown type $k => $v of var " . gettype( $v ) );
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static function find( $parameters = null, $options = null ) {
|
||||
$filters = array();
|
||||
$sql = 'SELECT * FROM MontageLayouts ';
|
||||
$values = array();
|
||||
|
||||
if ( $parameters ) {
|
||||
$fields = array();
|
||||
$sql .= 'WHERE ';
|
||||
foreach ( $parameters as $field => $value ) {
|
||||
if ( $value == null ) {
|
||||
$fields[] = $field.' IS NULL';
|
||||
} else if ( is_array( $value ) ) {
|
||||
$func = function(){return '?';};
|
||||
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
|
||||
$values += $value;
|
||||
|
||||
} else {
|
||||
$fields[] = $field.'=?';
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
$sql .= implode(' AND ', $fields );
|
||||
}
|
||||
if ( $options and isset($options['order']) ) {
|
||||
$sql .= ' ORDER BY ' . $options['order'];
|
||||
}
|
||||
$result = dbQuery($sql, $values);
|
||||
if ( $result ) {
|
||||
$results = $result->fetchALL();
|
||||
foreach ( $results as $row ) {
|
||||
$filters[] = new MontageLayout($row);
|
||||
}
|
||||
}
|
||||
return $filters;
|
||||
}
|
||||
public function save( $new_values = null ) {
|
||||
if ( $new_values ) {
|
||||
foreach ( $new_values as $k=>$v ) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$fields = array_values( array_filter( array_keys($this->defaults), function($field){return $field != 'Id';} ) );
|
||||
$values = null;
|
||||
if ( isset($this->{'Id'}) ) {
|
||||
$sql = 'UPDATE MontageLayouts SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?';
|
||||
$values = array_map( function($field){return $this->{$field};}, $fields );
|
||||
$values[] = $this->{'Id'};
|
||||
dbQuery($sql, $values);
|
||||
} else {
|
||||
$sql = 'INSERT INTO MontageLayouts ('.implode( ',', $fields ).') VALUES ('.implode(',',array_map( function(){return '?';}, $fields ) ).')';
|
||||
$values = array_map( function($field){return $this->{$field};}, $fields );
|
||||
dbQuery($sql, $values);
|
||||
global $dbConn;
|
||||
$this->{'Id'} = $dbConn->lastInsertId();
|
||||
}
|
||||
} // end function save
|
||||
|
||||
} // end class MontageLayout
|
||||
?>
|
||||
|
|
|
@ -45,7 +45,7 @@ class ZM_Object {
|
|||
$this->{$fn} = $args[0];
|
||||
}
|
||||
|
||||
if ( array_key_exists($fn, $this) ) {
|
||||
if ( property_exists($this, $fn) ) {
|
||||
return $this->{$fn};
|
||||
} else {
|
||||
if ( array_key_exists($fn, $this->defaults) ) {
|
||||
|
@ -110,8 +110,9 @@ class ZM_Object {
|
|||
|
||||
public static function _find_one($class, $parameters = array(), $options = array() ) {
|
||||
global $object_cache;
|
||||
if ( ! isset($object_cache[$class]) )
|
||||
if ( ! isset($object_cache[$class]) ) {
|
||||
$object_cache[$class] = array();
|
||||
}
|
||||
$cache = &$object_cache[$class];
|
||||
if (
|
||||
( count($parameters) == 1 ) and
|
||||
|
@ -127,6 +128,11 @@ class ZM_Object {
|
|||
return $results[0];
|
||||
}
|
||||
|
||||
public static function _clear_cache($class) {
|
||||
global $object_cache;
|
||||
$object_cache[$class] = array();
|
||||
}
|
||||
|
||||
public static function Objects_Indexed_By_Id($class) {
|
||||
$results = array();
|
||||
foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) {
|
||||
|
@ -140,10 +146,10 @@ class ZM_Object {
|
|||
foreach ($this->defaults as $key => $value) {
|
||||
if ( is_callable(array($this, $key)) ) {
|
||||
$json[$key] = $this->$key();
|
||||
} else if ( array_key_exists($key, $this) ) {
|
||||
} else if ( property_exists($this, $key) ) {
|
||||
$json[$key] = $this->{$key};
|
||||
} else {
|
||||
$json[$key] = $this->defaults{$key};
|
||||
$json[$key] = $this->defaults[$key];
|
||||
}
|
||||
}
|
||||
return json_encode($json);
|
||||
|
@ -158,14 +164,24 @@ class ZM_Object {
|
|||
# perhaps should turn into a comma-separated string
|
||||
$this->{$k} = implode(',', $v);
|
||||
} else if ( is_string($v) ) {
|
||||
if ( $v == '' and array_key_exists($k, $this->defaults) ) {
|
||||
if ( is_array($this->defaults[$k]) )
|
||||
if ( 0 ) {
|
||||
# Remarking this out. We are setting a value, not asking for a default to be set.
|
||||
# So don't do defaults here, do them somewhere else
|
||||
if ( ($v == null) and array_key_exists($k, $this->defaults) ) {
|
||||
Logger::Debug("$k => Have default for $v: ");
|
||||
if ( is_array($this->defaults[$k]) ) {
|
||||
$this->{$k} = $this->defaults[$k]['default'];
|
||||
else
|
||||
} else {
|
||||
$this->{$k} = $this->defaults[$k];
|
||||
Logger::Debug("$k => Have default for $v: " . $this->{$k});
|
||||
}
|
||||
} else {
|
||||
$this->{$k} = trim($v);
|
||||
}
|
||||
} else {
|
||||
$this->{$k} = trim($v);
|
||||
}
|
||||
|
||||
} else if ( is_integer($v) ) {
|
||||
$this->{$k} = $v;
|
||||
} else if ( is_bool($v) ) {
|
||||
|
@ -192,7 +208,7 @@ class ZM_Object {
|
|||
continue;
|
||||
}
|
||||
|
||||
if ( $this->defaults[$field] ) {
|
||||
if ( isset($this->defaults[$field]) ) {
|
||||
if ( is_array($this->defaults[$field]) ) {
|
||||
$new_values[$field] = $this->defaults[$field]['default'];
|
||||
} else {
|
||||
|
@ -215,7 +231,7 @@ class ZM_Object {
|
|||
} else if ( $this->$field() != $value ) {
|
||||
$changes[$field] = $value;
|
||||
}
|
||||
} else if ( array_key_exists($field, $this) ) {
|
||||
} else if ( property_exists($this, $field) ) {
|
||||
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
||||
Logger::Debug("Checking field $field => current ".
|
||||
(is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' .
|
||||
|
@ -280,6 +296,18 @@ class ZM_Object {
|
|||
$this->set($new_values);
|
||||
}
|
||||
|
||||
# Set defaults. Note that we only replace "" with null, not other values
|
||||
# because for example if we want to clear TimestampFormat, we clear it, but the default is a string value
|
||||
foreach ( $this->defaults as $field => $default ) {
|
||||
if ( (!property_exists($this, $field)) or ($this->{$field} == '') ) {
|
||||
if ( is_array($default) ) {
|
||||
$this->{$field} = $default['default'];
|
||||
} else if ( $default == null ) {
|
||||
$this->{$field} = $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fields = array_filter(
|
||||
$this->defaults,
|
||||
function($v) {
|
||||
|
|
|
@ -80,7 +80,7 @@ class Server extends ZM_Object {
|
|||
|
||||
public function PathToZMS( $new = null ) {
|
||||
if ( $new != null )
|
||||
$this{'PathToZMS'} = $new;
|
||||
$this->{'PathToZMS'} = $new;
|
||||
if ( $this->Id() and $this->{'PathToZMS'} ) {
|
||||
return $this->{'PathToZMS'};
|
||||
} else {
|
||||
|
|
|
@ -58,6 +58,13 @@ class Storage extends ZM_Object {
|
|||
return $this->{'Events'};
|
||||
}
|
||||
|
||||
public function EventCount() {
|
||||
if ( (! property_exists($this, 'EventCount')) or (!$this->{'EventCount'}) ) {
|
||||
$this->{'EventCount'} = dbFetchOne('SELECT COUNT(*) AS EventCount FROM Events WHERE StorageId=?', 'EventCount', array($this->Id()));
|
||||
}
|
||||
return $this->{'EventCount'};
|
||||
}
|
||||
|
||||
public function disk_usage_percent() {
|
||||
$path = $this->Path();
|
||||
if ( ! $path ) {
|
||||
|
@ -80,7 +87,7 @@ class Storage extends ZM_Object {
|
|||
}
|
||||
|
||||
public function disk_total_space() {
|
||||
if ( !array_key_exists('disk_total_space', $this) ) {
|
||||
if ( !property_exists($this, 'disk_total_space') ) {
|
||||
$path = $this->Path();
|
||||
if ( file_exists($path) ) {
|
||||
$this->{'disk_total_space'} = disk_total_space($path);
|
||||
|
@ -94,7 +101,7 @@ class Storage extends ZM_Object {
|
|||
|
||||
public function disk_used_space() {
|
||||
# This isn't a function like this in php, so we have to add up the space used in each event.
|
||||
if ( ( !array_key_exists('disk_used_space', $this)) or !$this->{'disk_used_space'} ) {
|
||||
if ( ( !property_exists($this, 'disk_used_space')) or !$this->{'disk_used_space'} ) {
|
||||
if ( $this->{'Type'} == 's3fs' ) {
|
||||
$this->{'disk_used_space'} = $this->event_disk_space();
|
||||
} else {
|
||||
|
@ -112,17 +119,18 @@ class Storage extends ZM_Object {
|
|||
|
||||
public function event_disk_space() {
|
||||
# This isn't a function like this in php, so we have to add up the space used in each event.
|
||||
if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) {
|
||||
if ( (! property_exists($this, 'DiskSpace')) or (!$this->{'DiskSpace'}) ) {
|
||||
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()));
|
||||
|
||||
do {
|
||||
# Do in batches of 1000 so as to not useup all ram
|
||||
# Do in batches of 1000 so as to not useup all ram, Event will do caching though...
|
||||
$events = Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null), array('limit'=>1000));
|
||||
foreach ( $events as $Event ) {
|
||||
$Event->Storage($this); // Prevent further db hit
|
||||
# DiskSpace will update the event
|
||||
$used += $Event->DiskSpace();
|
||||
} #end foreach
|
||||
Event::clear_cache();
|
||||
} while ( count($events) == 1000 );
|
||||
$this->{'DiskSpace'} = $used;
|
||||
}
|
||||
|
@ -130,8 +138,8 @@ class Storage extends ZM_Object {
|
|||
} // end function event_disk_space
|
||||
|
||||
public function Server() {
|
||||
if ( ! array_key_exists('Server',$this) ) {
|
||||
if ( array_key_exists('ServerId', $this) ) {
|
||||
if ( ! property_exists($this, 'Server') ) {
|
||||
if ( property_exists($this, 'ServerId') ) {
|
||||
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
||||
|
||||
if ( !$this->{'Server'} ) {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
namespace ZM;
|
||||
require_once('database.php');
|
||||
require_once('Object.php');
|
||||
|
||||
|
||||
class Zone extends ZM_Object {
|
||||
protected static $table = 'Zones';
|
||||
|
||||
protected $defaults = array(
|
||||
'Id' => null,
|
||||
'Name' => '',
|
||||
'Type' => 'Active',
|
||||
'Units' => 'Pixels',
|
||||
'CheckMethod' => 'Blobs',
|
||||
'MinPixelThreshold' => null,
|
||||
'MaxPixelThreshold' => null,
|
||||
'MinAlarmPixels' => null,
|
||||
'MaxAlarmPixels' => null,
|
||||
'FilterX' => null,
|
||||
'FilterY' => null,
|
||||
'MinFilterPixels' => null,
|
||||
'MaxFilterPixels' => null,
|
||||
'MinBlobPixels' => null,
|
||||
'MaxBlobPixels' => null,
|
||||
'MinBlobs' => null,
|
||||
'MaxBlobs' => null,
|
||||
'OverloadFrames' => 0,
|
||||
'ExtendAlarmFrames' => 0,
|
||||
);
|
||||
|
||||
public static function find( $parameters = array(), $options = array() ) {
|
||||
return ZM_Object::_find(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
public static function find_one( $parameters = array(), $options = array() ) {
|
||||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
} # end class Zone
|
||||
?>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue