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
|
ssh_known_hosts: zmrepo.zoneminder.com
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
- sourceline: ppa:iconnor/zoneminder
|
- sourceline: ppa:iconnor/zoneminder-master
|
||||||
- key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04
|
- key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04
|
||||||
packages:
|
packages:
|
||||||
- gdebi
|
- gdebi
|
||||||
|
@ -33,24 +33,23 @@ install:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- SMPFLAGS=-j4 OS=el DIST=7
|
- SMPFLAGS=-j4 OS=el DIST=7
|
||||||
- SMPFLAGS=-j4 OS=el DIST=8
|
- SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack
|
||||||
- SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack
|
|
||||||
- SMPFLAGS=-j4 OS=fedora DIST=30
|
- SMPFLAGS=-j4 OS=fedora DIST=30
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=fedora DIST=31
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack USE_SFTP=yes
|
- 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=trusty ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial 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=ubuntu DIST=disco ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
|
- SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=stretch 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:
|
compiler:
|
||||||
- gcc
|
- gcc
|
||||||
|
@ -58,12 +57,6 @@ services:
|
||||||
- mysql
|
- mysql
|
||||||
- docker
|
- 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:
|
script:
|
||||||
- utils/packpack/startpackpack.sh
|
- utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
|
|
@ -140,9 +140,9 @@ set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH
|
||||||
"Location of temporary files, default: /tmp/zm")
|
"Location of temporary files, default: /tmp/zm")
|
||||||
set(ZM_LOGDIR "/var/log/zm" CACHE PATH
|
set(ZM_LOGDIR "/var/log/zm" CACHE PATH
|
||||||
"Location of generated log files, default: /var/log/zm")
|
"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")
|
"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")
|
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
|
||||||
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
|
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
|
||||||
"Location of the web server cache busting files, default: /var/cache/zoneminder")
|
"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)
|
check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H)
|
||||||
set(optlibsfound "${optlibsfound} AVCodec")
|
set(optlibsfound "${optlibsfound} AVCodec")
|
||||||
else(AVCODEC_LIBRARIES)
|
else(AVCODEC_LIBRARIES)
|
||||||
|
message(WARNING "\nWhile it should be possible to build ZM without AVCODEC the result will pretty useless.")
|
||||||
set(optlibsnotfound "${optlibsnotfound} AVCodec")
|
set(optlibsnotfound "${optlibsnotfound} AVCodec")
|
||||||
endif(AVCODEC_LIBRARIES)
|
endif(AVCODEC_LIBRARIES)
|
||||||
|
|
||||||
|
@ -905,7 +906,7 @@ message(STATUS "Optional libraries not found:${optlibsnotfound}")
|
||||||
|
|
||||||
# Run ZM configuration generator
|
# Run ZM configuration generator
|
||||||
message(STATUS "Running ZoneMinder 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)
|
if(zmconfgen_result EQUAL 0)
|
||||||
message(STATUS
|
message(STATUS
|
||||||
"ZoneMinder configuration generator completed successfully")
|
"ZoneMinder configuration generator completed successfully")
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
ZoneMinder
|
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)
|
[![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
|
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',
|
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
|
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`AutoEmail` 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',
|
`AutoMessage` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
|
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`AutoExecuteCmd` tinytext,
|
`AutoExecuteCmd` tinytext,
|
||||||
|
@ -434,6 +437,7 @@ DROP TABLE IF EXISTS `Monitors`;
|
||||||
CREATE TABLE `Monitors` (
|
CREATE TABLE `Monitors` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
|
`Notes` TEXT,
|
||||||
`ServerId` int(10) unsigned,
|
`ServerId` int(10) unsigned,
|
||||||
`StorageId` smallint(5) unsigned default 0,
|
`StorageId` smallint(5) unsigned default 0,
|
||||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local',
|
`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
|
-- 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/*AutoArchive*/,
|
||||||
0/*AutoVideo*/,
|
0/*AutoVideo*/,
|
||||||
0/*AutoUpload*/,
|
0/*AutoUpload*/,
|
||||||
0/*AutoEmail*/,
|
0/*AutoEmail*/,
|
||||||
|
''/*EmailTo*/,
|
||||||
|
''/*EmailSubject*/,
|
||||||
|
''/*EmailBody*/,
|
||||||
0/*AutoMessage*/,
|
0/*AutoMessage*/,
|
||||||
0/*AutoExecute*/,'',
|
0/*AutoExecute*/,'',
|
||||||
1/*AutoDelete*/,
|
1/*AutoDelete*/,
|
||||||
0/*AutoMove*/,0/*MoveTo*/,
|
0/*AutoMove*/,0/*MoveTo*/,
|
||||||
0/*AutoCopy*/,0/*CopyTo*/,
|
0/*AutoCopy*/,0/*CopyTo*/,
|
||||||
0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/);
|
0/*UpdateDiskSpace*/,
|
||||||
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);
|
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
|
-- 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-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,'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-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,'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,'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);
|
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
|
-- 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','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, 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);
|
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','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,'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-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, 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','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);
|
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
|
switching between httpd <-> nginx requires manaully changing ownership of
|
||||||
all event folders and the php session folder after the change.
|
all event folders and the php session folder after the change.
|
||||||
|
|
||||||
4. If you have installed ZoneMinder from the FedBerry repositories, this build
|
4. The timezone must now be set from the ZoneMinder web console. See the
|
||||||
of ZoneMinder has support for Raspberry Pi hardware acceleration when using
|
appropriate README, mentioned in the next step, for details.
|
||||||
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
|
|
||||||
|
|
||||||
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.httpd - Follow these steps when using Apache
|
||||||
README.nginx - Follow these steps when using Nginx
|
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 chown root:apache *.conf
|
||||||
sudo chmod 640 *.conf
|
sudo chmod 640 *.conf
|
||||||
|
|
||||||
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
|
4. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||||
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.
|
|
||||||
|
|
||||||
If you are not sure of the proper timezone specification to use, look at
|
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||||
http://php.net/date.timezone
|
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
|
5. Disable SELinux
|
||||||
|
|
||||||
We currently do not have the resources to create and maintain an accurate
|
SELinux must be disabled or put into permissive mode. This is not optional!
|
||||||
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.
|
|
||||||
|
|
||||||
To immediately disbale SELinux for the current seesion, issue the following
|
To immediately disbale SELinux for the current seesion, issue the following
|
||||||
from the command line:
|
from the command line:
|
||||||
|
@ -166,3 +163,11 @@ Upgrades
|
||||||
sudo systemctl restart httpd
|
sudo systemctl restart httpd
|
||||||
sudo systemctl start zoneminder
|
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 chown root:nginx *.conf
|
||||||
sudo chmod 640 *.conf
|
sudo chmod 640 *.conf
|
||||||
|
|
||||||
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
|
4. Manually setting the timezone in /etc/php.ini is deprecated.
|
||||||
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.
|
|
||||||
|
|
||||||
If you are not sure of the proper timezone specification to use, look at
|
Instead, navigate to Options -> System from the ZoneMinder web console.
|
||||||
http://php.net/date.timezone
|
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
|
5. Disable SELinux
|
||||||
|
|
||||||
|
@ -169,3 +169,11 @@ Upgrades
|
||||||
sudo systemctl restart php-fpm
|
sudo systemctl restart php-fpm
|
||||||
sudo systemctl start zoneminder
|
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
|
# This will tell zoneminder's cmake process we are building against a known distro
|
||||||
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
|
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
|
||||||
|
|
||||||
# Fedora >= 25 needs apcu backwards compatibility module
|
# Fedora needs apcu backwards compatibility module
|
||||||
%if 0%{?fedora} >= 25
|
%if 0%{?fedora}
|
||||||
%global with_apcu_bc 1
|
%global with_apcu_bc 1
|
||||||
%endif
|
%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
|
# The default for everything but el7 these days
|
||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
|
|
||||||
Name: zoneminder
|
Name: zoneminder
|
||||||
Version: 1.33.14
|
Version: 1.35.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A camera monitoring and analysis tool
|
Summary: A camera monitoring and analysis tool
|
||||||
Group: System Environment/Daemons
|
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-mysqli
|
||||||
Requires: php-common
|
Requires: php-common
|
||||||
Requires: php-gd
|
Requires: php-gd
|
||||||
%{?fedora:Requires: php-json}
|
%{?with_php_json:Requires: php-json}
|
||||||
Requires: php-pecl-apcu
|
Requires: php-pecl-apcu
|
||||||
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
|
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
|
||||||
Requires: cambozola
|
Requires: cambozola
|
||||||
|
@ -411,26 +416,26 @@ EOF
|
||||||
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Sun Aug 11 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.14-1
|
* Tue Feb 04 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.2-1
|
||||||
- Bump to 1.33.13 Development
|
- 1.34.2 Release
|
||||||
|
|
||||||
* Sun Jul 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.12-1
|
* Fri Jan 31 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.1-1
|
||||||
- Bump to 1.33.12 Development
|
- 1.34.1 Release
|
||||||
|
|
||||||
* Sun Jun 23 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.9-1
|
* Sat Jan 18 2020 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.34.0-1
|
||||||
- Bump to 1.33.9 Development
|
- 1.34.0 Release
|
||||||
|
|
||||||
* Tue Apr 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.8-1
|
* Tue Dec 17 2019 Leigh Scott <leigh123linux@gmail.com> - 1.32.3-5
|
||||||
- Bump to 1.33.8 Development
|
- Mass rebuild for x264
|
||||||
|
|
||||||
* Sun Apr 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.6-1
|
* Wed Aug 07 2019 Leigh Scott <leigh123linux@gmail.com> - 1.32.3-4
|
||||||
- Bump to 1.33.6 Development
|
- Rebuild for new ffmpeg version
|
||||||
|
|
||||||
* Sat Mar 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.4-1
|
* Tue Mar 12 2019 Sérgio Basto <sergio@serjux.com> - 1.32.3-3
|
||||||
- Bump to 1.33.4 Development
|
- Mass rebuild for x264
|
||||||
|
|
||||||
* Tue Dec 11 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.0-1
|
* Tue Mar 05 2019 RPM Fusion Release Engineering <leigh123linux@gmail.com> - 1.32.3-2
|
||||||
- Bump to 1.33.0 Development
|
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
|
||||||
|
|
||||||
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
||||||
- 1.32.3 Release
|
- 1.32.3 Release
|
||||||
|
|
|
@ -6,6 +6,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
|
||||||
Require all granted
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
|
|
||||||
# Order matters. This alias must come first.
|
# Order matters. This alias must come first.
|
||||||
Alias /zm/cache /var/cache/zoneminder/cache
|
Alias /zm/cache /var/cache/zoneminder/cache
|
||||||
<Directory /var/cache/zoneminder/cache>
|
<Directory /var/cache/zoneminder/cache>
|
||||||
|
|
|
@ -61,7 +61,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libjson-maybexs-perl
|
,libjson-maybexs-perl
|
||||||
,libsys-mmap-perl [!hurd-any]
|
,libsys-mmap-perl [!hurd-any]
|
||||||
,liburi-encode-perl
|
,liburi-encode-perl
|
||||||
,libwww-perl
|
,libwww-perl, liburi-perl
|
||||||
,libdata-dump-perl
|
,libdata-dump-perl
|
||||||
,libdatetime-perl
|
,libdatetime-perl
|
||||||
,libclass-std-fast-perl
|
,libclass-std-fast-perl
|
||||||
|
@ -74,7 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libfile-slurp-perl
|
,libfile-slurp-perl
|
||||||
,mysql-client | mariadb-client | virtual-mysql-client
|
,mysql-client | mariadb-client | virtual-mysql-client
|
||||||
,perl-modules
|
,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
|
,policykit-1
|
||||||
,rsyslog | system-log-daemon
|
,rsyslog | system-log-daemon
|
||||||
,zip
|
,zip
|
||||||
|
|
|
@ -127,7 +127,7 @@ If you are using the old credentials mechanism present in v1.0, then the credent
|
||||||
Key lifetime (v2.0)
|
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)
|
Understanding access/refresh tokens (v2.0)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -482,6 +482,7 @@ Create a Zone
|
||||||
&Zone[Units]=Percent\
|
&Zone[Units]=Percent\
|
||||||
&Zone[NumCoords]=4\
|
&Zone[NumCoords]=4\
|
||||||
&Zone[Coords]=0,0 639,0 639,479 0,479\
|
&Zone[Coords]=0,0 639,0 639,479 0,479\
|
||||||
|
&Zone[Area]=307200\
|
||||||
&Zone[AlarmRGB]=16711680\
|
&Zone[AlarmRGB]=16711680\
|
||||||
&Zone[CheckMethod]=Blobs\
|
&Zone[CheckMethod]=Blobs\
|
||||||
&Zone[MinPixelThreshold]=25\
|
&Zone[MinPixelThreshold]=25\
|
||||||
|
|
|
@ -68,9 +68,13 @@ Here is what the filter window looks like
|
||||||
* %ESM% Maximum score of the event
|
* %ESM% Maximum score of the event
|
||||||
* %EP% Path to the event
|
* %EP% Path to the event
|
||||||
* %EPS% Path to the event stream
|
* %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
|
* %EPI% Path to the event images
|
||||||
* %EPI1% Path to the first alarmed event image
|
* %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
|
* %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
|
* %EI1% Attach first alarmed event image
|
||||||
* %EIM% Attach (first) event image with the highest score
|
* %EIM% Attach (first) event image with the highest score
|
||||||
* %EV% Attach event mpeg video
|
* %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
|
* %MEW% Number of events for the monitor in the last week
|
||||||
* %MEM% Number of events for the monitor in the last month
|
* %MEM% Number of events for the monitor in the last month
|
||||||
* %MEA% Number of archived events for the monitor
|
* %MEA% Number of archived events for the monitor
|
||||||
* %MOD% Path to image containing object detection
|
|
||||||
* %MP% Path to the monitor window
|
* %MP% Path to the monitor window
|
||||||
* %MPS% Path to the monitor stream
|
* %MPS% Path to the monitor stream
|
||||||
* %MPI% Path to the monitor recent image
|
* %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 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::
|
.. 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)
|
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)
|
* 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 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::
|
.. todo::
|
||||||
add GPU docs
|
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 \[Sending data to ZoneMinder Telemetry server.\]$
|
||||||
zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send 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:]]+"\]$
|
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( {
|
my $result = $self->service('device', 'ep')->GetServices( {
|
||||||
IncludeCapability => 'true', # boolean
|
IncludeCapability => 'true', # boolean
|
||||||
},,
|
}
|
||||||
);
|
);
|
||||||
if ( $result ) {
|
if ( $result ) {
|
||||||
foreach my $svc ( @{ $result->get_Service() } ) {
|
foreach my $svc ( @{ $result->get_Service() } ) {
|
||||||
|
@ -142,7 +142,7 @@ sub get_service_urls {
|
||||||
if ( my $function = $capabilities->can( "get_$capability" ) ) {
|
if ( my $function = $capabilities->can( "get_$capability" ) ) {
|
||||||
my $Services = $function->( $capabilities );
|
my $Services = $function->( $capabilities );
|
||||||
if ( !$Services ) {
|
if ( !$Services ) {
|
||||||
print "Nothing returned ffrom get_$capability\n";
|
#print "Nothing returned from get_$capability\n";
|
||||||
} else {
|
} else {
|
||||||
foreach my $svc ( @{ $Services } ) {
|
foreach my $svc ( @{ $Services } ) {
|
||||||
# The capability versions don't have a namespace, so just lowercase them.
|
# The capability versions don't have a namespace, so just lowercase them.
|
||||||
|
@ -202,7 +202,7 @@ sub BUILD {
|
||||||
# deserializer_args => { strict => 0 }
|
# 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
|
# Can't, don't have credentials yet
|
||||||
# $self->get_service_urls();
|
# $self->get_service_urls();
|
||||||
|
@ -260,7 +260,7 @@ sub set_credentials {
|
||||||
sub create_services {
|
sub create_services {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
#$self->get_service_urls();
|
$self->get_service_urls();
|
||||||
|
|
||||||
if ( defined $self->service('media', 'url') ) {
|
if ( defined $self->service('media', 'url') ) {
|
||||||
$self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({
|
$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 %is_success_of :ATTR(:name<is_success> :default<()>);
|
||||||
|
|
||||||
my %local_addr_of :ATTR(:name<local_addr> :init_arg<local_addr> :default<()>);
|
my %local_addr_of :ATTR(:name<local_addr> :init_arg<local_addr> :default<()>);
|
||||||
|
my $net_interface;
|
||||||
|
|
||||||
# create methods normally inherited from SOAP::Client
|
# create methods normally inherited from SOAP::Client
|
||||||
SUBFACTORY: {
|
SUBFACTORY: {
|
||||||
|
@ -60,14 +60,22 @@ sub _notify_response
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_net_interface {
|
||||||
|
my $self = shift;
|
||||||
|
$net_interface = shift;
|
||||||
|
}
|
||||||
|
|
||||||
sub send_multi() {
|
sub send_multi() {
|
||||||
my ($self, $address, $port, $utf8_string) = @_;
|
my ($self, $address, $port, $utf8_string) = @_;
|
||||||
|
|
||||||
my $destination = $address . ':' . $port;
|
my $destination = $address . ':' . $port;
|
||||||
my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
|
my $socket = IO::Socket::Multicast->new(
|
||||||
LocalPort=>$port, PeerAddr=>$destination, ReuseAddr=>1)
|
PROTO => 'udp',
|
||||||
|
LocalPort=>$port,
|
||||||
or die 'Cannot open multicast socket to ' . ${address} . ':' . ${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;
|
my $bytes = $utf8_string;
|
||||||
utf8::encode($bytes);
|
utf8::encode($bytes);
|
||||||
|
@ -80,9 +88,11 @@ sub receive_multi() {
|
||||||
my ($self, $address, $port) = @_;
|
my ($self, $address, $port) = @_;
|
||||||
my $data = undef;
|
my $data = undef;
|
||||||
|
|
||||||
my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
|
my $socket = IO::Socket::Multicast->new(
|
||||||
LocalPort=>$port, ReuseAddr=>1);
|
PROTO => 'udp',
|
||||||
$socket->mcast_add($address);
|
LocalPort=>$port,
|
||||||
|
ReuseAddr=>1);
|
||||||
|
$socket->mcast_add($address, $net_interface);
|
||||||
|
|
||||||
my $readbits = '';
|
my $readbits = '';
|
||||||
vec($readbits, $socket->fileno, 1) = 1;
|
vec($readbits, $socket->fileno, 1) = 1;
|
||||||
|
@ -98,10 +108,14 @@ sub receive_uni() {
|
||||||
my ($self, $address, $port, $localaddr) = @_;
|
my ($self, $address, $port, $localaddr) = @_;
|
||||||
my $data = undef;
|
my $data = undef;
|
||||||
|
|
||||||
my $socket = IO::Socket::Multicast->new(PROTO => 'udp',
|
my $socket = IO::Socket::Multicast->new(
|
||||||
LocalAddr => $localaddr, LocalPort=>$port, ReuseAddr=>1);
|
PROTO => 'udp',
|
||||||
|
LocalAddr => $localaddr,
|
||||||
|
LocalPort=>$port,
|
||||||
|
ReuseAddr=>1
|
||||||
|
);
|
||||||
|
|
||||||
$socket->mcast_add($address);
|
$socket->mcast_add($address, $net_interface);
|
||||||
|
|
||||||
my $readbits = '';
|
my $readbits = '';
|
||||||
vec($readbits, $socket->fileno, 1) = 1;
|
vec($readbits, $socket->fileno, 1) = 1;
|
||||||
|
@ -126,6 +140,7 @@ sub send_receive {
|
||||||
$self->send_multi($address, $port, $envelope);
|
$self->send_multi($address, $port, $envelope);
|
||||||
|
|
||||||
my $localaddr = $self->get_local_addr();
|
my $localaddr = $self->get_local_addr();
|
||||||
|
#warn "localddr $localaddr";
|
||||||
|
|
||||||
my ($response, $last_response);
|
my ($response, $last_response);
|
||||||
my $wait = WAIT_COUNT;
|
my $wait = WAIT_COUNT;
|
||||||
|
@ -146,13 +161,12 @@ sub send_receive {
|
||||||
|
|
||||||
if ( $last_response ) {
|
if ( $last_response ) {
|
||||||
$self->set_code();
|
$self->set_code();
|
||||||
$self->set_message("");
|
$self->set_message('');
|
||||||
$self->set_is_success(1);
|
$self->set_is_success(1);
|
||||||
$self->set_status('OK');
|
$self->set_status('OK');
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
$self->set_code();
|
$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_is_success(0);
|
||||||
$self->set_status('TIMEOUT');
|
$self->set_status('TIMEOUT');
|
||||||
}
|
}
|
||||||
|
@ -161,3 +175,4 @@ sub send_receive {
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
__END__
|
||||||
|
|
|
@ -785,7 +785,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_TIMEZONE',
|
name => 'ZM_TIMEZONE',
|
||||||
default => 'UTC',
|
default => '',
|
||||||
description => 'The timezone that php should use.',
|
description => 'The timezone that php should use.',
|
||||||
help => q`
|
help => q`
|
||||||
This should be set equal to the system timezone of the mysql server`,
|
This should be set equal to the system timezone of the mysql server`,
|
||||||
|
@ -1127,7 +1127,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_LOG_LEVEL_FILE',
|
name => 'ZM_LOG_LEVEL_FILE',
|
||||||
default => '-5',
|
default => '1',
|
||||||
description => 'Save logging output to component files',
|
description => 'Save logging output to component files',
|
||||||
help => q`
|
help => q`
|
||||||
ZoneMinder logging is now more integrated between
|
ZoneMinder logging is now more integrated between
|
||||||
|
@ -1312,7 +1312,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_LOG_DEBUG_FILE',
|
name => 'ZM_LOG_DEBUG_FILE',
|
||||||
default => '@ZM_LOGDIR@/zm_debug.log+',
|
default => '',
|
||||||
description => 'Where extra debug is output to',
|
description => 'Where extra debug is output to',
|
||||||
help => q`
|
help => q`
|
||||||
This option allows you to specify a different target for debug
|
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.
|
that match the appropriate filters will be sent to.
|
||||||
`,
|
`,
|
||||||
type => $types{email},
|
type => $types{email},
|
||||||
category => 'mail',
|
category => 'hidden',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_EMAIL_TEXT',
|
name => 'ZM_EMAIL_TEXT',
|
||||||
|
@ -2253,7 +2253,7 @@ our @options = (
|
||||||
sent for any events that match the appropriate filters.
|
sent for any events that match the appropriate filters.
|
||||||
`,
|
`,
|
||||||
type => $types{string},
|
type => $types{string},
|
||||||
category => 'mail',
|
category => 'hidden',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_EMAIL_BODY',
|
name => 'ZM_EMAIL_BODY',
|
||||||
|
@ -2280,7 +2280,7 @@ our @options = (
|
||||||
sent for any events that match the appropriate filters.
|
sent for any events that match the appropriate filters.
|
||||||
`,
|
`,
|
||||||
type => $types{text},
|
type => $types{text},
|
||||||
category => 'mail',
|
category => 'hidden',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_OPT_MESSAGE',
|
name => 'ZM_OPT_MESSAGE',
|
||||||
|
@ -2468,7 +2468,7 @@ our @options = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_WATCH_MAX_DELAY',
|
name => 'ZM_WATCH_MAX_DELAY',
|
||||||
default => '5',
|
default => '45',
|
||||||
description => 'The maximum delay allowed since the last captured image',
|
description => 'The maximum delay allowed since the last captured image',
|
||||||
help => q`
|
help => q`
|
||||||
The zmwatch daemon checks the image capture performance of the
|
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
|
# Copyright (C) 2001-2008 Philip Coombes
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
|
@ -46,11 +46,11 @@ our $AUTOLOAD;
|
||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my $id = shift;
|
my $id = shift;
|
||||||
|
if ( !defined($id) ) {
|
||||||
|
Fatal('No monitor defined when invoking protocol '.$class);
|
||||||
|
}
|
||||||
my $self = {};
|
my $self = {};
|
||||||
$self->{name} = $class;
|
$self->{name} = $class;
|
||||||
if ( !defined($id) ) {
|
|
||||||
Fatal('No monitor defined when invoking protocol '.$self->{name});
|
|
||||||
}
|
|
||||||
$self->{id} = $id;
|
$self->{id} = $id;
|
||||||
bless($self, $class);
|
bless($self, $class);
|
||||||
return $self;
|
return $self;
|
||||||
|
@ -78,7 +78,7 @@ sub AUTOLOAD {
|
||||||
|
|
||||||
sub getKey {
|
sub getKey {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return( $self->{id} );
|
return $self->{id};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub open {
|
sub open {
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Acrest HTTP API Control Protocol Module, 20180214, Rev 3.0
|
# ZoneMinder Amcrest HTTP API Control Protocol Module
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# 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::Base;
|
||||||
require ZoneMinder::Control;
|
require ZoneMinder::Control;
|
||||||
|
require LWP::UserAgent;
|
||||||
|
use URI;
|
||||||
|
|
||||||
our @ISA = qw(ZoneMinder::Control);
|
our @ISA = qw(ZoneMinder::Control);
|
||||||
|
|
||||||
|
@ -50,130 +42,130 @@ our @ISA = qw(ZoneMinder::Control);
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
sub new
|
sub new {
|
||||||
{
|
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my $id = shift;
|
my $id = shift;
|
||||||
my $self = ZoneMinder::Control->new($id);
|
my $self = ZoneMinder::Control->new($id);
|
||||||
bless($self, $class);
|
bless($self, $class);
|
||||||
srand( time() );
|
|
||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
our $AUTOLOAD;
|
sub open {
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->loadMonitor();
|
$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} = 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
|
# 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');
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
$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;
|
my $self = shift;
|
||||||
$self->{state} = 'closed';
|
$self->{state} = 'closed';
|
||||||
}
|
}
|
||||||
|
|
||||||
sub printMsg
|
sub sendCmd {
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $msg = shift;
|
|
||||||
my $msg_len = length($msg);
|
|
||||||
|
|
||||||
Debug( $msg."[".$msg_len."]" );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub sendCmd
|
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $cmd = shift;
|
my $cmd = shift;
|
||||||
my $result = undef;
|
my $result = undef;
|
||||||
|
|
||||||
destroyUA($self);
|
$self->printMsg($cmd, 'Tx');
|
||||||
initUA($self);
|
|
||||||
|
|
||||||
my $user = undef;
|
my $res = $self->{ua}->get($$self{base_url}.$cmd);
|
||||||
my $password = undef;
|
|
||||||
my $address = undef;
|
|
||||||
|
|
||||||
if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ )
|
if ( $res->is_success ) {
|
||||||
{
|
|
||||||
$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 )
|
|
||||||
{
|
|
||||||
$result = !undef;
|
$result = !undef;
|
||||||
# Command to camera appears successful, write Info item to log
|
# 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.
|
# 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;
|
my $self = shift;
|
||||||
# This reboots the camera effectively resetting it
|
# This reboots the camera effectively resetting it
|
||||||
Debug( "Camera Reset" );
|
|
||||||
$self->sendCmd('cgi-bin/magicBox.cgi?action=reboot');
|
$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.
|
# 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 $pan_degrees = shift || 0;
|
||||||
my $tilt_degrees = shift || 0;
|
my $tilt_degrees = shift || 0;
|
||||||
my $speed = shift || 1;
|
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);
|
$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;
|
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');
|
$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?
|
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');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Down&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
usleep(500);
|
usleep(500);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Down&channel=0&arg1=0&arg2=1&arg3=0');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Left&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
usleep(500);
|
usleep(500);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=Left&channel=0&arg1=0&arg2=1&arg3=0');
|
$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;
|
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=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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=Right&channel=0&arg1=0&arg2=1&arg3=0');
|
||||||
usleep(500);
|
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');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightUp&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
usleep(500);
|
usleep(500);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightUp&channel=0&arg1=0&arg2=1&arg3=0');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=RightDown&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
usleep(500);
|
usleep(500);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=RightDown&channel=0&arg1=0&arg2=1&arg3=0');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
usleep(500);
|
usleep(500);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0');
|
||||||
usleep (500);
|
usleep (500);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0');
|
$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
|
# 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.
|
# support a generic stop-all-current-action command.
|
||||||
|
|
||||||
sub moveStop
|
sub moveStop {
|
||||||
{
|
|
||||||
my $self = shift;
|
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');
|
$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
|
# 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.
|
# 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;
|
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');
|
$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 $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
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');
|
$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 $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
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');
|
$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.
|
# 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 $self = shift;
|
||||||
my $params = 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');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomTele&arg1=0&arg2=0&arg3=0&arg4=0');
|
$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;
|
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');
|
$self->sendCmd('cgi-bin/ptz.cgi?action=start&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0');
|
||||||
usleep (100000);
|
usleep (100000);
|
||||||
$self->sendCmd('cgi-bin/ptz.cgi?action=stop&channel=0&code=ZoomWide&arg1=0&arg2=0&arg3=0&arg4=0');
|
$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
|
# Copyright (C) 2001-2008 Philip Coombes
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# 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 ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
use Time::HiRes qw( usleep );
|
use Time::HiRes qw( usleep );
|
||||||
|
use URI;
|
||||||
|
|
||||||
sub open
|
our $ADDRESS;
|
||||||
{
|
|
||||||
|
sub open {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->loadMonitor();
|
$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;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$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';
|
$self->{state} = 'open';
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub printMsg
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $msg = shift;
|
|
||||||
my $msg_len = length($msg);
|
|
||||||
|
|
||||||
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 $self = shift;
|
||||||
my $cmd = 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" );
|
if ( $res->is_success ) {
|
||||||
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" );
|
Debug('sndCmd command: ' . $url . ' content: '.$res->content);
|
||||||
my $res = $self->{ua}->request($req);
|
return !undef;
|
||||||
|
|
||||||
if ( $res->is_success )
|
|
||||||
{
|
|
||||||
$result = !undef;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error( "Error check failed: '".$res->status_line()."'" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return( $result );
|
Error("Error cmd $url failed: '".$res->status_line()."'");
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cameraReset
|
sub cameraReset {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Camera Reset" );
|
Debug('Camera Reset');
|
||||||
my $cmd = "/axis-cgi/admin/restart.cgi";
|
my $cmd = '/axis-cgi/admin/restart.cgi';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUp
|
sub moveConUp {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Up" );
|
Debug('Move Up');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=up";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=up';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDown
|
sub moveConDown {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Down" );
|
Debug('Move Down');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=down";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=down';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConLeft
|
sub moveConLeft {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Left" );
|
Debug('Move Left');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=left";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=left';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConRight
|
sub moveConRight {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Right" );
|
Debug('Move Right');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=right";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=right';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUpRight
|
sub moveConUpRight {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Up/Right" );
|
Debug('Move Up/Right');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=upright";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=upright';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConUpLeft
|
sub moveConUpLeft {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Up/Left" );
|
Debug('Move Up/Left');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=upleft";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=upleft';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDownRight
|
sub moveConDownRight {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Down/Right" );
|
Debug('Move Down/Right');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=downright";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=downright';
|
||||||
$self->sendCmd( $cmd );
|
$self->sendCmd( $cmd );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveConDownLeft
|
sub moveConDownLeft {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Move Down/Left" );
|
Debug('Move Down/Left');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=downleft";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=downleft';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveMap
|
sub moveMap {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $xcoord = $self->getParam($params, 'xcoord');
|
my $xcoord = $self->getParam($params, 'xcoord');
|
||||||
my $ycoord = $self->getParam($params, 'ycoord');
|
my $ycoord = $self->getParam($params, 'ycoord');
|
||||||
Debug("Move Map to $xcoord,$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);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUp
|
sub moveRelUp {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'tiltstep');
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
Debug("Step Up $step");
|
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);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDown
|
sub moveRelDown {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'tiltstep');
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
Debug("Step Down $step");
|
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);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelLeft
|
sub moveRelLeft {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'panstep');
|
my $step = $self->getParam($params, 'panstep');
|
||||||
Debug("Step Left $step");
|
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);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelRight
|
sub moveRelRight {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'panstep');
|
my $step = $self->getParam($params, 'panstep');
|
||||||
Debug("Step Right $step");
|
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);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUpRight
|
sub moveRelUpRight {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $panstep = $self->getParam($params, 'panstep');
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
|
@ -225,8 +252,7 @@ sub moveRelUpRight
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUpLeft
|
sub moveRelUpLeft {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $panstep = $self->getParam($params, 'panstep');
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
|
@ -236,8 +262,7 @@ sub moveRelUpLeft
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDownRight
|
sub moveRelDownRight {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $panstep = $self->getParam($params, 'panstep');
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
|
@ -247,8 +272,7 @@ sub moveRelDownRight
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDownLeft
|
sub moveRelDownLeft {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $panstep = $self->getParam($params, 'panstep');
|
my $panstep = $self->getParam($params, 'panstep');
|
||||||
|
@ -258,100 +282,89 @@ sub moveRelDownLeft
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomRelTele
|
sub zoomRelTele {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'step');
|
my $step = $self->getParam($params, 'step');
|
||||||
Debug( "Zoom Tele" );
|
Debug('Zoom Tele');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step";
|
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step";
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomRelWide
|
sub zoomRelWide {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'step');
|
my $step = $self->getParam($params, 'step');
|
||||||
Debug( "Zoom Wide" );
|
Debug('Zoom Wide');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step";
|
my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step";
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusRelNear
|
sub focusRelNear {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'step');
|
my $step = $self->getParam($params, 'step');
|
||||||
Debug( "Focus Near" );
|
Debug('Focus Near');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step";
|
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step";
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusRelFar
|
sub focusRelFar {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'step');
|
my $step = $self->getParam($params, 'step');
|
||||||
Debug( "Focus Far" );
|
Debug('Focus Far');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step";
|
my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step";
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusAuto
|
sub focusAuto {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Focus Auto" );
|
Debug('Focus Auto');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=on";
|
my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=on';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub focusMan
|
sub focusMan {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Focus Manual" );
|
Debug('Focus Manual');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=off";
|
my $cmd = '/axis-cgi/com/ptz.cgi?autofocus=off';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisRelOpen
|
sub irisRelOpen {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'step');
|
my $step = $self->getParam($params, 'step');
|
||||||
Debug( "Iris Open" );
|
Debug('Iris Open');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step";
|
my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step";
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisRelClose
|
sub irisRelClose {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'step');
|
my $step = $self->getParam($params, 'step');
|
||||||
Debug( "Iris Close" );
|
Debug('Iris Close');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step";
|
my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step";
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisAuto
|
sub irisAuto {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Iris Auto" );
|
Debug('Iris Auto');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=on";
|
my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=on';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub irisMan
|
sub irisMan {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Iris Manual" );
|
Debug('Iris Manual');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=off";
|
my $cmd = '/axis-cgi/com/ptz.cgi?autoiris=off';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetClear
|
sub presetClear {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
my $preset = $self->getParam($params, 'preset');
|
||||||
|
@ -360,8 +373,7 @@ sub presetClear
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetSet
|
sub presetSet {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
my $preset = $self->getParam($params, 'preset');
|
||||||
|
@ -370,8 +382,7 @@ sub presetSet
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetGoto
|
sub presetGoto {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
my $preset = $self->getParam($params, 'preset');
|
||||||
|
@ -380,11 +391,10 @@ sub presetGoto
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetHome
|
sub presetHome {
|
||||||
{
|
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Home Preset" );
|
Debug('Home Preset');
|
||||||
my $cmd = "/axis-cgi/com/ptz.cgi?move=home";
|
my $cmd = '/axis-cgi/com/ptz.cgi?move=home';
|
||||||
$self->sendCmd($cmd);
|
$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 $self = shift;
|
||||||
my $cmd = shift;
|
my $cmd = shift;
|
||||||
my $content = 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) ) {
|
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);
|
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||||
}
|
}
|
||||||
my $res = $self->{UA}->request($req);
|
my $res = $self->{UA}->request($req);
|
||||||
|
@ -135,13 +135,13 @@ sub PutCmd {
|
||||||
# Check for username/password
|
# Check for username/password
|
||||||
#
|
#
|
||||||
if ( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
if ( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
||||||
Info("Check username/password is correct");
|
Info('Check username/password is correct');
|
||||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
} 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} =~ /^:.+@.+/ ) {
|
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
||||||
Info("Password but no username in Control Address.");
|
Info('Password but no username in Control Address.');
|
||||||
} else {
|
} else {
|
||||||
Info("Missing username and password in Control Address.");
|
Info('Missing username and password in Control Address.');
|
||||||
}
|
}
|
||||||
Fatal($res->status_line);
|
Fatal($res->status_line);
|
||||||
}
|
}
|
||||||
|
@ -382,7 +382,7 @@ sub irisRelOpen {
|
||||||
sub reset {
|
sub reset {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->PutCmd("ISAPI/System/reboot");
|
$self->PutCmd('ISAPI/System/reboot');
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
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)
|
# Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com)
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# 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::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
|
|
||||||
use Time::HiRes qw( usleep );
|
|
||||||
|
|
||||||
sub open {
|
sub open {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
@ -52,29 +50,21 @@ sub open {
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$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';
|
$self->{state} = 'open';
|
||||||
}
|
}
|
||||||
|
|
||||||
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 $self = shift;
|
||||||
my $cmd = shift;
|
my $cmd = shift;
|
||||||
|
|
||||||
my $result = undef;
|
my $result = undef;
|
||||||
|
|
||||||
printMsg( $cmd, "Tx" );
|
$self->printMsg($cmd, 'Tx');
|
||||||
|
|
||||||
my $url;
|
my $url;
|
||||||
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
|
if ( $self->{Monitor}->{ControlAddress} =~ /^http/i ) {
|
||||||
$url = $self->{Monitor}->{ControlAddress}.$cmd;
|
$url = $self->{Monitor}->{ControlAddress}.$cmd;
|
||||||
} else {
|
} else {
|
||||||
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
|
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
|
||||||
|
@ -86,16 +76,16 @@ sub sendCmd {
|
||||||
if ( $res->is_success ) {
|
if ( $res->is_success ) {
|
||||||
$result = !undef;
|
$result = !undef;
|
||||||
} else {
|
} else {
|
||||||
Error( "Error check failed: '".$res->status_line()."'" );
|
Error('Error check failed: \''.$res->status_line().'\'');
|
||||||
}
|
}
|
||||||
|
|
||||||
return( $result );
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reset {
|
sub reset {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Camera Reset" );
|
Debug('Camera Reset');
|
||||||
my $cmd = "/admin/ptctl.cgi?move=reset";
|
my $cmd = '/admin/ptctl.cgi?move=reset';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,19 +115,19 @@ sub moveMap {
|
||||||
elsif ( $hor > 50 ) {
|
elsif ( $hor > 50 ) {
|
||||||
# right
|
# right
|
||||||
$horSteps = (($hor - 50) / 50) * $maxhor;
|
$horSteps = (($hor - 50) / 50) * $maxhor;
|
||||||
$horDir = "right";
|
$horDir = 'right';
|
||||||
}
|
}
|
||||||
|
|
||||||
# Vertical movement
|
# Vertical movement
|
||||||
if ( $ver < 50 ) {
|
if ( $ver < 50 ) {
|
||||||
# up
|
# up
|
||||||
$verSteps = ((50 - $ver) / 50) * $maxver;
|
$verSteps = ((50 - $ver) / 50) * $maxver;
|
||||||
$verDir = "up";
|
$verDir = 'up';
|
||||||
}
|
}
|
||||||
elsif ( $ver > 50 ) {
|
elsif ( $ver > 50 ) {
|
||||||
# down
|
# down
|
||||||
$verSteps = (($ver - 50) / 50) * $maxver;
|
$verSteps = (($ver - 50) / 50) * $maxver;
|
||||||
$verDir = "down";
|
$verDir = 'down';
|
||||||
}
|
}
|
||||||
|
|
||||||
my $v = int($verSteps);
|
my $v = int($verSteps);
|
||||||
|
@ -155,7 +145,7 @@ sub moveRelUp {
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'tiltstep');
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
Debug("Step Up $step");
|
Debug("Step Up $step");
|
||||||
my $cmd = "/admin/ptctl.cgi?move=up";
|
my $cmd = '/admin/ptctl.cgi?move=up';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +154,7 @@ sub moveRelDown {
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'tiltstep');
|
my $step = $self->getParam($params, 'tiltstep');
|
||||||
Debug("Step Down $step");
|
Debug("Step Down $step");
|
||||||
my $cmd = "/admin/ptctl.cgi?move=down";
|
my $cmd = '/admin/ptctl.cgi?move=down';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,12 +163,12 @@ sub moveRelLeft {
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'panstep');
|
my $step = $self->getParam($params, 'panstep');
|
||||||
|
|
||||||
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
|
if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) {
|
||||||
Debug( "Stepping Right because flipped horizontally " );
|
Debug('Stepping Right because flipped horizontally');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
|
$self->sendCmd('/admin/ptctl.cgi?move=right');
|
||||||
} else {
|
} else {
|
||||||
Debug( "Step Left" );
|
Debug('Step Left');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
|
$self->sendCmd('/admin/ptctl.cgi?move=left');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,12 +176,12 @@ sub moveRelRight {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $step = $self->getParam($params, 'panstep');
|
my $step = $self->getParam($params, 'panstep');
|
||||||
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
|
if ( $self->{Monitor}->{Orientation} eq 'FLIP_HORI' ) {
|
||||||
Debug( "Stepping Left because flipped horizontally " );
|
Debug('Stepping Left because flipped horizontally');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
|
$self->sendCmd('/admin/ptctl.cgi?move=left');
|
||||||
} else {
|
} else {
|
||||||
Debug( "Step Right" );
|
Debug('Step Right');
|
||||||
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
|
$self->sendCmd('/admin/ptctl.cgi?move=right');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +199,7 @@ sub presetSet {
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
my $preset = $self->getParam($params, 'preset');
|
||||||
Debug("Set Preset $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 );
|
$self->sendCmd( $cmd );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,14 +208,14 @@ sub presetGoto {
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset = $self->getParam($params, 'preset');
|
my $preset = $self->getParam($params, 'preset');
|
||||||
Debug("Goto Preset $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);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetHome {
|
sub presetHome {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug( "Home Preset" );
|
Debug('Home Preset');
|
||||||
my $cmd = "/admin/ptctl.cgi?move=h";
|
my $cmd = '/admin/ptctl.cgi?move=h';
|
||||||
$self->sendCmd($cmd);
|
$self->sendCmd($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Filter Module, $Date$, $Revision$
|
# ZoneMinder Filter Module
|
||||||
# Copyright (C) 2001-2008 Philip Coombes
|
# Copyright (C) 2001-2008 Philip Coombes
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
|
@ -162,7 +162,9 @@ sub Sql {
|
||||||
my $value = $term->{val};
|
my $value = $term->{val};
|
||||||
my @value_list;
|
my @value_list;
|
||||||
if ( $term->{attr} ) {
|
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(.+)$/;
|
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||||
|
@ -214,7 +216,10 @@ sub Sql {
|
||||||
|
|
||||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
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'";
|
$value = "'$temp_value'";
|
||||||
} elsif ( $term->{attr} =~ /ServerId/) {
|
} elsif ( $term->{attr} =~ /ServerId/) {
|
||||||
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
|
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' ) {
|
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||||
if ( $temp_value eq 'NULL' ) {
|
if ( $temp_value eq 'NULL' ) {
|
||||||
$value = $temp_value;
|
$value = $temp_value;
|
||||||
|
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
|
||||||
|
$value = 'to_days('.$temp_value.')';
|
||||||
} else {
|
} else {
|
||||||
$value = DateTimeToSQL($temp_value);
|
$value = DateTimeToSQL($temp_value);
|
||||||
if ( !$value ) {
|
if ( !$value ) {
|
||||||
|
@ -294,6 +301,8 @@ sub Sql {
|
||||||
} else {
|
} else {
|
||||||
$self->{Sql} .= " IS $value";
|
$self->{Sql} .= " IS $value";
|
||||||
}
|
}
|
||||||
|
} elsif ( $term->{op} eq 'EXISTS' ) {
|
||||||
|
$self->{Sql} .= " EXISTS $value";
|
||||||
} elsif ( $term->{op} eq 'IS NOT' ) {
|
} elsif ( $term->{op} eq 'IS NOT' ) {
|
||||||
$self->{Sql} .= " IS NOT $value";
|
$self->{Sql} .= " IS NOT $value";
|
||||||
} elsif ( $term->{op} eq '=[]' ) {
|
} elsif ( $term->{op} eq '=[]' ) {
|
||||||
|
|
|
@ -503,11 +503,14 @@ sub openFile {
|
||||||
$LOGFILE->autoflush() if $this->{autoFlush};
|
$LOGFILE->autoflush() if $this->{autoFlush};
|
||||||
|
|
||||||
my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2];
|
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];
|
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 ) {
|
if ( $> == 0 ) {
|
||||||
chown( $webUid, $webGid, $this->{logFile} )
|
# If we are root, we want to make sure that www-data or whatever owns the file
|
||||||
or Fatal("Can't change permissions on log file $$this{logFile}: $!");
|
chown($webUid, $webGid, $this->{logFile} ) or
|
||||||
}
|
Error("Can't change permissions on log file $$this{logFile}: $!");
|
||||||
|
} # end if are root
|
||||||
} else {
|
} else {
|
||||||
$this->fileLevel(NOLOG);
|
$this->fileLevel(NOLOG);
|
||||||
$this->termLevel(INFO);
|
$this->termLevel(INFO);
|
||||||
|
|
|
@ -36,9 +36,172 @@ require ZoneMinder::Server;
|
||||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
use parent qw(ZoneMinder::Object);
|
use parent qw(ZoneMinder::Object);
|
||||||
|
|
||||||
use vars qw/ $table $primary_key /;
|
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||||
$table = 'Monitors';
|
$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 {
|
sub Server {
|
||||||
return new ZoneMinder::Server( $_[0]{ServerId} );
|
return new ZoneMinder::Server( $_[0]{ServerId} );
|
||||||
|
|
|
@ -172,7 +172,7 @@ sub interpret_messages {
|
||||||
# functions
|
# functions
|
||||||
|
|
||||||
sub discover {
|
sub discover {
|
||||||
my ( $soap_version ) = @_;
|
my ( $soap_version, $net_interface ) = @_;
|
||||||
my @results;
|
my @results;
|
||||||
|
|
||||||
## collect all responses
|
## collect all responses
|
||||||
|
@ -193,12 +193,17 @@ sub discover {
|
||||||
my %services;
|
my %services;
|
||||||
|
|
||||||
if ( $verbose ) {
|
if ( $verbose ) {
|
||||||
print "Probing for SOAP 1.1\n"
|
print "Probing for SOAP 1.1\n";
|
||||||
}
|
}
|
||||||
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
||||||
# no_dispatch => '1',
|
# no_dispatch => '1',
|
||||||
});
|
});
|
||||||
$svc_discover->set_soap_version('1.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();
|
my $uuid = $uuid_gen->create_str();
|
||||||
|
|
||||||
|
@ -222,12 +227,17 @@ sub discover {
|
||||||
if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) {
|
if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) {
|
||||||
my %services;
|
my %services;
|
||||||
if ( $verbose ) {
|
if ( $verbose ) {
|
||||||
print "Probing for SOAP 1.2\n"
|
print "Probing for SOAP 1.2\n";
|
||||||
}
|
}
|
||||||
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
||||||
# no_dispatch => '1',
|
# no_dispatch => '1',
|
||||||
});
|
});
|
||||||
$svc_discover->set_soap_version('1.2');
|
$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.
|
# copies of the same Probe message must have the same MessageID.
|
||||||
# This is not a copy. So we generate a new uuid.
|
# 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);
|
push @results, interpret_messages($svc_discover, \%services, @responses);
|
||||||
} # end if doing soap 1.2
|
} # end if doing soap 1.2
|
||||||
return @results;
|
return @results;
|
||||||
}
|
} # end sub discover
|
||||||
|
|
||||||
sub profiles {
|
sub profiles {
|
||||||
my ( $client ) = @_;
|
my ( $client ) = @_;
|
||||||
|
|
||||||
my $endpoint = $client->get_endpoint('media');
|
my $media = $client->get_endpoint('media');
|
||||||
if ( ! $endpoint ) {
|
if ( ! $media ) {
|
||||||
print "No media enpoint for client.\n";
|
print "No media endpoint for client.\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $result = $endpoint->GetProfiles( { } ,, );
|
my $result = $media->GetProfiles( { } ,, );
|
||||||
if ( ! $result ) {
|
if ( ! $result ) {
|
||||||
print "No result from GetProfiles\n";
|
print "No result from GetProfiles.\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( $verbose ) {
|
if ( $verbose ) {
|
||||||
|
@ -275,23 +285,18 @@ sub profiles {
|
||||||
foreach my $profile ( @{ $profiles } ) {
|
foreach my $profile ( @{ $profiles } ) {
|
||||||
|
|
||||||
my $token = $profile->attr()->get_token() ;
|
my $token = $profile->attr()->get_token() ;
|
||||||
my $video_encoder_configuration = $profile->get_VideoEncoderConfiguration();
|
my $Name = $profile->get_Name();
|
||||||
if ( ! $video_encoder_configuration ) {
|
|
||||||
print "Unknown profile $token " . $profile->get_Name()."\n";
|
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
|
||||||
|
if ( ! $VideoEncoderConfiguration ) {
|
||||||
|
print "Unknown profile $token $Name.\n";
|
||||||
next;
|
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.
|
# Specification gives conflicting values for unicast stream types, try both.
|
||||||
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
||||||
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) {
|
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) {
|
||||||
$result = $client->get_endpoint('media')->GetStreamUri( {
|
my $StreamUri = $media->GetStreamUri( {
|
||||||
StreamSetup => { # ONVIF::Media::Types::StreamSetup
|
StreamSetup => { # ONVIF::Media::Types::StreamSetup
|
||||||
Stream => $streamtype, # StreamType
|
Stream => $streamtype, # StreamType
|
||||||
Transport => { # ONVIF::Media::Types::Transport
|
Transport => { # ONVIF::Media::Types::Transport
|
||||||
|
@ -299,21 +304,30 @@ sub profiles {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProfileToken => $token, # ReferenceToken
|
ProfileToken => $token, # ReferenceToken
|
||||||
} ,, );
|
} );
|
||||||
last if $result;
|
next if ! ( $StreamUri and $StreamUri->can('get_MediaUri') );
|
||||||
}
|
my $MediaUri = $StreamUri->get_MediaUri();
|
||||||
die $result if not $result;
|
next if ! $MediaUri;
|
||||||
# print $result . "\n";
|
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
|
} # end foreach profile
|
||||||
|
|
||||||
#
|
#
|
||||||
# use message parser without schema validation ???
|
# use message parser without schema validation ???
|
||||||
#
|
#
|
||||||
|
|
||||||
}
|
} # end sub profiles
|
||||||
|
|
||||||
sub move {
|
sub move {
|
||||||
my ($client, $dir) = @_;
|
my ($client, $dir) = @_;
|
||||||
|
@ -326,13 +340,22 @@ sub move {
|
||||||
|
|
||||||
sub metadata {
|
sub metadata {
|
||||||
my ( $client ) = @_;
|
my ( $client ) = @_;
|
||||||
my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, );
|
my $media = $client->get_endpoint('media');
|
||||||
die $result if not $result;
|
die 'No media endpoint.' if !$media;
|
||||||
print $result . "\n";
|
|
||||||
|
|
||||||
$result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, );
|
my $result = $media->GetMetadataConfigurations( { } ,, );
|
||||||
die $result if not $result;
|
if ( ! $result ) {
|
||||||
|
print "No MetaDataConfigurations\n" if $verbose;
|
||||||
|
} else {
|
||||||
print $result . "\n";
|
print $result . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $media->GetVideoAnalyticsConfigurations( { } ,, );
|
||||||
|
if ( ! $result ) {
|
||||||
|
print "No VideoAnalyticsConfigurations\n" if $verbose;
|
||||||
|
} else {
|
||||||
|
print $result . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, );
|
# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, );
|
||||||
# die $result if not $result;
|
# die $result if not $result;
|
||||||
|
|
|
@ -420,7 +420,7 @@ MAIN: while( $loop ) {
|
||||||
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
|
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]/*");
|
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 ) {
|
foreach my $event_dir ( @event_dirs ) {
|
||||||
if ( ! -d $event_dir ) {
|
if ( ! -d $event_dir ) {
|
||||||
Debug("$event_dir is not a dir. Skipping");
|
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+)$/;
|
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/;
|
||||||
if ( !$event_id ) {
|
if ( !$event_id ) {
|
||||||
Debug("Unable to parse date/event_id from $event_dir");
|
Debug('Unable to parse date/event_id from '.$event_dir);
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
|
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
|
||||||
|
@ -438,6 +438,7 @@ MAIN: while( $loop ) {
|
||||||
$Event->MonitorId( $monitor_dir );
|
$Event->MonitorId( $monitor_dir );
|
||||||
$Event->StorageId( $Storage->Id() );
|
$Event->StorageId( $Storage->Id() );
|
||||||
$Event->Path();
|
$Event->Path();
|
||||||
|
$Event->age();
|
||||||
Debug("Have event $$Event{Id} at $$Event{Path}");
|
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()))));
|
$Event->StartTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path()))));
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
|
@ -466,13 +467,13 @@ MAIN: while( $loop ) {
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
chdir( $Storage->Path() );
|
chdir( $Storage->Path() );
|
||||||
} # if USE_DEEP_STORAGE
|
} # 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);
|
delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir);
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
|
|
||||||
if ( $cleaned ) {
|
if ( $cleaned ) {
|
||||||
Debug("First stage cleaning done. Restarting.");
|
Debug('First stage cleaning done. Restarting.');
|
||||||
redo MAIN;
|
redo MAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +485,7 @@ MAIN: while( $loop ) {
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
my @event_ids = keys %$fs_events;
|
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 ) {
|
foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) {
|
||||||
|
|
||||||
|
@ -499,7 +500,7 @@ MAIN: while( $loop ) {
|
||||||
}
|
}
|
||||||
my $age = $Event->age();
|
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");
|
aud_print("Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old");
|
||||||
if ( confirm() ) {
|
if ( confirm() ) {
|
||||||
$Event->delete_files();
|
$Event->delete_files();
|
||||||
|
@ -586,7 +587,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
||||||
} else {
|
} else {
|
||||||
Debug("$$Event{Id} Not found at $path");
|
Debug("$$Event{Id} Not found at $path");
|
||||||
}
|
}
|
||||||
}
|
} # end foreach Storage
|
||||||
if ( $Event->Archived() ) {
|
if ( $Event->Archived() ) {
|
||||||
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
|
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
|
||||||
next;
|
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}");
|
Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}");
|
||||||
$Event->StorageId($$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}");
|
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());
|
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
||||||
} else {
|
|
||||||
$Event->StartTime($$fs_events{$db_event}->StartTime());
|
|
||||||
}
|
|
||||||
$Event->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$Event->save();
|
$Event->save();
|
||||||
}
|
} # end if Event exists in db and not in filesystem
|
||||||
} # end if ! in fs_events
|
} # end if ! in fs_events
|
||||||
} # foreach db_event
|
} # foreach db_event
|
||||||
} # end foreach db_monitor
|
} # 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_day_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
|
||||||
$eventcounts_week_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());
|
$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;
|
sleep($Config{ZM_AUDIT_CHECK_INTERVAL}) if $continuous;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -351,8 +351,14 @@ sub exportsql {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($ARGV[0]) {
|
my $name = $ARGV[0];
|
||||||
$command .= qq( --where="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";
|
$command .= " zm Controls MonitorPresets";
|
||||||
|
|
|
@ -108,6 +108,9 @@ if ( $options{command} ) {
|
||||||
Fatal("Unable to load control data for monitor $id");
|
Fatal("Unable to load control data for monitor $id");
|
||||||
}
|
}
|
||||||
my $protocol = $monitor->{Protocol};
|
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 ) {
|
if ( -x $protocol ) {
|
||||||
# Protocol is actually a script!
|
# Protocol is actually a script!
|
||||||
|
|
|
@ -196,7 +196,7 @@ my $last_action = 0;
|
||||||
while( !$zm_terminate ) {
|
while( !$zm_terminate ) {
|
||||||
my $now = time;
|
my $now = time;
|
||||||
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
||||||
Debug("Reloading filters");
|
Debug('Reloading filters');
|
||||||
$last_action = $now;
|
$last_action = $now;
|
||||||
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
||||||
}
|
}
|
||||||
|
@ -699,8 +699,10 @@ sub substituteTags {
|
||||||
$text =~ s/%ESM%/$Event->{MaxScore}/g;
|
$text =~ s/%ESM%/$Event->{MaxScore}/g;
|
||||||
|
|
||||||
if ( $first_alarm_frame ) {
|
if ( $first_alarm_frame ) {
|
||||||
$text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
|
$text =~ s/%EPF1%/$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/%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 ( $attachments_ref ) {
|
||||||
if ( $text =~ s/%EI1%//g ) {
|
if ( $text =~ s/%EI1%//g ) {
|
||||||
my $path = generateImage($Event, $first_alarm_frame);
|
my $path = generateImage($Event, $first_alarm_frame);
|
||||||
|
@ -748,13 +750,14 @@ sub substituteTags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $text =~ s/%EIMOD%//g ) {
|
if ( $text =~ s/%EIMOD%//g or $text =~ s/%EFMOD%//g ) {
|
||||||
$text =~ s/%EIMOD%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=objdetect/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';
|
my $path = $Event->Path().'/objdetect.jpg';
|
||||||
if ( -e $path ) {
|
if ( -e $path ) {
|
||||||
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
|
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
|
||||||
} else {
|
} 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');
|
Error('No from email address defined, not sending email');
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ( ! $Config{ZM_EMAIL_ADDRESS} ) {
|
if ( ! $$filter{EmailTo} ) {
|
||||||
Error('No email address defined, not sending email');
|
Error('No email address defined, not sending email');
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Info('Creating notification email');
|
Info('Creating notification email');
|
||||||
|
|
||||||
my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event);
|
my $subject = substituteTags($$filter{EmailSubject}, $filter, $Event);
|
||||||
return 0 if !$subject;
|
return 0 if !$subject;
|
||||||
my @attachments;
|
my @attachments;
|
||||||
my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments);
|
my $body = substituteTags($$filter{EmailBody}, $filter, $Event, \@attachments);
|
||||||
return 0 if !$body;
|
return 0 if !$body;
|
||||||
|
|
||||||
Info("Sending notification email '$subject'");
|
Info("Sending notification email '$subject'");
|
||||||
|
@ -816,7 +819,7 @@ sub sendEmail {
|
||||||
### Create the multipart container
|
### Create the multipart container
|
||||||
my $mail = MIME::Lite->new (
|
my $mail = MIME::Lite->new (
|
||||||
From => $Config{ZM_FROM_EMAIL},
|
From => $Config{ZM_FROM_EMAIL},
|
||||||
To => $Config{ZM_EMAIL_ADDRESS},
|
To => $$filter{EmailTo},
|
||||||
Subject => $subject,
|
Subject => $subject,
|
||||||
Type => 'multipart/mixed'
|
Type => 'multipart/mixed'
|
||||||
);
|
);
|
||||||
|
@ -849,7 +852,7 @@ sub sendEmail {
|
||||||
$mail->send();
|
$mail->send();
|
||||||
} else {
|
} else {
|
||||||
### Send using SSMTP
|
### Send using SSMTP
|
||||||
$mail->send('sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS});
|
$mail->send('sendmail', $ssmtp_location, $$filter{EmailTo});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
|
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
|
||||||
|
@ -858,9 +861,9 @@ sub sendEmail {
|
||||||
} else {
|
} else {
|
||||||
my $mail = MIME::Entity->build(
|
my $mail = MIME::Entity->build(
|
||||||
From => $Config{ZM_FROM_EMAIL},
|
From => $Config{ZM_FROM_EMAIL},
|
||||||
To => $Config{ZM_EMAIL_ADDRESS},
|
To => $$filter{EmailTo},
|
||||||
Subject => $subject,
|
Subject => $subject,
|
||||||
Type => (($body=~/<html>/)?'text/html':'text/plain'),
|
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||||
Data => $body
|
Data => $body
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -962,7 +965,7 @@ sub sendMessage {
|
||||||
From => $Config{ZM_FROM_EMAIL},
|
From => $Config{ZM_FROM_EMAIL},
|
||||||
To => $Config{ZM_MESSAGE_ADDRESS},
|
To => $Config{ZM_MESSAGE_ADDRESS},
|
||||||
Subject => $subject,
|
Subject => $subject,
|
||||||
Type => (($body=~/<html>/)?'text/html':'text/plain'),
|
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||||
Data => $body
|
Data => $body
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ my $OPTIONS = 'v';
|
||||||
|
|
||||||
sub HELP_MESSAGE {
|
sub HELP_MESSAGE {
|
||||||
my ($fh, $pkg, $ver, $opts) = @_;
|
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 " " . __FILE__ . " [-v] <command> <device URI> <soap version> <user> <password>\n";
|
||||||
print $fh <<EOF
|
print $fh <<EOF
|
||||||
Commands are:
|
Commands are:
|
||||||
|
@ -84,7 +84,8 @@ if ( defined $opt_v ) {
|
||||||
|
|
||||||
if ( $action eq 'probe' ) {
|
if ( $action eq 'probe' ) {
|
||||||
my $soap_version = shift;
|
my $soap_version = shift;
|
||||||
ZoneMinder::ONVIF::discover($soap_version);
|
my $net_interface = shift;
|
||||||
|
ZoneMinder::ONVIF::discover($soap_version, $net_interface);
|
||||||
} else {
|
} else {
|
||||||
# all other actions need URI and credentials
|
# all other actions need URI and credentials
|
||||||
my $url_svc_device = shift @ARGV;
|
my $url_svc_device = shift @ARGV;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use bytes;
|
use bytes;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
@EXTRA_PERL_LIB@
|
@EXTRA_PERL_LIB@
|
||||||
use ZoneMinder;
|
use ZoneMinder;
|
||||||
|
@ -34,6 +35,7 @@ use Sys::MemInfo qw(totalmem);
|
||||||
use Sys::CPU qw(cpu_count);
|
use Sys::CPU qw(cpu_count);
|
||||||
use POSIX qw(strftime uname);
|
use POSIX qw(strftime uname);
|
||||||
use JSON::MaybeXS;
|
use JSON::MaybeXS;
|
||||||
|
use Encode;
|
||||||
|
|
||||||
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||||
|
@ -166,7 +168,7 @@ sub sendData {
|
||||||
$req->header('content-length' => length($msg));
|
$req->header('content-length' => length($msg));
|
||||||
$req->header('connection' => 'Close');
|
$req->header('connection' => 'Close');
|
||||||
|
|
||||||
$req->content($msg);
|
$req->content(encode('UTF-8',$msg));
|
||||||
|
|
||||||
my $resp = $ua->request($req);
|
my $resp = $ua->request($req);
|
||||||
my $resp_msg = $resp->decoded_content;
|
my $resp_msg = $resp->decoded_content;
|
||||||
|
@ -196,7 +198,7 @@ sub getUUID {
|
||||||
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
$uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array();
|
||||||
$sth->finish();
|
$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() );
|
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
$res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() );
|
$res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() );
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
@ -232,9 +234,9 @@ sub countQuery {
|
||||||
my $dbh = shift;
|
my $dbh = shift;
|
||||||
my $table = shift;
|
my $table = shift;
|
||||||
|
|
||||||
my $sql = "SELECT count(*) FROM $table";
|
my $sql = "SELECT count(*) FROM `$table`";
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
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 $res = $sth->execute() or die 'Can\'t execute: '.$sth->errstr();
|
||||||
my $count = $sth->fetchrow_array();
|
my $count = $sth->fetchrow_array();
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
|
||||||
|
@ -245,7 +247,7 @@ sub countQuery {
|
||||||
sub getMonitorRef {
|
sub getMonitorRef {
|
||||||
my $dbh = shift;
|
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 $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 $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||||
my $arrayref = $sth->fetchall_arrayref({});
|
my $arrayref = $sth->fetchall_arrayref({});
|
||||||
|
|
|
@ -329,14 +329,13 @@ sub loadMonitor {
|
||||||
} # end sub loadMonitor
|
} # end sub loadMonitor
|
||||||
|
|
||||||
sub loadMonitors {
|
sub loadMonitors {
|
||||||
Debug('Loading monitors');
|
|
||||||
$monitor_reload_time = time();
|
$monitor_reload_time = time();
|
||||||
|
|
||||||
my %new_monitors = ();
|
my %new_monitors = ();
|
||||||
|
|
||||||
my $sql = q`SELECT * FROM Monitors
|
my $sql = 'SELECT * FROM `Monitors`
|
||||||
WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )`.
|
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect\' )'.
|
||||||
( $Config{ZM_SERVER_ID} ? ' AND ServerId=?' : '' )
|
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' )
|
||||||
;
|
;
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
|
|
|
@ -93,12 +93,11 @@ if ( $version ) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
die( 'No command given' ) unless( $command );
|
die 'No command given' unless $command;
|
||||||
die( 'No unit code given' )
|
die 'No unit code given'
|
||||||
unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) );
|
unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) );
|
||||||
|
|
||||||
if ( $command eq 'start' )
|
if ( $command eq 'start' ) {
|
||||||
{
|
|
||||||
X10Server::runServer();
|
X10Server::runServer();
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
@ -108,14 +107,12 @@ socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 )
|
||||||
|
|
||||||
my $saddr = sockaddr_un(SOCK_FILE);
|
my $saddr = sockaddr_un(SOCK_FILE);
|
||||||
|
|
||||||
if ( !connect( CLIENT, $saddr ) )
|
if ( !connect(CLIENT, $saddr) ) {
|
||||||
{
|
|
||||||
# The server isn't there
|
# The server isn't there
|
||||||
print("Unable to connect, starting server\n");
|
print("Unable to connect, starting server\n");
|
||||||
close(CLIENT);
|
close(CLIENT);
|
||||||
|
|
||||||
if ( my $cpid = fork() )
|
if ( my $cpid = fork() ) {
|
||||||
{
|
|
||||||
# Parent process just sleep and fall through
|
# Parent process just sleep and fall through
|
||||||
sleep(2);
|
sleep(2);
|
||||||
logReinit();
|
logReinit();
|
||||||
|
@ -123,28 +120,23 @@ if ( !connect( CLIENT, $saddr ) )
|
||||||
or Fatal("Can't open socket: $!");
|
or Fatal("Can't open socket: $!");
|
||||||
connect(CLIENT, $saddr)
|
connect(CLIENT, $saddr)
|
||||||
or Fatal("Can't connect: $!");
|
or Fatal("Can't connect: $!");
|
||||||
}
|
} elsif ( defined($cpid) ) {
|
||||||
elsif ( defined($cpid) )
|
|
||||||
{
|
|
||||||
setpgrp();
|
setpgrp();
|
||||||
|
|
||||||
logReinit();
|
logReinit();
|
||||||
X10Server::runServer();
|
X10Server::runServer();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Fatal("Can't fork: $!");
|
Fatal("Can't fork: $!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# The server is there, connect to it
|
# The server is there, connect to it
|
||||||
#print( "Writing commands\n" );
|
#print( "Writing commands\n" );
|
||||||
CLIENT->autoflush();
|
CLIENT->autoflush();
|
||||||
my $message = "$command";
|
my $message = $command;
|
||||||
$message .= ";$unit_code" if ( $unit_code );
|
$message .= ';'.$unit_code if $unit_code;
|
||||||
print(CLIENT $message);
|
print(CLIENT $message);
|
||||||
shutdown(CLIENT, 1);
|
shutdown(CLIENT, 1);
|
||||||
while ( my $line = <CLIENT> )
|
while ( my $line = <CLIENT> ) {
|
||||||
{
|
|
||||||
chomp($line);
|
chomp($line);
|
||||||
print("$line\n");
|
print("$line\n");
|
||||||
}
|
}
|
||||||
|
@ -178,9 +170,8 @@ our %monitor_hash;
|
||||||
our %device_hash;
|
our %device_hash;
|
||||||
our %pending_tasks;
|
our %pending_tasks;
|
||||||
|
|
||||||
sub runServer
|
sub runServer {
|
||||||
{
|
Info('X10 server starting');
|
||||||
Info( "X10 server starting\n" );
|
|
||||||
|
|
||||||
socket(SERVER, PF_UNIX, SOCK_STREAM, 0)
|
socket(SERVER, PF_UNIX, SOCK_STREAM, 0)
|
||||||
or Fatal("Can't open socket: $!");
|
or Fatal("Can't open socket: $!");
|
||||||
|
@ -191,7 +182,11 @@ sub runServer
|
||||||
|
|
||||||
$dbh = zmDbConnect();
|
$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();
|
loadTasks();
|
||||||
|
|
||||||
|
@ -205,46 +200,38 @@ sub runServer
|
||||||
my $reload = undef;
|
my $reload = undef;
|
||||||
my $reload_count = 0;
|
my $reload_count = 0;
|
||||||
my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout;
|
my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout;
|
||||||
while( 1 )
|
while( 1 ) {
|
||||||
{
|
|
||||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||||
#print( "Off select, NF:$nfound, ER:$!\n" );
|
#print( "Off select, NF:$nfound, ER:$!\n" );
|
||||||
#print( vec( $rout, fileno(SERVER),1)."\n" );
|
#print( vec( $rout, fileno(SERVER),1)."\n" );
|
||||||
#print( vec( $rout, $x10->select_fds(),1)."\n" );
|
#print( vec( $rout, $x10->select_fds(),1)."\n" );
|
||||||
if ( $nfound > 0 )
|
if ( $nfound > 0 ) {
|
||||||
{
|
if ( vec($rout, fileno(SERVER),1) ) {
|
||||||
if ( vec( $rout, fileno(SERVER),1) )
|
|
||||||
{
|
|
||||||
my $paddr = accept(CLIENT, SERVER);
|
my $paddr = accept(CLIENT, SERVER);
|
||||||
my $message = <CLIENT>;
|
my $message = <CLIENT>;
|
||||||
|
|
||||||
my ( $command, $unit_code ) = split( /;/, $message );
|
my ($command, $unit_code) = split(';', $message);
|
||||||
|
|
||||||
my $device;
|
my $device;
|
||||||
if ( defined($unit_code) )
|
if ( defined($unit_code) ) {
|
||||||
{
|
if ( $unit_code < 1 || $unit_code > 16 ) {
|
||||||
if ( $unit_code < 1 || $unit_code > 16 )
|
|
||||||
{
|
|
||||||
dPrint(ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n");
|
dPrint(ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n");
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
$device = $device_hash{$unit_code};
|
$device = $device_hash{$unit_code};
|
||||||
if ( !$device )
|
if ( !$device ) {
|
||||||
{
|
$device = $device_hash{$unit_code} = {
|
||||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||||
status=>'unknown'
|
status=>'unknown'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
} # end if defined($unit_code)
|
||||||
|
|
||||||
my $result;
|
my $result;
|
||||||
if ( $command eq 'on' )
|
if ( $command eq 'on' ) {
|
||||||
{
|
|
||||||
$result = $device->{appliance}->on();
|
$result = $device->{appliance}->on();
|
||||||
}
|
} elsif ( $command eq 'off' ) {
|
||||||
elsif ( $command eq 'off' )
|
|
||||||
{
|
|
||||||
$result = $device->{appliance}->off();
|
$result = $device->{appliance}->off();
|
||||||
}
|
}
|
||||||
#elsif ( $command eq 'dim' )
|
#elsif ( $command eq 'dim' )
|
||||||
|
@ -255,128 +242,97 @@ sub runServer
|
||||||
#{
|
#{
|
||||||
#$result = $device->{appliance}->bright();
|
#$result = $device->{appliance}->bright();
|
||||||
#}
|
#}
|
||||||
elsif ( $command eq 'status' )
|
elsif ( $command eq 'status' ) {
|
||||||
{
|
if ( $device ) {
|
||||||
if ( $device )
|
|
||||||
{
|
|
||||||
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
||||||
}
|
} else {
|
||||||
else
|
foreach my $unit_code ( sort( keys(%device_hash) ) ) {
|
||||||
{
|
|
||||||
foreach my $unit_code ( sort( keys(%device_hash) ) )
|
|
||||||
{
|
|
||||||
my $device = $device_hash{$unit_code};
|
my $device = $device_hash{$unit_code};
|
||||||
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
dPrint(ZoneMinder::Logger::DEBUG, $unit_code.' '.$device->{status}."\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} elsif ( $command eq 'shutdown' ) {
|
||||||
elsif ( $command eq 'shutdown' )
|
|
||||||
{
|
|
||||||
last;
|
last;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n");
|
dPrint(ZoneMinder::Logger::ERROR, "Invalid command '$command'\n");
|
||||||
}
|
}
|
||||||
if ( defined($result) )
|
if ( defined($result) ) {
|
||||||
{
|
# FIXME
|
||||||
if ( 1 || $result )
|
if ( 1 || $result ) {
|
||||||
{
|
|
||||||
$device->{status} = uc($command);
|
$device->{status} = uc($command);
|
||||||
dPrint(ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n");
|
dPrint(ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n");
|
||||||
#x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) );
|
#x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) );
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
dPrint(ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n");
|
dPrint(ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n");
|
||||||
}
|
}
|
||||||
}
|
} # end if defined result
|
||||||
close(CLIENT);
|
close(CLIENT);
|
||||||
}
|
} elsif ( vec($rout, $x10->select_fds(),1) ) {
|
||||||
elsif ( vec( $rout, $x10->select_fds(),1) )
|
|
||||||
{
|
|
||||||
$x10->handle_input();
|
$x10->handle_input();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
Fatal('Bogus descriptor');
|
Fatal('Bogus descriptor');
|
||||||
}
|
}
|
||||||
}
|
} elsif ( $nfound < 0 ) {
|
||||||
elsif ( $nfound < 0 )
|
if ( $! != EINTR ) {
|
||||||
{
|
|
||||||
if ( $! != EINTR )
|
|
||||||
{
|
|
||||||
Fatal("Can't select: $!");
|
Fatal("Can't select: $!");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
#print( "Select timed out\n" );
|
#print( "Select timed out\n" );
|
||||||
# Check for state changes
|
# 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 $monitor = $monitor_hash{$monitor_id};
|
||||||
my $state = zmGetMonitorState($monitor);
|
my $state = zmGetMonitorState($monitor);
|
||||||
if ( !defined($state) )
|
if ( !defined($state) ) {
|
||||||
{
|
|
||||||
$reload = !undef;
|
$reload = !undef;
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
if ( defined( $monitor->{LastState} ) )
|
if ( defined( $monitor->{LastState} ) ) {
|
||||||
{
|
|
||||||
my $task_list;
|
my $task_list;
|
||||||
if ( ($state == STATE_ALARM || $state == STATE_ALERT)
|
if ( ($state == STATE_ALARM || $state == STATE_ALERT)
|
||||||
&& ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE)
|
&& ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE)
|
||||||
) # Gone into alarm state
|
) # Gone into alarm state
|
||||||
{
|
{
|
||||||
Debug( "Applying ON_list for $monitor_id\n" );
|
Debug("Applying ON_list for $monitor_id");
|
||||||
$task_list = $monitor->{'ON_list'};
|
$task_list = $monitor->{ON_list};
|
||||||
}
|
} elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
|
||||||
elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE)
|
|
||||||
|| ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE)
|
|| ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE)
|
||||||
) # Come out of alarm state
|
) # Come out of alarm state
|
||||||
{
|
{
|
||||||
Debug( "Applying OFF_list for $monitor_id\n" );
|
Debug("Applying OFF_list for $monitor_id");
|
||||||
$task_list = $monitor->{'OFF_list'};
|
$task_list = $monitor->{OFF_list};
|
||||||
}
|
}
|
||||||
if ( $task_list )
|
if ( $task_list ) {
|
||||||
{
|
foreach my $task ( @$task_list ) {
|
||||||
foreach my $task ( @$task_list )
|
|
||||||
{
|
|
||||||
processTask($task);
|
processTask($task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} # end if defined laststate
|
||||||
$monitor->{LastState} = $state;
|
$monitor->{LastState} = $state;
|
||||||
}
|
} # end foreach monitor
|
||||||
|
|
||||||
# Check for pending tasks
|
# Check for pending tasks
|
||||||
my $now = time();
|
my $now = time();
|
||||||
foreach my $activation_time ( sort(keys(%pending_tasks) ) )
|
foreach my $activation_time ( sort(keys(%pending_tasks) ) ) {
|
||||||
{
|
|
||||||
last if ( $activation_time > $now );
|
last if ( $activation_time > $now );
|
||||||
my $pending_list = $pending_tasks{$activation_time};
|
my $pending_list = $pending_tasks{$activation_time};
|
||||||
foreach my $task ( @$pending_list )
|
foreach my $task ( @$pending_list ) {
|
||||||
{
|
|
||||||
processTask($task);
|
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();
|
loadTasks();
|
||||||
$reload = undef;
|
$reload = undef;
|
||||||
$reload_count = 0;
|
$reload_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Info( "X10 server exiting\n" );
|
Info("X10 server exiting");
|
||||||
close(SERVER);
|
close(SERVER);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
sub addToDeviceList
|
sub addToDeviceList {
|
||||||
{
|
|
||||||
my $unit_code = shift;
|
my $unit_code = shift;
|
||||||
my $event = shift;
|
my $event = shift;
|
||||||
my $monitor = shift;
|
my $monitor = shift;
|
||||||
|
@ -384,36 +340,35 @@ sub addToDeviceList
|
||||||
my $limit = shift;
|
my $limit = shift;
|
||||||
|
|
||||||
Debug("Adding to device list, uc:$unit_code, ev:$event, mo:"
|
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};
|
my $device = $device_hash{$unit_code};
|
||||||
if ( !$device )
|
if ( !$device ) {
|
||||||
{
|
$device = $device_hash{$unit_code} = {
|
||||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||||
status=>'unknown'
|
status=>'unknown'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
my $task = { type=>'device',
|
my $task = {
|
||||||
|
type=>'device',
|
||||||
monitor=>$monitor,
|
monitor=>$monitor,
|
||||||
address=>$device->{appliance}->address(),
|
address=>$device->{appliance}->address(),
|
||||||
function=>$function
|
function=>$function
|
||||||
};
|
};
|
||||||
if ( $limit )
|
|
||||||
{
|
if ( $limit ) {
|
||||||
$task->{limit} = $limit
|
$task->{limit} = $limit
|
||||||
}
|
}
|
||||||
|
|
||||||
my $task_list = $device->{$event.'_list'};
|
my $task_list = $device->{$event.'_list'};
|
||||||
if ( !$task_list )
|
if ( !$task_list ) {
|
||||||
{
|
|
||||||
$task_list = $device->{$event.'_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 $monitor = shift;
|
||||||
my $event = shift;
|
my $event = shift;
|
||||||
my $unit_code = shift;
|
my $unit_code = shift;
|
||||||
|
@ -421,59 +376,54 @@ sub addToMonitorList
|
||||||
my $limit = shift;
|
my $limit = shift;
|
||||||
|
|
||||||
Debug("Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}
|
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};
|
my $device = $device_hash{$unit_code};
|
||||||
if ( !$device )
|
if ( !$device ) {
|
||||||
{
|
$device = $device_hash{$unit_code} = {
|
||||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||||
status=>'unknown'
|
status=>'unknown'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
my $task = { type=>'monitor',
|
my $task = {
|
||||||
|
type=>'monitor',
|
||||||
device=>$device,
|
device=>$device,
|
||||||
id=>$monitor->{Id},
|
id=>$monitor->{Id},
|
||||||
function=>$function
|
function=>$function
|
||||||
};
|
};
|
||||||
if ( $limit )
|
if ( $limit ) {
|
||||||
{
|
|
||||||
$task->{limit} = $limit;
|
$task->{limit} = $limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $task_list = $monitor->{$event.'_list'};
|
my $task_list = $monitor->{$event.'_list'};
|
||||||
if ( !$task_list )
|
if ( !$task_list ) {
|
||||||
{
|
|
||||||
$task_list = $monitor->{$event.'_list'} = [];
|
$task_list = $monitor->{$event.'_list'} = [];
|
||||||
}
|
}
|
||||||
push( @$task_list, $task );
|
push @$task_list, $task;
|
||||||
}
|
} # end sub addToMonitorList
|
||||||
|
|
||||||
sub loadTasks
|
sub loadTasks {
|
||||||
{
|
|
||||||
%monitor_hash = ();
|
%monitor_hash = ();
|
||||||
|
|
||||||
Debug( "Loading tasks\n" );
|
Debug('Loading tasks');
|
||||||
# Clear out all old device task lists
|
# 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};
|
my $device = $device_hash{$unit_code};
|
||||||
$device->{ON_list} = [];
|
$device->{ON_list} = [];
|
||||||
$device->{OFF_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)
|
INNER JOIN TriggersX10 as T on (M.Id = T.MonitorId)
|
||||||
WHERE find_in_set( M.Function, 'Modect,Record,Mocord,Nodect' )
|
WHERE find_in_set(M.`Function`, \'Modect,Record,Mocord,Nodect\')
|
||||||
AND M.Enabled = 1
|
AND M.`Enabled` = 1
|
||||||
AND find_IN_set( 'X10', M.Triggers )"
|
AND find_IN_set(\'X10\', M.Triggers)';
|
||||||
;
|
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
||||||
my $res = $sth->execute()
|
my $res = $sth->execute()
|
||||||
or Fatal("Can't execute: ".$sth->errstr());
|
or Fatal("Can't execute: ".$sth->errstr());
|
||||||
while( my $monitor = $sth->fetchrow_hashref() )
|
while( my $monitor = $sth->fetchrow_hashref() ) {
|
||||||
{
|
|
||||||
# Check shared memory ok
|
# Check shared memory ok
|
||||||
if ( !zmMemVerify($monitor) ) {
|
if ( !zmMemVerify($monitor) ) {
|
||||||
zmMemInvalidate($monitor);
|
zmMemInvalidate($monitor);
|
||||||
|
@ -482,301 +432,235 @@ sub loadTasks
|
||||||
|
|
||||||
$monitor_hash{$monitor->{Id}} = $monitor;
|
$monitor_hash{$monitor->{Id}} = $monitor;
|
||||||
|
|
||||||
if ( $monitor->{Activation} )
|
if ( $monitor->{Activation} ) {
|
||||||
{
|
Debug("$monitor->{Name} has active string '$monitor->{Activation}'");
|
||||||
Debug( "$monitor->{Name} has active string '$monitor->{Activation}'\n" );
|
foreach my $code_string ( split(',', $monitor->{Activation}) ) {
|
||||||
foreach my $code_string ( split( /,/, $monitor->{Activation} ) )
|
|
||||||
{
|
|
||||||
#Debug( "Code string: $code_string\n" );
|
#Debug( "Code string: $code_string\n" );
|
||||||
my ( $invert, $unit_code, $modifier, $limit )
|
my ( $invert, $unit_code, $modifier, $limit )
|
||||||
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
||||||
$limit = 0 if ( !$limit );
|
$limit = 0 if !$limit;
|
||||||
if ( $unit_code )
|
if ( $unit_code ) {
|
||||||
{
|
if ( !$modifier || $modifier eq '+' ) {
|
||||||
if ( !$modifier || $modifier eq '+' )
|
|
||||||
{
|
|
||||||
addToDeviceList( $unit_code,
|
addToDeviceList( $unit_code,
|
||||||
'ON',
|
'ON',
|
||||||
$monitor,
|
$monitor,
|
||||||
!$invert ? 'start_active'
|
(!$invert ? 'start_active' : 'stop_active'),
|
||||||
: 'stop_active',
|
|
||||||
$limit
|
$limit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( !$modifier || $modifier eq '-' )
|
if ( !$modifier || $modifier eq '-' ) {
|
||||||
{
|
|
||||||
addToDeviceList( $unit_code,
|
addToDeviceList( $unit_code,
|
||||||
'OFF',
|
'OFF',
|
||||||
$monitor,
|
$monitor,
|
||||||
!$invert ? 'stop_active'
|
(!$invert ? 'stop_active' : 'start_active'),
|
||||||
: 'start_active',
|
|
||||||
$limit
|
$limit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} # end if unit_code
|
||||||
|
} # end foreach code_string
|
||||||
}
|
}
|
||||||
}
|
if ( $monitor->{AlarmInput} ) {
|
||||||
}
|
Debug("$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'");
|
||||||
if ( $monitor->{AlarmInput} )
|
foreach my $code_string ( split(',', $monitor->{AlarmInput}) ) {
|
||||||
{
|
|
||||||
Debug( "$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'\n" );
|
|
||||||
foreach my $code_string ( split( /,/, $monitor->{AlarmInput} ) )
|
|
||||||
{
|
|
||||||
#Debug( "Code string: $code_string\n" );
|
#Debug( "Code string: $code_string\n" );
|
||||||
my ( $invert, $unit_code, $modifier, $limit )
|
my ( $invert, $unit_code, $modifier, $limit )
|
||||||
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
||||||
$limit = 0 if ( !$limit );
|
$limit = 0 if !$limit;
|
||||||
if ( $unit_code )
|
if ( $unit_code ) {
|
||||||
{
|
if ( !$modifier || $modifier eq '+' ) {
|
||||||
if ( !$modifier || $modifier eq '+' )
|
|
||||||
{
|
|
||||||
addToDeviceList( $unit_code,
|
addToDeviceList( $unit_code,
|
||||||
'ON',
|
'ON',
|
||||||
$monitor,
|
$monitor,
|
||||||
!$invert ? 'start_alarm'
|
(!$invert ? 'start_alarm' : 'stop_alarm'),
|
||||||
: 'stop_alarm',
|
|
||||||
$limit
|
$limit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( !$modifier || $modifier eq '-' )
|
if ( !$modifier || $modifier eq '-' ) {
|
||||||
{
|
|
||||||
addToDeviceList( $unit_code,
|
addToDeviceList( $unit_code,
|
||||||
'OFF',
|
'OFF',
|
||||||
$monitor,
|
$monitor,
|
||||||
!$invert ? 'stop_alarm'
|
(!$invert ? 'stop_alarm' : 'start_alarm'),
|
||||||
: 'start_alarm',
|
|
||||||
$limit
|
$limit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
} # end if unit_code
|
||||||
}
|
} # end foreach code_string
|
||||||
}
|
} # end if AlarmInput
|
||||||
if ( $monitor->{AlarmOutput} )
|
if ( $monitor->{AlarmOutput} ) {
|
||||||
{
|
Debug("$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'");
|
||||||
Debug( "$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'\n" );
|
foreach my $code_string ( split( ',', $monitor->{AlarmOutput} ) ) {
|
||||||
foreach my $code_string ( split( /,/, $monitor->{AlarmOutput} ) )
|
|
||||||
{
|
|
||||||
#Debug( "Code string: $code_string\n" );
|
#Debug( "Code string: $code_string\n" );
|
||||||
my ( $invert, $unit_code, $modifier, $limit )
|
my ( $invert, $unit_code, $modifier, $limit )
|
||||||
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
= ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ );
|
||||||
$limit = 0 if ( !$limit );
|
$limit = 0 if !$limit;
|
||||||
if ( $unit_code )
|
if ( $unit_code ) {
|
||||||
{
|
if ( !$modifier || $modifier eq '+' ) {
|
||||||
if ( !$modifier || $modifier eq '+' )
|
|
||||||
{
|
|
||||||
addToMonitorList( $monitor,
|
addToMonitorList( $monitor,
|
||||||
'ON',
|
'ON',
|
||||||
$unit_code,
|
$unit_code,
|
||||||
!$invert ? 'on'
|
(!$invert ? 'on' : 'off'),
|
||||||
: 'off',
|
|
||||||
$limit
|
$limit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ( !$modifier || $modifier eq '-' )
|
if ( !$modifier || $modifier eq '-' ) {
|
||||||
{
|
|
||||||
addToMonitorList( $monitor,
|
addToMonitorList( $monitor,
|
||||||
'OFF',
|
'OFF',
|
||||||
$unit_code,
|
$unit_code,
|
||||||
!$invert ? 'off'
|
(!$invert ? 'off' : 'on'),
|
||||||
: 'on',
|
|
||||||
$limit
|
$limit
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
} # end if unit_code
|
||||||
}
|
} # end foreach code_string
|
||||||
}
|
} # end if AlarmOutput
|
||||||
zmMemInvalidate($monitor);
|
zmMemInvalidate($monitor);
|
||||||
}
|
}
|
||||||
}
|
} # end sub loadTasks
|
||||||
|
|
||||||
sub addPendingTask
|
sub addPendingTask {
|
||||||
{
|
|
||||||
my $task = shift;
|
my $task = shift;
|
||||||
|
|
||||||
# Check whether we are just extending a previous pending task
|
# Check whether we are just extending a previous pending task
|
||||||
# and remove it if it's there
|
# 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 $pending_list = $pending_tasks{$activation_time};
|
||||||
my $new_pending_list = [];
|
my $new_pending_list = [];
|
||||||
foreach my $pending_task ( @$pending_list )
|
foreach my $pending_task ( @$pending_list ) {
|
||||||
{
|
if ( $task->{type} ne $pending_task->{type} ) {
|
||||||
if ( $task->{type} ne $pending_task->{type} )
|
|
||||||
{
|
|
||||||
push( @$new_pending_list, $pending_task )
|
push( @$new_pending_list, $pending_task )
|
||||||
}
|
} elsif ( $task->{type} eq 'device' ) {
|
||||||
elsif ( $task->{type} eq 'device' )
|
|
||||||
{
|
|
||||||
if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} )
|
if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} )
|
||||||
|| ( $task->{function} ne $pending_task->{function} ))
|
|| ( $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()
|
if (( $task->{device}->{appliance}->unit_code()
|
||||||
!= $pending_task->{device}->{appliance}->unit_code()
|
!= $pending_task->{device}->{appliance}->unit_code()
|
||||||
)
|
)
|
||||||
|| ( $task->{function} ne $pending_task->{function} )
|
|| ( $task->{function} ne $pending_task->{function} )
|
||||||
)
|
) {
|
||||||
{
|
push @$new_pending_list, $pending_task;
|
||||||
push( @$new_pending_list, $pending_task )
|
|
||||||
}
|
}
|
||||||
}
|
} # end switch task->type
|
||||||
}
|
} # end foreach pending_task
|
||||||
if ( @$new_pending_list )
|
|
||||||
{
|
if ( @$new_pending_list ) {
|
||||||
$pending_tasks{$activation_time} = $new_pending_list;
|
$pending_tasks{$activation_time} = $new_pending_list;
|
||||||
|
} else {
|
||||||
|
delete $pending_tasks{$activation_time};
|
||||||
}
|
}
|
||||||
else
|
} # end foreach activation_time
|
||||||
{
|
|
||||||
delete( $pending_tasks{$activation_time} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my $end_time = time() + $task->{limit};
|
my $end_time = time() + $task->{limit};
|
||||||
my $pending_list = $pending_tasks{$end_time};
|
my $pending_list = $pending_tasks{$end_time};
|
||||||
if ( !$pending_list )
|
if ( !$pending_list ) {
|
||||||
{
|
|
||||||
$pending_list = $pending_tasks{$end_time} = [];
|
$pending_list = $pending_tasks{$end_time} = [];
|
||||||
}
|
}
|
||||||
my $pending_task;
|
my $pending_task;
|
||||||
if ( $task->{type} eq 'device' )
|
if ( $task->{type} eq 'device' ) {
|
||||||
{
|
$pending_task = {
|
||||||
$pending_task = { type=>$task->{type},
|
type=>$task->{type},
|
||||||
monitor=>$task->{monitor},
|
monitor=>$task->{monitor},
|
||||||
function=>$task->{function}
|
function=>$task->{function}
|
||||||
};
|
};
|
||||||
$pending_task->{function} =~ s/start/stop/;
|
$pending_task->{function} =~ s/start/stop/;
|
||||||
}
|
} elsif ( $task->{type} eq 'monitor' ) {
|
||||||
elsif ( $task->{type} eq 'monitor' )
|
$pending_task = {
|
||||||
{
|
type=>$task->{type},
|
||||||
$pending_task = { type=>$task->{type},
|
|
||||||
device=>$task->{device},
|
device=>$task->{device},
|
||||||
function=>$task->{function}
|
function=>$task->{function}
|
||||||
};
|
};
|
||||||
$pending_task->{function} =~ s/on/off/;
|
$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;
|
my $task = shift;
|
||||||
|
|
||||||
if ( $task->{type} eq 'device' )
|
if ( $task->{type} eq 'device' ) {
|
||||||
{
|
|
||||||
my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ );
|
my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ );
|
||||||
|
|
||||||
if ( $class eq 'active' )
|
if ( $class eq 'active' ) {
|
||||||
{
|
if ( $instruction eq 'start' ) {
|
||||||
if ( $instruction eq 'start' )
|
|
||||||
{
|
|
||||||
zmMonitorEnable($task->{monitor});
|
zmMonitorEnable($task->{monitor});
|
||||||
if ( $task->{limit} )
|
if ( $task->{limit} ) {
|
||||||
{
|
|
||||||
addPendingTask($task);
|
addPendingTask($task);
|
||||||
}
|
}
|
||||||
}
|
} elsif( $instruction eq 'stop' ) {
|
||||||
elsif( $instruction eq 'stop' )
|
|
||||||
{
|
|
||||||
zmMonitorDisable($task->{monitor});
|
zmMonitorDisable($task->{monitor});
|
||||||
}
|
}
|
||||||
}
|
} elsif( $class eq 'alarm' ) {
|
||||||
elsif( $class eq 'alarm' )
|
if ( $instruction eq 'start' ) {
|
||||||
{
|
zmTriggerEventOn(
|
||||||
if ( $instruction eq 'start' )
|
$task->{monitor},
|
||||||
{
|
|
||||||
zmTriggerEventOn( $task->{monitor},
|
|
||||||
0,
|
0,
|
||||||
main::CAUSE_STRING,
|
main::CAUSE_STRING,
|
||||||
$task->{address}
|
$task->{address}
|
||||||
);
|
);
|
||||||
if ( $task->{limit} )
|
if ( $task->{limit} ) {
|
||||||
{
|
|
||||||
addPendingTask($task);
|
addPendingTask($task);
|
||||||
}
|
}
|
||||||
}
|
} elsif( $instruction eq 'stop' ) {
|
||||||
elsif( $instruction eq 'stop' )
|
|
||||||
{
|
|
||||||
zmTriggerEventCancel($task->{monitor});
|
zmTriggerEventCancel($task->{monitor});
|
||||||
}
|
}
|
||||||
}
|
} # end switch class
|
||||||
}
|
} elsif( $task->{type} eq 'monitor' ) {
|
||||||
elsif( $task->{type} eq 'monitor' )
|
if ( $task->{function} eq 'on' ) {
|
||||||
{
|
|
||||||
if ( $task->{function} eq 'on' )
|
|
||||||
{
|
|
||||||
$task->{device}->{appliance}->on();
|
$task->{device}->{appliance}->on();
|
||||||
if ( $task->{limit} )
|
if ( $task->{limit} ) {
|
||||||
{
|
|
||||||
addPendingTask($task);
|
addPendingTask($task);
|
||||||
}
|
}
|
||||||
}
|
} elsif ( $task->{function} eq 'off' ) {
|
||||||
elsif ( $task->{function} eq 'off' )
|
|
||||||
{
|
|
||||||
$task->{device}->{appliance}->off();
|
$task->{device}->{appliance}->off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub dPrint
|
sub dPrint {
|
||||||
{
|
|
||||||
my $dbg_level = shift;
|
my $dbg_level = shift;
|
||||||
if ( fileno(CLIENT) )
|
if ( fileno(CLIENT) ) {
|
||||||
{
|
|
||||||
print CLIENT @_
|
print CLIENT @_
|
||||||
}
|
}
|
||||||
if ( $dbg_level == ZoneMinder::Logger::DEBUG )
|
if ( $dbg_level == ZoneMinder::Logger::DEBUG ) {
|
||||||
{
|
|
||||||
Debug(@_);
|
Debug(@_);
|
||||||
}
|
} elsif ( $dbg_level == ZoneMinder::Logger::INFO ) {
|
||||||
elsif ( $dbg_level == ZoneMinder::Logger::INFO )
|
|
||||||
{
|
|
||||||
Info(@_);
|
Info(@_);
|
||||||
}
|
} elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) {
|
||||||
elsif ( $dbg_level == ZoneMinder::Logger::WARNING )
|
|
||||||
{
|
|
||||||
Warning(@_);
|
Warning(@_);
|
||||||
}
|
}
|
||||||
elsif ( $dbg_level == ZoneMinder::Logger::ERROR )
|
elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) {
|
||||||
{
|
|
||||||
Error( @_ );
|
Error( @_ );
|
||||||
}
|
} elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) {
|
||||||
elsif ( $dbg_level == ZoneMinder::Logger::FATAL )
|
|
||||||
{
|
|
||||||
Fatal( @_ );
|
Fatal( @_ );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub x10listen
|
sub x10listen {
|
||||||
{
|
foreach my $event ( @_ ) {
|
||||||
foreach my $event ( @_ )
|
|
||||||
{
|
|
||||||
#print( Data::Dumper( $_ )."\n" );
|
#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 $unit_code = $event->unit_code();
|
||||||
my $device = $device_hash{$unit_code};
|
my $device = $device_hash{$unit_code};
|
||||||
if ( !$device )
|
if ( !$device ) {
|
||||||
{
|
$device = $device_hash{$unit_code} = {
|
||||||
$device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ),
|
appliance=>$x10->Appliance(unit_code=>$unit_code),
|
||||||
status=>'unknown'
|
status=>'unknown'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
next if ( $event->func() !~ /(?:ON|OFF)/ );
|
next if ( $event->func() !~ /(?:ON|OFF)/ );
|
||||||
$device->{status} = $event->func();
|
$device->{status} = $event->func();
|
||||||
my $task_list = $device->{$event->func().'_list'};
|
my $task_list = $device->{$event->func().'_list'};
|
||||||
if ( $task_list )
|
if ( $task_list ) {
|
||||||
{
|
foreach my $task ( @$task_list ) {
|
||||||
foreach my $task ( @$task_list )
|
|
||||||
{
|
|
||||||
processTask($task);
|
processTask($task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} # end if correct house code
|
||||||
|
Info('Got event - '.$event->as_string());
|
||||||
}
|
}
|
||||||
Info( "Got event - ".$event->as_string()."\n" );
|
} # end sub x10listen
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
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
|
// Class used for storing a box, which is defined as a region
|
||||||
// defined by two coordinates
|
// defined by two coordinates
|
||||||
//
|
//
|
||||||
class Box
|
class Box {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
Coord lo, hi;
|
Coord lo, hi;
|
||||||
Coord size;
|
Coord size;
|
||||||
|
|
||||||
public:
|
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 ) ) { }
|
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 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( 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 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 const Coord &Lo() const { return lo; }
|
||||||
inline int LoX() const { return( lo.X() ); }
|
inline int LoX() const { return lo.X(); }
|
||||||
inline int LoY() const { return( lo.Y() ); }
|
inline int LoY() const { return lo.Y(); }
|
||||||
inline const Coord &Hi() const { return( hi ); }
|
inline const Coord &Hi() const { return hi; }
|
||||||
inline int HiX() const { return( hi.X() ); }
|
inline int HiX() const { return hi.X(); }
|
||||||
inline int HiY() const { return( hi.Y() ); }
|
inline int HiY() const { return hi.Y(); }
|
||||||
inline const Coord &Size() const { return( size ); }
|
inline const Coord &Size() const { return size; }
|
||||||
inline int Width() const { return( size.X() ); }
|
inline int Width() const { return size.X(); }
|
||||||
inline int Height() const { return( size.Y() ); }
|
inline int Height() const { return size.Y(); }
|
||||||
inline int Area() const { return( size.X()*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_x = int(round(lo.X()+(size.X()/2.0)));
|
||||||
int mid_y = int(round(lo.Y()+(size.Y()/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
|
inline bool Inside( const Coord &coord ) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -64,7 +64,9 @@ void zmLoadConfig() {
|
||||||
closedir(configSubFolder);
|
closedir(configSubFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
zmDbConnect();
|
if ( !zmDbConnect() ) {
|
||||||
|
Fatal("Can't connect to db. Can't continue.");
|
||||||
|
}
|
||||||
config.Load();
|
config.Load();
|
||||||
config.Assign();
|
config.Assign();
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
//
|
//
|
||||||
// Class used for storing an x,y pair, i.e. a coordinate
|
// Class used for storing an x,y pair, i.e. a coordinate
|
||||||
//
|
//
|
||||||
class Coord
|
class Coord {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
|
@ -44,8 +43,7 @@ public:
|
||||||
inline int &Y() { return( y ); }
|
inline int &Y() { return( y ); }
|
||||||
inline const int &Y() const { 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 );
|
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,6 +259,20 @@ Event::~Event() {
|
||||||
if ( frame_data.size() )
|
if ( frame_data.size() )
|
||||||
WriteDbFrames();
|
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
|
// Should not be static because we might be multi-threaded
|
||||||
char sql[ZM_SQL_LGE_BUFSIZ];
|
char sql[ZM_SQL_LGE_BUFSIZ];
|
||||||
snprintf(sql, sizeof(sql),
|
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,
|
// 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.
|
// so I am changing this to 1, but we should overwrite it later with a better snapshot.
|
||||||
if ( frames == 1 ) {
|
if ( frames == 1 ) {
|
||||||
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file);
|
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DeltaTimeval delta_time;
|
struct DeltaTimeval delta_time;
|
||||||
|
@ -553,6 +567,27 @@ void Event::WriteDbFrames() {
|
||||||
db_mutex.unlock();
|
db_mutex.unlock();
|
||||||
} // end void Event::WriteDbFrames()
|
} // 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) {
|
void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) {
|
||||||
if ( !timestamp.tv_sec ) {
|
if ( !timestamp.tv_sec ) {
|
||||||
Debug(1, "Not adding new frame, zero timestamp");
|
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++;
|
frames++;
|
||||||
|
|
||||||
bool write_to_db = false;
|
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 ) {
|
if ( save_jpegs & 1 ) {
|
||||||
static char event_file[PATH_MAX];
|
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 this is the first frame, we should add a thumbnail to the event directory
|
||||||
if ( (frames == 1) || (score > (int)max_score) ) {
|
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.
|
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
|
// We are writing an Alarm frame
|
||||||
if ( frame_type == ALARM ) {
|
if ( frame_type == ALARM ) {
|
||||||
// The first frame with a score will be the frame that alarmed the event
|
// 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;
|
alarm_frame_written = true;
|
||||||
WriteFrameImage(image, timestamp, alarm_file.c_str());
|
WriteFrameImage(image, timestamp, alarm_file.c_str());
|
||||||
}
|
}
|
||||||
if ( videowriter != NULL ) {
|
alarm_frames++;
|
||||||
WriteFrameVideo(image, timestamp, videowriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DeltaTimeval delta_time;
|
tot_score += score;
|
||||||
DELTA_TIMEVAL(delta_time, timestamp, start_time, DT_PREC_2);
|
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) ;
|
bool db_frame = ( frame_type != BULK ) || (frames==1) || ((frames%config.bulk_frame_interval)==0) ;
|
||||||
if ( db_frame ) {
|
if ( db_frame ) {
|
||||||
|
|
||||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
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));
|
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
|
||||||
if ( write_to_db || ( frame_data.size() > 20 ) ) {
|
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;
|
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)
|
} // 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 snapshot_file;
|
||||||
std::string alarm_file;
|
std::string alarm_file;
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
std::string snapshot_file;
|
|
||||||
std::string alarm_file;
|
|
||||||
|
|
||||||
VideoWriter* videowriter;
|
VideoWriter* videowriter;
|
||||||
char video_name[PATH_MAX];
|
char video_name[PATH_MAX];
|
||||||
|
@ -137,6 +135,7 @@ class Event {
|
||||||
private:
|
private:
|
||||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||||
void WriteDbFrames();
|
void WriteDbFrames();
|
||||||
|
void UpdateFramesDelta(double offset);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const char *getSubPath( struct tm *time ) {
|
static const char *getSubPath( struct tm *time ) {
|
||||||
|
|
|
@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
|
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
|
||||||
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
|
"(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) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
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->scheme = Storage::SHALLOW;
|
||||||
}
|
}
|
||||||
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
|
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);
|
mysql_free_result(result);
|
||||||
|
|
||||||
Storage * storage = new Storage(event_data->storage_id);
|
Storage * storage = new Storage(event_data->storage_id);
|
||||||
|
@ -703,6 +704,34 @@ Debug(1, "Loading image");
|
||||||
Error("Failed getting a frame.");
|
Error("Failed getting a frame.");
|
||||||
return false;
|
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 {
|
} else {
|
||||||
Error("Unable to get a frame");
|
Error("Unable to get a frame");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -66,6 +66,7 @@ class EventStream : public StreamBase {
|
||||||
char video_file[PATH_MAX];
|
char video_file[PATH_MAX];
|
||||||
Storage::Schemes scheme;
|
Storage::Schemes scheme;
|
||||||
int SaveJPEGs;
|
int SaveJPEGs;
|
||||||
|
Monitor::Orientation Orientation;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -81,7 +81,7 @@ void FFMPEGInit() {
|
||||||
av_log_set_callback(log_libav_callback);
|
av_log_set_callback(log_libav_callback);
|
||||||
Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options");
|
Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options");
|
||||||
} else {
|
} 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);
|
av_log_set_level(AV_LOG_QUIET);
|
||||||
}
|
}
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)
|
#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)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
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,
|
par->codec_type,
|
||||||
|
av_get_media_type_string(par->codec_type),
|
||||||
par->codec_id,
|
par->codec_id,
|
||||||
avcodec_get_name(par->codec_id),
|
avcodec_get_name(par->codec_id),
|
||||||
par->codec_tag,
|
par->codec_tag,
|
||||||
|
@ -300,7 +301,7 @@ void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
||||||
par->height,
|
par->height,
|
||||||
par->bit_rate,
|
par->bit_rate,
|
||||||
par->format,
|
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
|
#endif
|
||||||
|
|
|
@ -402,19 +402,19 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
Debug(1, "Selected hw_pix_fmt %d %s",
|
Debug(1, "Selected hw_pix_fmt %d %s",
|
||||||
hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt));
|
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,
|
ret = av_hwdevice_ctx_create(&hw_device_ctx, type,
|
||||||
(hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0);
|
(hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0);
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
Error("Failed to create hwaccel device.");
|
Error("Failed to create hwaccel device. %s",av_make_error_string(ret).c_str());
|
||||||
return -1;
|
hw_pix_fmt = AV_PIX_FMT_NONE;
|
||||||
}
|
} else {
|
||||||
Debug(1, "Created hwdevice for %s", hwaccel_device.c_str());
|
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);
|
mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx);
|
||||||
hwFrame = zm_av_frame_alloc();
|
hwFrame = zm_av_frame_alloc();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Failed to setup hwaccel.");
|
Debug(1, "Failed to find suitable hw_pix_fmt.");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
Debug(1, "AVCodec not new enough for hwaccel");
|
Debug(1, "AVCodec not new enough for hwaccel");
|
||||||
|
@ -422,7 +422,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
#else
|
#else
|
||||||
Warning("HWAccel support not compiled in.");
|
Warning("HWAccel support not compiled in.");
|
||||||
#endif
|
#endif
|
||||||
} // end if hwacel_name
|
} // end if hwaccel_name
|
||||||
|
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||||
ret = avcodec_open(mVideoCodecContext, mVideoCodec);
|
ret = avcodec_open(mVideoCodecContext, mVideoCodec);
|
||||||
|
@ -542,7 +542,6 @@ int FfmpegCamera::Close() {
|
||||||
return 0;
|
return 0;
|
||||||
} // end FfmpegCamera::Close
|
} // end FfmpegCamera::Close
|
||||||
|
|
||||||
|
|
||||||
int FfmpegCamera::transfer_to_image(
|
int FfmpegCamera::transfer_to_image(
|
||||||
Image &image,
|
Image &image,
|
||||||
AVFrame *output_frame,
|
AVFrame *output_frame,
|
||||||
|
@ -557,9 +556,12 @@ int FfmpegCamera::transfer_to_image(
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
#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(
|
int size = av_image_fill_arrays(
|
||||||
output_frame->data, output_frame->linesize,
|
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 ) {
|
if ( size < 0 ) {
|
||||||
Error("Problem setting up data pointers into image %s",
|
Error("Problem setting up data pointers into image %s",
|
||||||
av_make_error_string(size).c_str());
|
av_make_error_string(size).c_str());
|
||||||
|
@ -598,19 +600,31 @@ int FfmpegCamera::transfer_to_image(
|
||||||
mConvertContext, input_frame->data, input_frame->linesize,
|
mConvertContext, input_frame->data, input_frame->linesize,
|
||||||
0, mVideoCodecContext->height,
|
0, mVideoCodecContext->height,
|
||||||
output_frame->data, output_frame->linesize);
|
output_frame->data, output_frame->linesize);
|
||||||
if ( ret <= 0 ) {
|
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",
|
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->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,
|
imagePixFormat,
|
||||||
av_get_pix_fmt_name(imagePixFormat),
|
av_get_pix_fmt_name(imagePixFormat),
|
||||||
output_frame->linesize,
|
output_frame->linesize[0], output_frame->linesize[1],
|
||||||
frameCount,
|
frameCount,
|
||||||
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
|
||||||
|
mVideoCodecContext->height,
|
||||||
ret
|
ret
|
||||||
);
|
);
|
||||||
return -1;
|
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
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal("You must compile ffmpeg with the --enable-swscale "
|
Fatal("You must compile ffmpeg with the --enable-swscale "
|
||||||
"option to use ffmpeg cameras");
|
"option to use ffmpeg cameras");
|
||||||
|
|
|
@ -53,6 +53,7 @@ class FfmpegCamera : public Camera {
|
||||||
AVCodec *mAudioCodec;
|
AVCodec *mAudioCodec;
|
||||||
AVFrame *mRawFrame;
|
AVFrame *mRawFrame;
|
||||||
AVFrame *mFrame;
|
AVFrame *mFrame;
|
||||||
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
|
|
||||||
AVFrame *input_frame; // Use to point to mRawFrame or hwFrame;
|
AVFrame *input_frame; // Use to point to mRawFrame or hwFrame;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ FFmpeg_Input::FFmpeg_Input() {
|
||||||
FFMPEGInit();
|
FFMPEGInit();
|
||||||
streams = NULL;
|
streams = NULL;
|
||||||
frame = NULL;
|
frame = NULL;
|
||||||
|
last_seek_request = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFmpeg_Input::~FFmpeg_Input() {
|
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 FFmpeg_Input::Open(const char *filepath) {
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
|
@ -127,7 +139,6 @@ int FFmpeg_Input::Close( ) {
|
||||||
} // end int FFmpeg_Input::Close()
|
} // end int FFmpeg_Input::Close()
|
||||||
|
|
||||||
AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
||||||
Debug(1, "Getting frame from stream %d", stream_id);
|
|
||||||
|
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
|
@ -166,12 +177,14 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) {
|
||||||
frame = zm_av_frame_alloc();
|
frame = zm_av_frame_alloc();
|
||||||
}
|
}
|
||||||
ret = zm_send_packet_receive_frame(context, frame, packet);
|
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",
|
Error("Unable to decode frame at frame %d: %s, continuing",
|
||||||
streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str());
|
streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str());
|
||||||
zm_av_packet_unref(&packet);
|
zm_av_packet_unref(&packet);
|
||||||
av_frame_free(&frame);
|
av_frame_free(&frame);
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
zm_dump_frame(frame, "resulting frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
frameComplete = 1;
|
frameComplete = 1;
|
||||||
|
@ -203,7 +216,18 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
|
||||||
get_frame(stream_id);
|
get_frame(stream_id);
|
||||||
} // end if ! frame
|
} // 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");
|
zm_dump_frame(frame, "frame->pts > seek_target, seek backwards");
|
||||||
// our frame must be beyond our seek target. so go backwards to before it
|
// 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,
|
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");
|
zm_dump_frame(frame, "frame->pts > seek_target, got");
|
||||||
} // end if frame->pts > seek_target
|
} // 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.
|
// 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 ) {
|
if ( frame->pts <= seek_target ) {
|
||||||
zm_dump_frame(frame, "pts <= seek_target");
|
zm_dump_frame(frame, "pts <= seek_target");
|
||||||
|
|
|
@ -42,6 +42,7 @@ class FFmpeg_Input {
|
||||||
int audio_stream_id;
|
int audio_stream_id;
|
||||||
AVFormatContext *input_format_context;
|
AVFormatContext *input_format_context;
|
||||||
AVFrame *frame;
|
AVFrame *frame;
|
||||||
|
int64_t last_seek_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -165,8 +165,10 @@ Image::Image( const AVFrame *frame ) {
|
||||||
width = frame->width;
|
width = frame->width;
|
||||||
height = frame->height;
|
height = frame->height;
|
||||||
pixels = width*height;
|
pixels = width*height;
|
||||||
|
|
||||||
colours = ZM_COLOUR_RGB32;
|
colours = ZM_COLOUR_RGB32;
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
|
|
||||||
size = pixels*colours;
|
size = pixels*colours;
|
||||||
buffer = 0;
|
buffer = 0;
|
||||||
holdbuffer = 0;
|
holdbuffer = 0;
|
||||||
|
|
|
@ -157,6 +157,7 @@ void Logger::initialise(const std::string &id, const Options &options) {
|
||||||
if ( options.mTerminalLevel != NOOPT )
|
if ( options.mTerminalLevel != NOOPT )
|
||||||
tempTerminalLevel = options.mTerminalLevel;
|
tempTerminalLevel = options.mTerminalLevel;
|
||||||
|
|
||||||
|
// DEBUG1 == 1. So >= DEBUG1, we set to DEBUG9?! Why?
|
||||||
if ( options.mDatabaseLevel != NOOPT )
|
if ( options.mDatabaseLevel != NOOPT )
|
||||||
tempDatabaseLevel = options.mDatabaseLevel;
|
tempDatabaseLevel = options.mDatabaseLevel;
|
||||||
else
|
else
|
||||||
|
@ -358,7 +359,7 @@ Logger::Level Logger::databaseLevel(Logger::Level databaseLevel) {
|
||||||
if ( databaseLevel > NOOPT ) {
|
if ( databaseLevel > NOOPT ) {
|
||||||
databaseLevel = limit(databaseLevel);
|
databaseLevel = limit(databaseLevel);
|
||||||
if ( mDatabaseLevel != databaseLevel ) {
|
if ( mDatabaseLevel != databaseLevel ) {
|
||||||
if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) {
|
if ( (databaseLevel > NOLOG) && (mDatabaseLevel <= NOLOG) ) { // <= NOLOG would be NOOPT
|
||||||
if ( !zmDbConnect() ) {
|
if ( !zmDbConnect() ) {
|
||||||
databaseLevel = NOLOG;
|
databaseLevel = NOLOG;
|
||||||
}
|
}
|
||||||
|
@ -534,8 +535,11 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
if ( level <= mFileLevel ) {
|
if ( level <= mFileLevel ) {
|
||||||
if ( !mLogFileFP )
|
if ( !mLogFileFP ) {
|
||||||
|
log_mutex.unlock();
|
||||||
openFile();
|
openFile();
|
||||||
|
log_mutex.lock();
|
||||||
|
}
|
||||||
if ( mLogFileFP ) {
|
if ( mLogFileFP ) {
|
||||||
fputs(logString, mLogFileFP);
|
fputs(logString, mLogFileFP);
|
||||||
if ( mFlush )
|
if ( mFlush )
|
||||||
|
@ -553,8 +557,10 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
||||||
if ( level <= mDatabaseLevel ) {
|
if ( level <= mDatabaseLevel ) {
|
||||||
|
|
||||||
if ( !db_mutex.trylock() ) {
|
if ( !db_mutex.trylock() ) {
|
||||||
|
char escapedString[(strlen(syslogStart)*2)+1];
|
||||||
mysql_real_escape_string(&dbconn, escapedString, syslogStart, strlen(syslogStart));
|
mysql_real_escape_string(&dbconn, escapedString, syslogStart, strlen(syslogStart));
|
||||||
|
|
||||||
|
char sql[ZM_SQL_MED_BUFSIZ];
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"INSERT INTO `Logs` "
|
"INSERT INTO `Logs` "
|
||||||
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
|
"( `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 {
|
} else {
|
||||||
Level tempDatabaseLevel = mDatabaseLevel;
|
Level tempDatabaseLevel = mDatabaseLevel;
|
||||||
databaseLevel(NOLOG);
|
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);
|
databaseLevel(tempDatabaseLevel);
|
||||||
}
|
}
|
||||||
db_mutex.unlock();
|
db_mutex.unlock();
|
||||||
|
|
|
@ -288,6 +288,8 @@ Monitor::Monitor()
|
||||||
orientation(ROTATE_0),
|
orientation(ROTATE_0),
|
||||||
deinterlacing(0),
|
deinterlacing(0),
|
||||||
deinterlacing_value(0),
|
deinterlacing_value(0),
|
||||||
|
decoder_hwaccel_name(""),
|
||||||
|
decoder_hwaccel_device(""),
|
||||||
videoRecording(0),
|
videoRecording(0),
|
||||||
rtsp_describe(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++;
|
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++;
|
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] )
|
if ( dbrow[col] )
|
||||||
strncpy(device, dbrow[col], sizeof(device)-1);
|
strncpy(device, dbrow[col], sizeof(device)-1);
|
||||||
else
|
else
|
||||||
|
@ -545,6 +553,8 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
last_signal = false;
|
last_signal = false;
|
||||||
|
|
||||||
camera = NULL;
|
camera = NULL;
|
||||||
|
uint64_t image_size = width * height * colours;
|
||||||
|
|
||||||
mem_size = sizeof(SharedData)
|
mem_size = sizeof(SharedData)
|
||||||
+ sizeof(TriggerData)
|
+ sizeof(TriggerData)
|
||||||
+ sizeof(VideoStoreData) //Information to pass back to the capture process
|
+ 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(mem_size),
|
||||||
sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData),
|
sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData),
|
||||||
(image_buffer_count*sizeof(struct timeval)),
|
(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_size);
|
||||||
mem_ptr = NULL;
|
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() ),
|
//ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ),
|
||||||
Debug(1, "Loaded monitor %d(%s), %d zones", id, name, n_zones);
|
Debug(1, "Loaded monitor %d(%s), %d zones", id, name, n_zones);
|
||||||
getCamera();
|
getCamera();
|
||||||
|
|
|
@ -257,10 +257,10 @@ protected:
|
||||||
Orientation orientation; // Whether the image has to be rotated at all
|
Orientation orientation; // Whether the image has to be rotated at all
|
||||||
unsigned int deinterlacing;
|
unsigned int deinterlacing;
|
||||||
unsigned int deinterlacing_value;
|
unsigned int deinterlacing_value;
|
||||||
bool videoRecording;
|
|
||||||
bool rtsp_describe;
|
|
||||||
std::string decoder_hwaccel_name;
|
std::string decoder_hwaccel_name;
|
||||||
std::string decoder_hwaccel_device;
|
std::string decoder_hwaccel_device;
|
||||||
|
bool videoRecording;
|
||||||
|
bool rtsp_describe;
|
||||||
|
|
||||||
int savejpegs;
|
int savejpegs;
|
||||||
int colours;
|
int colours;
|
||||||
|
@ -297,6 +297,7 @@ protected:
|
||||||
int frame_skip; // How many frames to skip in continuous modes
|
int frame_skip; // How many frames to skip in continuous modes
|
||||||
int motion_frame_skip; // How many frames to skip in motion detection
|
int motion_frame_skip; // How many frames to skip in motion detection
|
||||||
double analysis_fps_limit; // Target framerate for video analysis
|
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
|
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||||
int capture_delay; // How long we wait between capture frames
|
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
|
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; }
|
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; }
|
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; };
|
unsigned int GetPreEventCount() const { return pre_event_count; };
|
||||||
|
struct timeval GetVideoBufferDuration() const { return video_buffer_duration; };
|
||||||
int GetImageBufferCount() const { return image_buffer_count; };
|
int GetImageBufferCount() const { return image_buffer_count; };
|
||||||
State GetState() const;
|
State GetState() const;
|
||||||
int GetImage( int index=-1, int scale=100 );
|
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",
|
Warning("Frame send time %d msec too slow, throttling maxfps to %.2f",
|
||||||
frameSendTime, maxfps);
|
frameSendTime, maxfps);
|
||||||
}
|
}
|
||||||
}
|
} // Not mpeg
|
||||||
last_frame_sent = TV_2_FLOAT(now);
|
last_frame_sent = TV_2_FLOAT(now);
|
||||||
return true;
|
return true;
|
||||||
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
||||||
|
@ -478,6 +478,7 @@ void MonitorStream::runStream() {
|
||||||
Image *paused_image = NULL;
|
Image *paused_image = NULL;
|
||||||
struct timeval paused_timestamp;
|
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
|
// 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;
|
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 subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
|
||||||
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
|
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 ) {
|
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);
|
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
|
||||||
} else {
|
} else {
|
||||||
|
@ -612,8 +611,9 @@ void MonitorStream::runStream() {
|
||||||
if ( temp_index%frame_mod == 0 ) {
|
if ( temp_index%frame_mod == 0 ) {
|
||||||
Debug(2, "Sending delayed frame %d", temp_index);
|
Debug(2, "Sending delayed frame %d", temp_index);
|
||||||
// Send the next frame
|
// 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;
|
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;
|
// frame_sent = true;
|
||||||
}
|
}
|
||||||
|
@ -626,9 +626,17 @@ void MonitorStream::runStream() {
|
||||||
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
||||||
|
|
||||||
// Send the next frame
|
// 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;
|
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;
|
// frame_sent = true;
|
||||||
step = 0;
|
step = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -636,12 +644,13 @@ void MonitorStream::runStream() {
|
||||||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||||
|
|
||||||
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
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
|
// Send keepalive
|
||||||
Debug(2, "Sending keepalive frame %d", temp_index);
|
Debug(2, "Sending keepalive frame %d", temp_index);
|
||||||
// Send the next frame
|
// 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;
|
zm_terminate = true;
|
||||||
|
}
|
||||||
// frame_sent = true;
|
// frame_sent = true;
|
||||||
}
|
}
|
||||||
} // end if (!paused) or step or paused
|
} // end if (!paused) or step or paused
|
||||||
|
@ -658,17 +667,9 @@ void MonitorStream::runStream() {
|
||||||
}
|
}
|
||||||
} // end if ( buffered_playback && delayed )
|
} // end if ( buffered_playback && delayed )
|
||||||
|
|
||||||
|
if ( last_read_index != monitor->shared_data->last_write_index ) {
|
||||||
// have a new image to send
|
// have a new image to send
|
||||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
|
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 ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
|
||||||
if ( !paused && !delayed ) {
|
if ( !paused && !delayed ) {
|
||||||
last_read_index = monitor->shared_data->last_write_index;
|
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);
|
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||||
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size );
|
fprintf(stdout,
|
||||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
"Content-Length: %d\r\n"
|
||||||
|
"Content-Type: image/jpeg\r\n\r\n",
|
||||||
|
img_buffer_size);
|
||||||
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -859,12 +862,13 @@ void MonitorStream::SingleImageRaw( int scale ) {
|
||||||
monitor->TimestampImage(snap_image, snap->timestamp);
|
monitor->TimestampImage(snap_image, snap->timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() );
|
fprintf(stdout,
|
||||||
fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" );
|
"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);
|
fwrite(snap_image->Buffer(), snap_image->Size(), 1, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_ZLIB_H
|
#ifdef HAVE_ZLIB_H
|
||||||
void MonitorStream::SingleImageZip(int scale) {
|
void MonitorStream::SingleImageZip(int scale) {
|
||||||
unsigned long img_buffer_size = 0;
|
unsigned long img_buffer_size = 0;
|
||||||
|
@ -884,8 +888,10 @@ void MonitorStream::SingleImageZip( int scale ) {
|
||||||
}
|
}
|
||||||
snap_image->Zip(img_buffer, &img_buffer_size);
|
snap_image->Zip(img_buffer, &img_buffer_size);
|
||||||
|
|
||||||
fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size );
|
fprintf(stdout,
|
||||||
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
|
"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);
|
fwrite(img_buffer, img_buffer_size, 1, stdout);
|
||||||
}
|
}
|
||||||
#endif // HAVE_ZLIB_H
|
#endif // HAVE_ZLIB_H
|
||||||
|
|
|
@ -235,6 +235,73 @@ void zm_packetqueue::clearQueue() {
|
||||||
mutex.unlock();
|
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() {
|
unsigned int zm_packetqueue::size() {
|
||||||
return pktQueue.size();
|
return pktQueue.size();
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ class zm_packetqueue {
|
||||||
bool popVideoPacket(ZMPacket* packet);
|
bool popVideoPacket(ZMPacket* packet);
|
||||||
bool popAudioPacket(ZMPacket* packet);
|
bool popAudioPacket(ZMPacket* packet);
|
||||||
unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id);
|
unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id);
|
||||||
|
unsigned int clearQueue(struct timeval *duration, int streamid);
|
||||||
void clearQueue();
|
void clearQueue();
|
||||||
void dumpQueue();
|
void dumpQueue();
|
||||||
unsigned int size();
|
unsigned int size();
|
||||||
|
|
|
@ -26,11 +26,9 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Polygon::calcArea()
|
void Polygon::calcArea() {
|
||||||
{
|
|
||||||
double float_area = 0.0L;
|
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;
|
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||||
float_area += trap_area;
|
float_area += trap_area;
|
||||||
//printf( "%.2f (%.2f)\n", 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));
|
area = (int)round(fabs(float_area));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Polygon::calcCentre()
|
void Polygon::calcCentre() {
|
||||||
{
|
|
||||||
if ( !area && n_coords )
|
if ( !area && n_coords )
|
||||||
calcArea();
|
calcArea();
|
||||||
double float_x = 0.0L, float_y = 0.0L;
|
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_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)));
|
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) );
|
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];
|
coords = new Coord[n_coords];
|
||||||
|
|
||||||
int min_x = -1;
|
int min_x = -1;
|
||||||
int max_x = -1;
|
int max_x = -1;
|
||||||
int min_y = -1;
|
int min_y = -1;
|
||||||
int max_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];
|
coords[i] = p_coords[i];
|
||||||
if ( min_x == -1 || coords[i].X() < min_x )
|
if ( min_x == -1 || coords[i].X() < min_x )
|
||||||
min_x = coords[i].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();
|
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];
|
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];
|
coords[i] = p_polygon.coords[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
Polygon &Polygon::operator=( const Polygon &p_polygon ) {
|
||||||
{
|
if ( n_coords < p_polygon.n_coords ) {
|
||||||
if ( n_coords < p_polygon.n_coords )
|
|
||||||
{
|
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
coords = new Coord[p_polygon.n_coords];
|
coords = new Coord[p_polygon.n_coords];
|
||||||
}
|
}
|
||||||
n_coords = 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];
|
coords[i] = p_polygon.coords[i];
|
||||||
}
|
}
|
||||||
extent = p_polygon.extent;
|
extent = p_polygon.extent;
|
||||||
area = p_polygon.area;
|
area = p_polygon.area;
|
||||||
centre = p_polygon.centre;
|
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;
|
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()) )
|
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].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()))
|
&& (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;
|
inside = !inside;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( inside );
|
return inside;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,13 +41,13 @@ protected:
|
||||||
static int CompareYX( const void *p1, const void *p2 ) {
|
static int CompareYX( const void *p1, const void *p2 ) {
|
||||||
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
|
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
|
||||||
if ( e1->min_y == e2->min_y )
|
if ( e1->min_y == e2->min_y )
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return int(e1->min_x - e2->min_x);
|
||||||
else
|
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 ) {
|
static int CompareX( const void *p1, const void *p2 ) {
|
||||||
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(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 );
|
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 {
|
inline const Coord &getCoord( int index ) const {
|
||||||
return( coords[index] );
|
return coords[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Box &Extent() const { return( extent ); }
|
inline const Box &Extent() const { return extent; }
|
||||||
inline int LoX() const { return( extent.LoX() ); }
|
inline int LoX() const { return extent.LoX(); }
|
||||||
inline int HiX() const { return( extent.HiX() ); }
|
inline int HiX() const { return extent.HiX(); }
|
||||||
inline int LoY() const { return( extent.LoY() ); }
|
inline int LoY() const { return extent.LoY(); }
|
||||||
inline int HiY() const { return( extent.HiY() ); }
|
inline int HiY() const { return extent.HiY(); }
|
||||||
inline int Width() const { return( extent.Width() ); }
|
inline int Width() const { return extent.Width(); }
|
||||||
inline int Height() const { return( extent.Height() ); }
|
inline int Height() const { return extent.Height(); }
|
||||||
|
|
||||||
inline int Area() const { return( area ); }
|
inline int Area() const { return area; }
|
||||||
inline const Coord &Centre() const {
|
inline const Coord &Centre() const {
|
||||||
return( centre );
|
return centre;
|
||||||
}
|
}
|
||||||
bool isInside( const Coord &coord ) const;
|
bool isInside( const Coord &coord ) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -235,7 +235,7 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
||||||
} else {
|
} else {
|
||||||
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) {
|
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) {
|
||||||
Error( "Can't ioctl(): %s", strerror(errno) );
|
Error( "Can't ioctl(): %s", strerror(errno) );
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( total_bytes_to_read == 0 ) {
|
if ( total_bytes_to_read == 0 ) {
|
||||||
|
@ -250,13 +250,13 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
||||||
return -1;
|
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.
|
// 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
|
// 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.
|
// then we have an exception on our socket.. but no data.
|
||||||
Debug(3, "Socket closed remotely");
|
Debug(3, "Socket closed remotely");
|
||||||
//Disconnect(); // Disconnect is done outside of ReadData now.
|
//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.
|
// 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;
|
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 RemoteCameraHttp::GetResponse() {
|
||||||
int buffer_len;
|
int buffer_len;
|
||||||
#if HAVE_LIBPCRE
|
#if HAVE_LIBPCRE
|
||||||
|
@ -315,9 +327,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
switch( state ) {
|
switch( state ) {
|
||||||
case HEADER :
|
case HEADER :
|
||||||
{
|
{
|
||||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
buffer_len = GetData();
|
||||||
Debug(4, "Timeout waiting for REGEXP HEADER");
|
|
||||||
}
|
|
||||||
if ( buffer_len < 0 ) {
|
if ( buffer_len < 0 ) {
|
||||||
Error("Unable to read header data");
|
Error("Unable to read header data");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -457,9 +467,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
} else {
|
} else {
|
||||||
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
||||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
buffer_len = GetData();
|
||||||
Debug(4, "Timeout waiting to extract subheader");
|
|
||||||
}
|
|
||||||
if ( buffer_len < 0 ) {
|
if ( buffer_len < 0 ) {
|
||||||
Error( "Unable to extract subheader data" );
|
Error( "Unable to extract subheader data" );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -491,7 +499,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
if ( content_length ) {
|
if ( content_length ) {
|
||||||
while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) {
|
while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) {
|
||||||
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
|
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 ) {
|
if ( bytes_read < 0 ) {
|
||||||
Error( "Unable to read content" );
|
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 );
|
Debug( 3, "Got end of image by length, content-length = %d", content_length );
|
||||||
} else {
|
} else {
|
||||||
while ( !content_length ) {
|
while ( !content_length ) {
|
||||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
buffer_len = GetData();
|
||||||
Debug(4, "Timeout waiting for content");
|
|
||||||
}
|
|
||||||
if ( buffer_len < 0 ) {
|
if ( buffer_len < 0 ) {
|
||||||
Error( "Unable to read content" );
|
Error( "Unable to read content" );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -616,9 +622,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
}
|
}
|
||||||
case HEADERCONT :
|
case HEADERCONT :
|
||||||
{
|
{
|
||||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
buffer_len = GetData();
|
||||||
Debug(1, "Timeout waiting for HEADERCONT");
|
|
||||||
}
|
|
||||||
if ( buffer_len < 0 ) {
|
if ( buffer_len < 0 ) {
|
||||||
Error("Unable to read header");
|
Error("Unable to read header");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -903,9 +907,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
} else {
|
} else {
|
||||||
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
||||||
while ( !( buffer_len = ReadData(buffer) ) &&!zm_terminate ) {
|
buffer_len = GetData();
|
||||||
Debug(1, "Timeout waiting to extra subheader non regexp");
|
|
||||||
}
|
|
||||||
if ( buffer_len < 0 ) {
|
if ( buffer_len < 0 ) {
|
||||||
Error( "Unable to read subheader" );
|
Error( "Unable to read subheader" );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -945,7 +947,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
if ( content_length ) {
|
if ( content_length ) {
|
||||||
while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) {
|
while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) {
|
||||||
Debug(4, "getting more data");
|
Debug(4, "getting more data");
|
||||||
int bytes_read = ReadData(buffer);
|
int bytes_read = GetData();
|
||||||
if ( bytes_read < 0 ) {
|
if ( bytes_read < 0 ) {
|
||||||
Error("Unable to read content");
|
Error("Unable to read content");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -958,8 +960,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
while ( !content_length && !zm_terminate ) {
|
while ( !content_length && !zm_terminate ) {
|
||||||
Debug(4, "!content_length, ReadData");
|
Debug(4, "!content_length, ReadData");
|
||||||
buffer_len = ReadData( buffer );
|
buffer_len = ReadData( buffer );
|
||||||
if ( buffer_len < 0 )
|
if ( buffer_len < 0 ) {
|
||||||
{
|
|
||||||
Error( "Unable to read content" );
|
Error( "Unable to read content" );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
@ -1024,7 +1025,6 @@ int RemoteCameraHttp::PreCapture() {
|
||||||
if ( sd < 0 ) {
|
if ( sd < 0 ) {
|
||||||
Connect();
|
Connect();
|
||||||
if ( sd < 0 ) {
|
if ( sd < 0 ) {
|
||||||
Error("Unable to connect to camera");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
mode = SINGLE_IMAGE;
|
mode = SINGLE_IMAGE;
|
||||||
|
|
|
@ -68,6 +68,7 @@ public:
|
||||||
int Disconnect();
|
int Disconnect();
|
||||||
int SendRequest();
|
int SendRequest();
|
||||||
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
|
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
|
||||||
|
int GetData();
|
||||||
int GetResponse();
|
int GetResponse();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( ZMPacket &p );
|
int Capture( ZMPacket &p );
|
||||||
|
|
|
@ -342,7 +342,7 @@ int RemoteCameraRtsp::Capture( ZMPacket &zm_packet ) {
|
||||||
while ( !frameComplete && (buffer.size() > 0) ) {
|
while ( !frameComplete && (buffer.size() > 0) ) {
|
||||||
packet->data = buffer.head();
|
packet->data = buffer.head();
|
||||||
packet->size = buffer.size();
|
packet->size = buffer.size();
|
||||||
bytes += packet.size;
|
bytes += packet->size;
|
||||||
|
|
||||||
// So I think this is the magic decode step. Result is a raw image?
|
// So I think this is the magic decode step. Result is a raw image?
|
||||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
#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;
|
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());
|
Debug (1,"Authenticated user '%s' via token", username.c_str());
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
return user;
|
return user;
|
||||||
|
|
|
@ -58,6 +58,7 @@ VideoStore::VideoStore(
|
||||||
packets_written = 0;
|
packets_written = 0;
|
||||||
frame_count = 0;
|
frame_count = 0;
|
||||||
in_frame = NULL;
|
in_frame = NULL;
|
||||||
|
|
||||||
video_in_frame = NULL;
|
video_in_frame = NULL;
|
||||||
video_in_ctx = 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.
|
// 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;
|
video_start_pts = 0;
|
||||||
audio_next_pts = 0;
|
audio_next_pts = 0;
|
||||||
audio_next_dts = 0;
|
|
||||||
out_format = NULL;
|
out_format = NULL;
|
||||||
oc = NULL;
|
oc = NULL;
|
||||||
ret = 0;
|
|
||||||
} // VideoStore::VideoStore
|
} // VideoStore::VideoStore
|
||||||
|
|
||||||
bool VideoStore::open() {
|
bool VideoStore::open() {
|
||||||
|
@ -100,8 +99,6 @@ bool VideoStore::open() {
|
||||||
"Could not create video storage stream %s as no out ctx"
|
"Could not create video storage stream %s as no out ctx"
|
||||||
" could be assigned based on filename: %s",
|
" could be assigned based on filename: %s",
|
||||||
filename, av_make_error_string(ret).c_str());
|
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
|
// 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",
|
" could not be assigned based on filename or format %s",
|
||||||
filename, format);
|
filename, format);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
Debug(4, "Success allocating out ctx");
|
|
||||||
}
|
}
|
||||||
} // end if ! oc
|
} // end if ! oc
|
||||||
Debug(2, "Success opening output contect");
|
|
||||||
|
|
||||||
AVDictionary *pmetadata = NULL;
|
AVDictionary *pmetadata = NULL;
|
||||||
ret = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0);
|
ret = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0);
|
||||||
|
@ -145,6 +139,7 @@ bool VideoStore::open() {
|
||||||
zm_dump_codec(video_in_ctx);
|
zm_dump_codec(video_in_ctx);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
// FIXME delete?
|
||||||
Debug(2, "No input ctx");
|
Debug(2, "No input ctx");
|
||||||
video_in_ctx = avcodec_alloc_context3(NULL);
|
video_in_ctx = avcodec_alloc_context3(NULL);
|
||||||
video_in_stream_index = 0;
|
video_in_stream_index = 0;
|
||||||
|
@ -170,14 +165,14 @@ bool VideoStore::open() {
|
||||||
}
|
}
|
||||||
max_stream_index = video_out_stream->index;
|
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 ) ) {
|
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)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar);
|
ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar);
|
||||||
#else
|
#else
|
||||||
ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
||||||
#endif
|
#endif
|
||||||
// Copy params from instream to ctx
|
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
Error("Could not initialize ctx parameteres");
|
Error("Could not initialize ctx parameteres");
|
||||||
return false;
|
return false;
|
||||||
|
@ -219,7 +214,7 @@ bool VideoStore::open() {
|
||||||
Warning("Unsupported Orientation(%d)", orientation);
|
Warning("Unsupported Orientation(%d)", orientation);
|
||||||
}
|
}
|
||||||
} // end if 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++ ) {
|
for ( unsigned int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) {
|
||||||
if ( codec_data[i].codec_id != monitor->OutputCodec() )
|
if ( codec_data[i].codec_id != monitor->OutputCodec() )
|
||||||
continue;
|
continue;
|
||||||
|
@ -244,7 +239,7 @@ bool VideoStore::open() {
|
||||||
Debug(2,"No timebase found in video in context, defaulting to Q");
|
Debug(2,"No timebase found in video in context, defaulting to Q");
|
||||||
video_out_ctx->time_base = AV_TIME_BASE_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 ) {
|
if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) {
|
||||||
video_out_ctx->max_b_frames = 1;
|
video_out_ctx->max_b_frames = 1;
|
||||||
|
@ -327,6 +322,7 @@ bool VideoStore::open() {
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
avcodec_copy_context(video_out_stream->codec, video_out_ctx);
|
avcodec_copy_context(video_out_stream->codec, video_out_ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
converted_in_samples = NULL;
|
converted_in_samples = NULL;
|
||||||
audio_out_codec = NULL;
|
audio_out_codec = NULL;
|
||||||
|
@ -458,10 +454,7 @@ bool VideoStore::open() {
|
||||||
for ( int i = 0; i <= max_stream_index; i++ ) {
|
for ( int i = 0; i <= max_stream_index; i++ ) {
|
||||||
next_dts[i] = 0;
|
next_dts[i] = 0;
|
||||||
}
|
}
|
||||||
} // VideoStore::VideoStore
|
|
||||||
|
|
||||||
bool VideoStore::open() {
|
|
||||||
int ret;
|
|
||||||
/* open the out file, if needed */
|
/* open the out file, if needed */
|
||||||
if ( !(out_format->flags & AVFMT_NOFILE) ) {
|
if ( !(out_format->flags & AVFMT_NOFILE) ) {
|
||||||
if ( (ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL) ) < 0 ) {
|
if ( (ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL) ) < 0 ) {
|
||||||
|
@ -501,27 +494,8 @@ bool VideoStore::open() {
|
||||||
return true;
|
return true;
|
||||||
} // end bool VideoStore::open()
|
} // 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() {
|
void VideoStore::flush_codecs() {
|
||||||
|
int ret;
|
||||||
// The codec queues data. We need to send a flush command and out
|
// The codec queues data. We need to send a flush command and out
|
||||||
// whatever we get. Failures are not fatal.
|
// whatever we get. Failures are not fatal.
|
||||||
|
|
||||||
|
@ -560,7 +534,7 @@ void VideoStore::flush_codecs() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
write_video_packet(pkt);
|
write_packet(&pkt, video_out_stream);
|
||||||
zm_av_packet_unref(&pkt);
|
zm_av_packet_unref(&pkt);
|
||||||
} // while have buffered frames
|
} // while have buffered frames
|
||||||
} // end if have delay capability
|
} // end if have delay capability
|
||||||
|
@ -635,8 +609,6 @@ void VideoStore::flush_codecs() {
|
||||||
zm_av_packet_unref(&pkt);
|
zm_av_packet_unref(&pkt);
|
||||||
} // while have buffered frames
|
} // while have buffered frames
|
||||||
} // end if audio_out_codec
|
} // end if audio_out_codec
|
||||||
|
|
||||||
} // end if audio_out_codec
|
|
||||||
} // end flush_codecs
|
} // end flush_codecs
|
||||||
|
|
||||||
VideoStore::~VideoStore() {
|
VideoStore::~VideoStore() {
|
||||||
|
@ -1057,6 +1029,7 @@ int VideoStore::writePacket( ZMPacket *ipkt ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
||||||
|
int ret;
|
||||||
frame_count += 1;
|
frame_count += 1;
|
||||||
|
|
||||||
// if we have to transcode
|
// if we have to transcode
|
||||||
|
@ -1191,7 +1164,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
||||||
video_in_stream->time_base,
|
video_in_stream->time_base,
|
||||||
video_out_stream->time_base);
|
video_out_stream->time_base);
|
||||||
Debug(1, "duration from ipkt: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ") (%d/%d) (%d/%d)",
|
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,
|
video_last_pts,
|
||||||
zm_packet->in_frame->pkt_duration,
|
zm_packet->in_frame->pkt_duration,
|
||||||
duration,
|
duration,
|
||||||
|
@ -1203,13 +1176,13 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
||||||
} else {
|
} else {
|
||||||
duration =
|
duration =
|
||||||
av_rescale_q(
|
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_in_stream->time_base,
|
||||||
video_out_stream->time_base);
|
video_out_stream->time_base);
|
||||||
Debug(1, "duration calc: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ")",
|
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,
|
video_last_pts,
|
||||||
zm_packet->in_frame->pkt_pts - video_last_pts,
|
zm_packet->in_frame->pts - video_last_pts,
|
||||||
duration
|
duration
|
||||||
);
|
);
|
||||||
if ( duration <= 0 ) {
|
if ( duration <= 0 ) {
|
||||||
|
@ -1226,7 +1199,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
||||||
opkt.data = ipkt->data;
|
opkt.data = ipkt->data;
|
||||||
opkt.size = ipkt->size;
|
opkt.size = ipkt->size;
|
||||||
opkt.flags = ipkt->flags;
|
opkt.flags = ipkt->flags;
|
||||||
opkt.duration = pkt->duration;
|
opkt.duration = ipkt->duration;
|
||||||
|
|
||||||
if ( ipkt->dts != AV_NOPTS_VALUE ) {
|
if ( ipkt->dts != AV_NOPTS_VALUE ) {
|
||||||
if ( !video_first_dts ) {
|
if ( !video_first_dts ) {
|
||||||
|
@ -1394,7 +1367,6 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) {
|
||||||
av_make_error_string(ret).c_str());
|
av_make_error_string(ret).c_str());
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Success writing packet");
|
Debug(2, "Success writing packet");
|
||||||
>>>>>>> master
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
} // end int VideoStore::write_packet(AVPacket *pkt, AVStream *stream)
|
} // end int VideoStore::write_packet(AVPacket *pkt, AVStream *stream)
|
||||||
|
|
|
@ -41,11 +41,17 @@ static struct CodecData codec_data[];
|
||||||
int audio_in_stream_index;
|
int audio_in_stream_index;
|
||||||
|
|
||||||
AVCodec *video_out_codec;
|
AVCodec *video_out_codec;
|
||||||
|
AVCodecContext *video_in_ctx;
|
||||||
AVCodecContext *video_out_ctx;
|
AVCodecContext *video_out_ctx;
|
||||||
|
|
||||||
AVStream *video_in_stream;
|
AVStream *video_in_stream;
|
||||||
AVStream *audio_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
|
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||||
AVPacket opkt;
|
AVPacket opkt;
|
||||||
// we are transcoding
|
// we are transcoding
|
||||||
|
@ -53,18 +59,10 @@ static struct CodecData codec_data[];
|
||||||
AVFrame *in_frame;
|
AVFrame *in_frame;
|
||||||
AVFrame *out_frame;
|
AVFrame *out_frame;
|
||||||
|
|
||||||
AVCodecContext *video_in_ctx;
|
|
||||||
const AVCodec *audio_in_codec;
|
|
||||||
AVCodecContext *audio_in_ctx;
|
|
||||||
|
|
||||||
SWScale swscale;
|
SWScale swscale;
|
||||||
unsigned int packets_written;
|
unsigned int packets_written;
|
||||||
unsigned int frame_count;
|
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
|
#ifdef HAVE_LIBSWRESAMPLE
|
||||||
SwrContext *resample_ctx;
|
SwrContext *resample_ctx;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -349,7 +349,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if ( result < 0 ) {
|
if ( result < 0 ) {
|
||||||
// Failure, try reconnecting
|
// Failure, try reconnecting
|
||||||
sleep(1);
|
sleep(5);
|
||||||
break;
|
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++ ) {
|
for ( int p = 0; p < parm_no; p++ ) {
|
||||||
char *name = strtok(parms[p], "=");
|
char *name = strtok(parms[p], "=");
|
||||||
char *value = strtok(NULL, "=");
|
char const *value = strtok(NULL, "=");
|
||||||
if ( !value )
|
if ( !value )
|
||||||
value = (char *)"";
|
value = "";
|
||||||
if ( !strcmp(name, "source") ) {
|
if ( !strcmp(name, "source") ) {
|
||||||
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
|
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
|
||||||
if ( !strcmp(value, "fifo") )
|
if ( !strcmp(value, "fifo") )
|
||||||
|
@ -127,10 +127,10 @@ int main(int argc, const char *argv[]) {
|
||||||
} else if ( !strcmp(name, "time") ) {
|
} else if ( !strcmp(name, "time") ) {
|
||||||
event_time = atoi(value);
|
event_time = atoi(value);
|
||||||
} else if ( !strcmp(name, "event") ) {
|
} else if ( !strcmp(name, "event") ) {
|
||||||
event_id = strtoull(value, (char **)NULL, 10);
|
event_id = strtoull(value, NULL, 10);
|
||||||
source = ZMS_EVENT;
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp(name, "frame") ) {
|
} else if ( !strcmp(name, "frame") ) {
|
||||||
frame_id = strtoull(value, (char **)NULL, 10);
|
frame_id = strtoull(value, NULL, 10);
|
||||||
source = ZMS_EVENT;
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp(name, "scale") ) {
|
} else if ( !strcmp(name, "scale") ) {
|
||||||
scale = atoi(value);
|
scale = atoi(value);
|
||||||
|
@ -237,11 +237,13 @@ int main(int argc, const char *argv[]) {
|
||||||
|
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
char date_string[64];
|
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(
|
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: no-store, no-cache, must-revalidate\r\n"
|
||||||
"Cache-Control: post-check=0, pre-check=0\r\n"
|
"Cache-Control: post-check=0, pre-check=0\r\n"
|
||||||
"Pragma: no-cache\r\n",
|
"Pragma: no-cache\r\n",
|
||||||
|
@ -279,7 +281,9 @@ int main(int argc, const char *argv[]) {
|
||||||
stream.setStreamType(MonitorStream::STREAM_MPEG);
|
stream.setStreamType(MonitorStream::STREAM_MPEG);
|
||||||
#else // HAVE_LIBAVCODEC
|
#else // HAVE_LIBAVCODEC
|
||||||
Error("MPEG streaming of '%s' attempted while disabled", query);
|
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();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -430,7 +430,9 @@ int main(int argc, char *argv[]) {
|
||||||
User *user = 0;
|
User *user = 0;
|
||||||
|
|
||||||
if ( config.opt_use_auth ) {
|
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 ) {
|
if ( !username ) {
|
||||||
Error("Username must be supplied");
|
Error("Username must be supplied");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
|
@ -444,13 +446,10 @@ int main(int argc, char *argv[]) {
|
||||||
user = zmLoadUser(username);
|
user = zmLoadUser(username);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if ( !(username && password) && !auth && (jwt_token_str=="")) {
|
if ( !(username && password) && !auth ) {
|
||||||
Error("Username and password or auth/token string must be supplied");
|
Error("Username and password or auth/token string must be supplied");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
if (jwt_token_str != "") {
|
|
||||||
user = zmLoadTokenUser(jwt_token_str, false);
|
|
||||||
}
|
|
||||||
if ( auth ) {
|
if ( auth ) {
|
||||||
user = zmLoadAuthUser(auth, false);
|
user = zmLoadAuthUser(auth, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,14 +128,14 @@ else
|
||||||
fi;
|
fi;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||||
if [ "$PPA" == "" ]; then
|
if [ "$PPA" == "" ]; then
|
||||||
if [ "$RELEASE" != "" ]; then
|
if [ "$RELEASE" != "" ]; then
|
||||||
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
# 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_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||||
if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then
|
|
||||||
PPA="ppa:iconnor/zoneminder-stable"
|
PPA="ppa:iconnor/zoneminder-stable"
|
||||||
else
|
else
|
||||||
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
|
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
|
||||||
fi;
|
fi;
|
||||||
else
|
else
|
||||||
if [ "$BRANCH" == "" ]; then
|
if [ "$BRANCH" == "" ]; then
|
||||||
|
@ -175,7 +175,7 @@ cd ../
|
||||||
|
|
||||||
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
|
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
|
||||||
|
|
||||||
if [ $VERSION == "" ]; then
|
if [ -z "$VERSION" ]; then
|
||||||
exit 1;
|
exit 1;
|
||||||
fi;
|
fi;
|
||||||
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
|
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
|
||||||
|
@ -316,7 +316,7 @@ EOF
|
||||||
read -p "Do you want to upload this binary to zmrepo? (y/N)"
|
read -p "Do you want to upload this binary to zmrepo? (y/N)"
|
||||||
if [[ $REPLY == [yY] ]]; then
|
if [[ $REPLY == [yY] ]]; then
|
||||||
if [ "$RELEASE" != "" ]; 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
|
else
|
||||||
if [ "$BRANCH" == "" ]; then
|
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/"
|
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
|
#!/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
|
# Check to see if this script has access to all the commands it needs
|
||||||
for CMD in sshfs rsync find fusermount mkdir; do
|
for CMD in sshfs rsync find fusermount mkdir; do
|
||||||
type $CMD 2>&1 > /dev/null
|
type $CMD 2>&1 > /dev/null
|
||||||
|
@ -12,12 +17,17 @@ for CMD in sshfs rsync find fusermount mkdir; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# We only want to deploy packages during cron events
|
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||||
# See https://docs.travis-ci.com/user/cron-jobs/
|
if [ "${RELEASE}" != "" ]; then
|
||||||
if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||||
|
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||||
if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; 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"
|
targetfolder="debian/master/mini-dinstall/incoming"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
targetfolder="travis"
|
targetfolder="travis"
|
||||||
fi
|
fi
|
||||||
|
@ -25,40 +35,17 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${OS}" == "debian" ] || [ "${OS}"
|
||||||
echo
|
echo
|
||||||
echo "Target subfolder set to $targetfolder"
|
echo "Target subfolder set to $targetfolder"
|
||||||
echo
|
echo
|
||||||
if [ "${USE_SFTP}" == "yes" ]; then
|
|
||||||
echo "Running \$(rsync -v -e 'ssh -vvv' build/* zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)"
|
echo "Running \$(rsync -v -e 'ssh -vvv' build/*.{rpm,deb,dsc,tar.xz,buildinfo,changes} zmrepo@zmrepo.zoneminder.com:${targetfolder}/ 2>&1)"
|
||||||
rsync -v -e 'ssh -vvv' build/* 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
|
if [ "$?" -eq 0 ]; then
|
||||||
echo
|
echo
|
||||||
echo "Files copied successfully."
|
echo "Files copied successfully."
|
||||||
echo
|
echo
|
||||||
else
|
else
|
||||||
echo
|
echo
|
||||||
echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!"
|
echo "ERROR: Attempt to rsync to zmrepo.zoneminder.com failed!"
|
||||||
|
echo "See log output for details."
|
||||||
echo
|
echo
|
||||||
exit 99
|
exit 99
|
||||||
fi
|
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
|
# General sanity checks
|
||||||
checksanity () {
|
checksanity () {
|
||||||
# Check to see if this script has access to all the commands it needs
|
# 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
|
type $CMD 2>&1 > /dev/null
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
@ -30,7 +30,7 @@ checksanity () {
|
||||||
ARCH="x86_64"
|
ARCH="x86_64"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" ]]; then
|
if [[ "${ARCH}" != "x86_64" && "${ARCH}" != "i386" && "${ARCH}" != "armhf" && "${ARCH}" != "aarch64" ]]; then
|
||||||
echo
|
echo
|
||||||
echo "ERROR: Unsupported architecture specified \"${ARCH}\"."
|
echo "ERROR: Unsupported architecture specified \"${ARCH}\"."
|
||||||
echo
|
echo
|
||||||
|
@ -150,7 +150,7 @@ install_deb () {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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"
|
pkgname="build/zoneminder_${VERSION}-${RELEASE}_amd64.deb"
|
||||||
|
|
||||||
if [ -e $pkgname ]; then
|
if [ -e $pkgname ]; then
|
||||||
|
@ -275,6 +275,8 @@ checkdeploytarget () {
|
||||||
echo "*** TRACEROUTE ***"
|
echo "*** TRACEROUTE ***"
|
||||||
echo
|
echo
|
||||||
traceroute -w 2 -m 15 ${DEPLOYTARGET}
|
traceroute -w 2 -m 15 ${DEPLOYTARGET}
|
||||||
|
|
||||||
|
exit 97
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,16 +293,16 @@ if [ "${TRAVIS}" == "true" ]; then
|
||||||
fi
|
fi
|
||||||
checksanity
|
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
|
# Steps common to Redhat distros
|
||||||
if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
||||||
if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
|
|
||||||
commonprep
|
commonprep
|
||||||
echo "Begin Redhat build..."
|
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
|
setrpmpkgname
|
||||||
|
|
||||||
ln -sfT distros/redhat rpm
|
ln -sfT distros/redhat rpm
|
||||||
|
@ -326,7 +328,7 @@ if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
||||||
|
|
||||||
echo "Starting packpack..."
|
echo "Starting packpack..."
|
||||||
execpackpack
|
execpackpack
|
||||||
fi;
|
|
||||||
# Steps common to Debian based distros
|
# Steps common to Debian based distros
|
||||||
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||||
commonprep
|
commonprep
|
||||||
|
@ -348,14 +350,27 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
|
||||||
echo "Starting packpack..."
|
echo "Starting packpack..."
|
||||||
execpackpack
|
execpackpack
|
||||||
|
|
||||||
# We were not triggered via cron so just build and test trusty
|
# Try to install and run the newly built zoneminder package
|
||||||
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ]; then
|
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "xenial" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then
|
||||||
# If we are running inside Travis then attempt to install the deb we just built
|
echo "Begin Deb package installation..."
|
||||||
if [ "${TRAVIS}" == "true" ]; then
|
|
||||||
install_deb
|
install_deb
|
||||||
fi
|
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
|
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
|
SOL_SOCKET, // socket level
|
||||||
SO_SNDTIMEO, // timeout option
|
SO_SNDTIMEO, // timeout option
|
||||||
array(
|
array(
|
||||||
"sec"=>0, // Timeout in seconds
|
'sec'=>0, // Timeout in seconds
|
||||||
"usec"=>500 // I assume timeout in microseconds
|
'usec'=>500 // I assume timeout in microseconds
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$new_stream = null;
|
$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 ) ) {
|
if ( socket_connect( $socket, $url_bits['host'], $port ) ) {
|
||||||
$new_stream = $url_bits; // make a copy
|
$new_stream = $url_bits; // make a copy
|
||||||
$new_stream['port'] = $port;
|
$new_stream['port'] = $port;
|
||||||
} else {
|
} else {
|
||||||
socket_close($socket);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if ( $new_stream ) {
|
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'));
|
$stream['url'] = unparse_url($stream, array('path'=>'/','query'=>'action=stream'));
|
||||||
$monitors = ZM\Monitor::find(array('Path'=>$stream['url']));
|
$monitors = ZM\Monitor::find(array('Path'=>$stream['url']));
|
||||||
if ( count($monitors) ) {
|
if ( count($monitors) ) {
|
||||||
ZM\Info("Found monitors matching " . $stream['url'] );
|
ZM\Info('Found monitors matching ' . $stream['url'] );
|
||||||
$stream['Monitor'] = $monitors[0];
|
$stream['Monitor'] = $monitors[0];
|
||||||
if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) {
|
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";
|
$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 ) {
|
if ( ! $url_bits ) {
|
||||||
ajaxError("The given URL was too malformed to parse.");
|
ajaxError('The given URL was too malformed to parse.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,16 +169,16 @@ if ( 0 ) {
|
||||||
|
|
||||||
$file = $_FILES['import_file'];
|
$file = $_FILES['import_file'];
|
||||||
|
|
||||||
if ($file["error"] > 0) {
|
if ( $file['error'] > 0 ) {
|
||||||
ajaxError($file["error"]);
|
ajaxError($file['error']);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
$filename = $file["name"];
|
$filename = $file['name'];
|
||||||
|
|
||||||
$available_streams = array();
|
$available_streams = array();
|
||||||
$row = 1;
|
$row = 1;
|
||||||
if ( ($handle = fopen($file['tmp_name'], 'r')) !== FALSE ) {
|
if ( ($handle = fopen($file['tmp_name'], 'r')) !== FALSE ) {
|
||||||
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
|
while ( ($data = fgetcsv($handle, 1000, ',')) !== FALSE ) {
|
||||||
$name = $data[0];
|
$name = $data[0];
|
||||||
$url = $data[1];
|
$url = $data[1];
|
||||||
$group = $data[2];
|
$group = $data[2];
|
||||||
|
@ -207,21 +207,17 @@ if ( 0 ) {
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
ajaxResponse(array('Streams'=>$available_streams));
|
ajaxResponse(array('Streams'=>$available_streams));
|
||||||
} else {
|
} else {
|
||||||
ajaxError("Uploaded file does not exist");
|
ajaxError('Uploaded file does not exist');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} // end case import
|
} // end case import
|
||||||
default:
|
default:
|
||||||
{
|
ZM\Warning('unknown action '.$_REQUEST['action']);
|
||||||
ZM\Warning("unknown action " . $_REQUEST['action'] );
|
} // end switch action
|
||||||
} // end ddcase default
|
|
||||||
}
|
|
||||||
} else {
|
} 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 = $monitor_ids[$i];
|
||||||
$monitor_id = preg_replace('/^monitor_id-/', '', $monitor_id);
|
$monitor_id = preg_replace('/^monitor_id-/', '', $monitor_id);
|
||||||
if ( ( !$monitor_id ) or ! ( is_integer($monitor_id) or ctype_digit($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;
|
continue;
|
||||||
}
|
}
|
||||||
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id));
|
dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id));
|
||||||
|
@ -23,13 +23,11 @@ if ( canEdit('Monitors') ) {
|
||||||
return;
|
return;
|
||||||
} // end case sort
|
} // end case sort
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
ZM\Warning('unknown action '.$_REQUEST['action']);
|
ZM\Warning('unknown action '.$_REQUEST['action']);
|
||||||
} // end ddcase default
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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']);
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -155,5 +155,5 @@ if ( canEdit('Events') ) {
|
||||||
} // end switch action
|
} // end switch action
|
||||||
} // end if canEdit('Events')
|
} // 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;
|
$sql .= ' ORDER BY '.$sortField.' '.$sortOrder.' LIMIT '.$limit;
|
||||||
|
|
||||||
return array('sql'=>$sql, 'values'=>$values);
|
return array('sql'=>$sql, 'values'=>$values);
|
||||||
}
|
} # function buildLogQuery($action)
|
||||||
|
|
||||||
switch ( $_REQUEST['task'] ) {
|
switch ( $_REQUEST['task'] ) {
|
||||||
case 'create' :
|
case 'create' :
|
||||||
|
@ -70,14 +70,16 @@ switch ( $_REQUEST['task'] ) {
|
||||||
$string = $_POST['message'];
|
$string = $_POST['message'];
|
||||||
|
|
||||||
$file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : '';
|
$file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : '';
|
||||||
if ( !empty( $_POST['line'] ) )
|
if ( !empty( $_POST['line'] ) ) {
|
||||||
$line = validInt($_POST['line']);
|
$line = validInt($_POST['line']);
|
||||||
else
|
} else {
|
||||||
$line = NULL;
|
$line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
$levels = array_flip(ZM\Logger::$codes);
|
$levels = array_flip(ZM\Logger::$codes);
|
||||||
if ( !isset($levels[$_POST['level']]) )
|
if ( !isset($levels[$_POST['level']]) ) {
|
||||||
ZM\Panic('Unexpected logger level '.$_POST['level']);
|
ZM\Panic('Unexpected logger level '.$_POST['level']);
|
||||||
|
}
|
||||||
$level = $levels[$_POST['level']];
|
$level = $levels[$_POST['level']];
|
||||||
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
|
ZM\Logger::fetch()->logPrint($level, $string, $file, $line);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +143,10 @@ switch ( $_REQUEST['task'] ) {
|
||||||
$logs[] = $log;
|
$logs[] = $log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ( $options as $field => $values ) {
|
||||||
|
asort($options[$field]);
|
||||||
|
}
|
||||||
|
|
||||||
$available = count($logs);
|
$available = count($logs);
|
||||||
ajaxResponse( array(
|
ajaxResponse( array(
|
||||||
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG),
|
'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG),
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
if ( $_REQUEST['entity'] == 'navBar' ) {
|
if ( $_REQUEST['entity'] == 'navBar' ) {
|
||||||
$data = array();
|
$data = array();
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||||
$data['auth'] = generateAuthHash( ZM_AUTH_HASH_IPS );
|
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
|
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||||
|
$data['auth'] = $auth_hash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$data['message'] = getNavBarHtml('reload');
|
$data['message'] = getNavBarHtml('reload');
|
||||||
ajaxResponse($data);
|
ajaxResponse($data);
|
||||||
|
@ -83,8 +86,8 @@ $statusData = array(
|
||||||
'MinEventId' => array( 'sql' => '(SELECT min(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
'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' ),
|
'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' ),
|
'TotalEvents' => array( 'sql' => '(SELECT count(Events.Id) FROM Events WHERE Events.MonitorId = Monitors.Id' ),
|
||||||
'Status' => array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -s' ),
|
'Status' => (isset($_REQUEST['id'])?array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -s' ):null),
|
||||||
'FrameRate' => array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -f' ),
|
'FrameRate' => (isset($_REQUEST['id'])?array( 'zmu' => '-m '.escapeshellarg($_REQUEST['id'][0]).' -f' ):null),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'events' => array(
|
'events' => array(
|
||||||
|
@ -204,6 +207,7 @@ function collectData() {
|
||||||
$fieldSql = array();
|
$fieldSql = array();
|
||||||
$joinSql = array();
|
$joinSql = array();
|
||||||
$groupSql = array();
|
$groupSql = array();
|
||||||
|
$values = array();
|
||||||
|
|
||||||
$elements = &$entitySpec['elements'];
|
$elements = &$entitySpec['elements'];
|
||||||
$lc_elements = array_change_key_case( $elements );
|
$lc_elements = array_change_key_case( $elements );
|
||||||
|
@ -258,7 +262,6 @@ function collectData() {
|
||||||
if ( $id && !empty($entitySpec['selector']) ) {
|
if ( $id && !empty($entitySpec['selector']) ) {
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$where = array();
|
$where = array();
|
||||||
$values = array();
|
|
||||||
foreach( $entitySpec['selector'] as $selIndex => $selector ) {
|
foreach( $entitySpec['selector'] as $selIndex => $selector ) {
|
||||||
$selectorParamName = ':selector' . $selIndex;
|
$selectorParamName = ':selector' . $selIndex;
|
||||||
if ( is_array( $selector ) ) {
|
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);
|
$numSockets = socket_select($rSockets, $wSockets, $eSockets, intval($timeout/1000), ($timeout%1000)*1000);
|
||||||
|
|
||||||
if ( $numSockets === false ) {
|
if ( $numSockets === false ) {
|
||||||
ZM\Error('socket_select failed: ' . socket_strerror(socket_last_error()));
|
|
||||||
ajaxError('socket_select failed: '.socket_strerror(socket_last_error()));
|
ajaxError('socket_select failed: '.socket_strerror(socket_last_error()));
|
||||||
} else if ( $numSockets < 0 ) {
|
} else if ( $numSockets < 0 ) {
|
||||||
ZM\Error("Socket closed $remSockFile");
|
|
||||||
ajaxError("Socket closed $remSockFile");
|
ajaxError("Socket closed $remSockFile");
|
||||||
} else if ( $numSockets == 0 ) {
|
} else if ( $numSockets == 0 ) {
|
||||||
ZM\Error("Timed out waiting for msg $remSockFile");
|
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");
|
#ajaxError("Timed out waiting for msg $remSockFile");
|
||||||
} else if ( $numSockets > 0 ) {
|
} else if ( $numSockets > 0 ) {
|
||||||
if ( count($rSockets) != 1 ) {
|
if ( count($rSockets) != 1 ) {
|
||||||
ZM\Error('Bogus return from select, '.count($rSockets).' sockets available');
|
|
||||||
ajaxError('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'] ) {
|
switch ( $data['type'] ) {
|
||||||
case MSG_DATA_WATCH :
|
case MSG_DATA_WATCH :
|
||||||
$data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg);
|
$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 );
|
$data['fps'] = round( $data['fps'], 2 );
|
||||||
ZM\Logger::Debug('FPS: ' . $data['fps'] );
|
|
||||||
$data['rate'] /= RATE_BASE;
|
$data['rate'] /= RATE_BASE;
|
||||||
$data['delay'] = round( $data['delay'], 2 );
|
$data['delay'] = round( $data['delay'], 2 );
|
||||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||||
$time = time();
|
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
// Regenerate auth hash after half the lifetime of the hash
|
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||||
if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
|
$data['auth'] = $auth_hash;
|
||||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
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));
|
ajaxResponse(array('status'=>$data));
|
||||||
|
@ -144,11 +141,10 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
}
|
}
|
||||||
$data['rate'] /= RATE_BASE;
|
$data['rate'] /= RATE_BASE;
|
||||||
$data['zoom'] = round($data['zoom']/SCALE_BASE, 1);
|
$data['zoom'] = round($data['zoom']/SCALE_BASE, 1);
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||||
$time = time();
|
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
// Regenerate auth hash after half the lifetime of the hash
|
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||||
if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
|
$data['auth'] = $auth_hash;
|
||||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ajaxResponse(array('status'=>$data));
|
ajaxResponse(array('status'=>$data));
|
||||||
|
|
|
@ -15,6 +15,10 @@ class EventsController extends AppController {
|
||||||
*/
|
*/
|
||||||
public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
|
public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator');
|
||||||
|
|
||||||
|
public function beforeRender() {
|
||||||
|
$this->set($this->Event->enumValues());
|
||||||
|
}
|
||||||
|
|
||||||
public function beforeFilter() {
|
public function beforeFilter() {
|
||||||
parent::beforeFilter();
|
parent::beforeFilter();
|
||||||
global $user;
|
global $user;
|
||||||
|
|
|
@ -77,16 +77,24 @@ class GroupsController extends AppController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->Group->create();
|
$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(
|
return $this->flash(
|
||||||
__('The group has been saved.'),
|
__('The group has been saved.'),
|
||||||
array('action' => 'index')
|
array('action' => 'index')
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
ZM\Error("Failed to save Group");
|
||||||
|
debug($this->Group->invalidFields());
|
||||||
}
|
}
|
||||||
}
|
} # end if post
|
||||||
$monitors = $this->Group->Monitor->find('list');
|
$monitors = $this->Group->Monitor->find('list');
|
||||||
$this->set(compact('monitors'));
|
$this->set(compact('monitors'));
|
||||||
}
|
} # end add
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* edit method
|
* edit method
|
||||||
|
|
|
@ -50,8 +50,10 @@ class HostController extends AppController {
|
||||||
$cred_depr = [];
|
$cred_depr = [];
|
||||||
|
|
||||||
if ( $username && $password ) {
|
if ( $username && $password ) {
|
||||||
|
ZM\Logger::Debug('Username and password provided, generating access and refresh tokens');
|
||||||
$cred = $this->_getCredentials(true, '', $username); // generate refresh
|
$cred = $this->_getCredentials(true, '', $username); // generate refresh
|
||||||
} else {
|
} else {
|
||||||
|
ZM\Logger::Debug('Only generating access token');
|
||||||
$cred = $this->_getCredentials(false, $token); // don't generate refresh
|
$cred = $this->_getCredentials(false, $token); // don't generate refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +71,8 @@ class HostController extends AppController {
|
||||||
$cred_depr = $this->_getCredentialsDeprecated();
|
$cred_depr = $this->_getCredentialsDeprecated();
|
||||||
$login_array['credentials'] = $cred_depr[0];
|
$login_array['credentials'] = $cred_depr[0];
|
||||||
$login_array['append_password'] = $cred_depr[1];
|
$login_array['append_password'] = $cred_depr[1];
|
||||||
|
} else {
|
||||||
|
ZM\Logger::Debug('Legacy Auth is disabled, not generating auth= credentials');
|
||||||
}
|
}
|
||||||
|
|
||||||
$login_array['version'] = $ver[0];
|
$login_array['version'] = $ver[0];
|
||||||
|
@ -108,8 +112,11 @@ class HostController extends AppController {
|
||||||
|
|
||||||
private function _getCredentials($generate_refresh_token=false, $token='', $username='') {
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( !ZM_AUTH_HASH_SECRET )
|
if ( !ZM_AUTH_HASH_SECRET )
|
||||||
throw new ForbiddenException(__('Please create a valid AUTH_HASH_SECRET in ZoneMinder'));
|
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_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
|
// by default access token will expire in 2 hrs
|
||||||
// you can change it by changing the value of ZM_AUTH_HASH_TLL
|
// you can change it by changing the value of ZM_AUTH_HASH_TLL
|
||||||
|
|
|
@ -44,33 +44,26 @@ class MonitorsController extends AppController {
|
||||||
} else {
|
} else {
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
global $user;
|
global $user;
|
||||||
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
|
$allowedMonitors = $user ? preg_split('@,@', $user['MonitorIds'], NULL, PREG_SPLIT_NO_EMPTY) : null;
|
||||||
if ( $allowedMonitors ) {
|
if ( $allowedMonitors ) {
|
||||||
$conditions['Monitor.Id' ] = $allowedMonitors;
|
$conditions['Monitor.Id' ] = $allowedMonitors;
|
||||||
}
|
}
|
||||||
$find_array = array('conditions'=>$conditions,'contain'=>array('Group'));
|
|
||||||
|
|
||||||
if ( isset($conditions['GroupId']) ) {
|
$find_array = array(
|
||||||
$find_array['joins'] = array(
|
'conditions' => &$conditions,
|
||||||
|
'contain' => array('Group'),
|
||||||
|
'joins' => array(
|
||||||
array(
|
array(
|
||||||
'table' => 'Groups_Monitors',
|
'table' => 'Groups_Monitors',
|
||||||
'type' => 'inner',
|
'type' => 'left',
|
||||||
'conditions' => array(
|
'conditions' => array(
|
||||||
'Groups_Monitors.MonitorId = Monitor.Id'
|
'Groups_Monitors.MonitorId = Monitor.Id',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
//array(
|
),
|
||||||
//'table' => 'Groups',
|
'group' => '`Monitor`.`Id`',
|
||||||
//'type' => 'inner',
|
|
||||||
//'conditions' => array(
|
|
||||||
//'Groups.Id = Groups_Monitors.GroupId',
|
|
||||||
//'Groups.Id' => $this->request->params['GroupId'],
|
|
||||||
//),
|
|
||||||
//)
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
$monitors = $this->Monitor->find('all',$find_array);
|
$monitors = $this->Monitor->find('all',$find_array);
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
'monitors' => $monitors,
|
'monitors' => $monitors,
|
||||||
|
@ -249,16 +242,11 @@ class MonitorsController extends AppController {
|
||||||
// where C=on|off|status
|
// where C=on|off|status
|
||||||
public function alarm() {
|
public function alarm() {
|
||||||
$id = $this->request->params['named']['id'];
|
$id = $this->request->params['named']['id'];
|
||||||
$cmd = strtolower($this->request->params['named']['command']);
|
|
||||||
if ( !$this->Monitor->exists($id) ) {
|
if ( !$this->Monitor->exists($id) ) {
|
||||||
throw new NotFoundException(__('Invalid monitor'));
|
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) {
|
switch ($cmd) {
|
||||||
case 'on':
|
case 'on':
|
||||||
$q = '-a';
|
$q = '-a';
|
||||||
|
@ -272,42 +260,43 @@ class MonitorsController extends AppController {
|
||||||
$verbose = ''; // zmu has a bug - gives incorrect verbose output in this case
|
$verbose = ''; // zmu has a bug - gives incorrect verbose output in this case
|
||||||
$q = '-s';
|
$q = '-s';
|
||||||
break;
|
break;
|
||||||
|
default :
|
||||||
|
throw new BadRequestException(__('Invalid command'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// form auth key based on auth credentials
|
// 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 = '';
|
$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 ) {
|
if ( $mToken ) {
|
||||||
$auth = ' -T '.$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();
|
$auth = ' -U ' .$user['Username'].' -P '.$password;
|
||||||
$ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5];
|
} else if ( ZM_AUTH_RELAY == 'none' ) {
|
||||||
$ak = md5($ak);
|
$auth = ' -U ' .$user['Username'];
|
||||||
$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');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$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);
|
$status = exec ($shellcmd);
|
||||||
|
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
|
|
|
@ -17,12 +17,17 @@ class ServersController extends AppController {
|
||||||
|
|
||||||
public function beforeFilter() {
|
public function beforeFilter() {
|
||||||
parent::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;
|
global $user;
|
||||||
$canView = (!$user) || ($user['System'] != 'None');
|
$canView = (!$user) || ($user['System'] != 'None');
|
||||||
if ( !$canView ) {
|
if ( !$canView ) {
|
||||||
throw new UnauthorizedException(__('Insufficient Privileges'));
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Group extends AppModel {
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $hasMany = array(
|
public $hasAndBelongsToMany = array(
|
||||||
'Monitor' => array(
|
'Monitor' => array(
|
||||||
'className' => 'Monitor',
|
'className' => 'Monitor',
|
||||||
'joinTable' => 'Groups_Monitors',
|
'joinTable' => 'Groups_Monitors',
|
||||||
|
@ -77,4 +77,5 @@ class Group extends AppModel {
|
||||||
'counterQuery' => ''
|
'counterQuery' => ''
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
var $actsAs = array( 'Containable' );
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,12 +128,16 @@ class Control extends ZM_Object {
|
||||||
$cmds['PresetHome'] = 'presetHome';
|
$cmds['PresetHome'] = 'presetHome';
|
||||||
|
|
||||||
if ( $this->CanZoom() ) {
|
if ( $this->CanZoom() ) {
|
||||||
if ( $this->CanZoomCon() )
|
if ( $this->CanZoomCon() ) {
|
||||||
$cmds['ZoomRoot'] = 'zoomCon';
|
$cmds['ZoomRoot'] = 'zoomCon';
|
||||||
elseif ( $this->CanZoomRel() )
|
} else if ( $this->CanZoomRel() ) {
|
||||||
$cmds['ZoomRoot'] = 'zoomRel';
|
$cmds['ZoomRoot'] = 'zoomRel';
|
||||||
elseif ( $this->CanZoomAbs() )
|
} else if ( $this->CanZoomAbs() ) {
|
||||||
$cmds['ZoomRoot'] = 'zoomAbs';
|
$cmds['ZoomRoot'] = 'zoomAbs';
|
||||||
|
} else {
|
||||||
|
$cmds['ZoomRoot'] = '';
|
||||||
|
Error('No zoom type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
}
|
||||||
$cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele';
|
$cmds['ZoomTele'] = $cmds['ZoomRoot'].'Tele';
|
||||||
$cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide';
|
$cmds['ZoomWide'] = $cmds['ZoomRoot'].'Wide';
|
||||||
$cmds['ZoomStop'] = 'zoomStop';
|
$cmds['ZoomStop'] = 'zoomStop';
|
||||||
|
@ -142,12 +146,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanFocus() ) {
|
if ( $this->CanFocus() ) {
|
||||||
if ( $this->CanFocusCon() )
|
if ( $this->CanFocusCon() ) {
|
||||||
$cmds['FocusRoot'] = 'focusCon';
|
$cmds['FocusRoot'] = 'focusCon';
|
||||||
elseif ( $this->CanFocusRel() )
|
} else if ( $this->CanFocusRel() ) {
|
||||||
$cmds['FocusRoot'] = 'focusRel';
|
$cmds['FocusRoot'] = 'focusRel';
|
||||||
elseif ( $this->CanFocusAbs() )
|
} else if ( $this->CanFocusAbs() ) {
|
||||||
$cmds['FocusRoot'] = 'focusAbs';
|
$cmds['FocusRoot'] = 'focusAbs';
|
||||||
|
} else {
|
||||||
|
$cmds['FocusRoot'] = '';
|
||||||
|
Error('No focus type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
}
|
||||||
$cmds['FocusFar'] = $cmds['FocusRoot'].'Far';
|
$cmds['FocusFar'] = $cmds['FocusRoot'].'Far';
|
||||||
$cmds['FocusNear'] = $cmds['FocusRoot'].'Near';
|
$cmds['FocusNear'] = $cmds['FocusRoot'].'Near';
|
||||||
$cmds['FocusStop'] = 'focusStop';
|
$cmds['FocusStop'] = 'focusStop';
|
||||||
|
@ -156,12 +164,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanIris() ) {
|
if ( $this->CanIris() ) {
|
||||||
if ( $this->CanIrisCon() )
|
if ( $this->CanIrisCon() ) {
|
||||||
$cmds['IrisRoot'] = 'irisCon';
|
$cmds['IrisRoot'] = 'irisCon';
|
||||||
elseif ( $this->CanIrisRel() )
|
} else if ( $this->CanIrisRel() ) {
|
||||||
$cmds['IrisRoot'] = 'irisRel';
|
$cmds['IrisRoot'] = 'irisRel';
|
||||||
elseif ( $this->CanIrisAbs() )
|
} else if ( $this->CanIrisAbs() ) {
|
||||||
$cmds['IrisRoot'] = 'irisAbs';
|
$cmds['IrisRoot'] = 'irisAbs';
|
||||||
|
} else {
|
||||||
|
$cmds['IrisRoot'] = '';
|
||||||
|
Error('No iris type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
}
|
||||||
$cmds['IrisOpen'] = $cmds['IrisRoot'].'Open';
|
$cmds['IrisOpen'] = $cmds['IrisRoot'].'Open';
|
||||||
$cmds['IrisClose'] = $cmds['IrisRoot'].'Close';
|
$cmds['IrisClose'] = $cmds['IrisRoot'].'Close';
|
||||||
$cmds['IrisStop'] = 'irisStop';
|
$cmds['IrisStop'] = 'irisStop';
|
||||||
|
@ -170,12 +182,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanWhite() ) {
|
if ( $this->CanWhite() ) {
|
||||||
if ( $this->CanWhiteCon() )
|
if ( $this->CanWhiteCon() ) {
|
||||||
$cmds['WhiteRoot'] = 'whiteCon';
|
$cmds['WhiteRoot'] = 'whiteCon';
|
||||||
elseif ( $this->CanWhiteRel() )
|
} else if ( $this->CanWhiteRel() ) {
|
||||||
$cmds['WhiteRoot'] = 'whiteRel';
|
$cmds['WhiteRoot'] = 'whiteRel';
|
||||||
elseif ( $this->CanWhiteAbs() )
|
} else if ( $this->CanWhiteAbs() ) {
|
||||||
$cmds['WhiteRoot'] = 'whiteAbs';
|
$cmds['WhiteRoot'] = 'whiteAbs';
|
||||||
|
} else {
|
||||||
|
Error('No White type selected. Please select Continuous, Relative, Absolute');
|
||||||
|
$cmds['WhiteRoot'] = '';
|
||||||
|
}
|
||||||
$cmds['WhiteIn'] = $cmds['WhiteRoot'].'In';
|
$cmds['WhiteIn'] = $cmds['WhiteRoot'].'In';
|
||||||
$cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out';
|
$cmds['WhiteOut'] = $cmds['WhiteRoot'].'Out';
|
||||||
$cmds['WhiteAuto'] = 'whiteAuto';
|
$cmds['WhiteAuto'] = 'whiteAuto';
|
||||||
|
@ -183,12 +199,16 @@ class Control extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->CanGain() ) {
|
if ( $this->CanGain() ) {
|
||||||
if ( $this->CanGainCon() )
|
if ( $this->CanGainCon() ) {
|
||||||
$cmds['GainRoot'] = 'gainCon';
|
$cmds['GainRoot'] = 'gainCon';
|
||||||
elseif ( $this->CanGainRel() )
|
} else if ( $this->CanGainRel() ) {
|
||||||
$cmds['GainRoot'] = 'gainRel';
|
$cmds['GainRoot'] = 'gainRel';
|
||||||
elseif ( $this->CanGainAbs() )
|
} else if ( $this->CanGainAbs() ) {
|
||||||
$cmds['GainRoot'] = 'gainAbs';
|
$cmds['GainRoot'] = 'gainAbs';
|
||||||
|
} else {
|
||||||
|
Error('No Gain type selected');
|
||||||
|
$cmds['GainRoot'] = '';
|
||||||
|
}
|
||||||
$cmds['GainUp'] = $cmds['GainRoot'].'Up';
|
$cmds['GainUp'] = $cmds['GainRoot'].'Up';
|
||||||
$cmds['GainDown'] = $cmds['GainRoot'].'Down';
|
$cmds['GainDown'] = $cmds['GainRoot'].'Down';
|
||||||
$cmds['GainAuto'] = 'gainAuto';
|
$cmds['GainAuto'] = 'gainAuto';
|
||||||
|
@ -207,6 +227,7 @@ class Control extends ZM_Object {
|
||||||
$cmds['Center'] = $cmds['PresetHome'];
|
$cmds['Center'] = $cmds['PresetHome'];
|
||||||
} else {
|
} else {
|
||||||
$cmds['MoveRoot'] = '';
|
$cmds['MoveRoot'] = '';
|
||||||
|
Error('No move type selected. Please select Continuous, Relative, Absolute');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cmds['MoveUp'] = $cmds['MoveRoot'].'Up';
|
$cmds['MoveUp'] = $cmds['MoveRoot'].'Up';
|
||||||
|
|
|
@ -47,14 +47,18 @@ class Event extends ZM_Object {
|
||||||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
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 ) {
|
public function Storage( $new = null ) {
|
||||||
if ( $new ) {
|
if ( $new ) {
|
||||||
$this->{'Storage'} = $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'} )
|
if ( isset($this->{'StorageId'}) and $this->{'StorageId'} )
|
||||||
$this->{'Storage'} = Storage::find_one(array('Id'=>$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);
|
$this->{'Storage'} = new Storage(NULL);
|
||||||
}
|
}
|
||||||
return $this->{'Storage'};
|
return $this->{'Storage'};
|
||||||
|
@ -64,10 +68,10 @@ class Event extends ZM_Object {
|
||||||
if ( $new ) {
|
if ( $new ) {
|
||||||
$this->{'SecondaryStorage'} = $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'} )
|
if ( isset($this->{'SecondaryStorageId'}) and $this->{'SecondaryStorageId'} )
|
||||||
$this->{'SecondaryStorage'} = Storage::find_one(array('Id'=>$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);
|
$this->{'SecondaryStorage'} = new Storage(NULL);
|
||||||
}
|
}
|
||||||
return $this->{'SecondaryStorage'};
|
return $this->{'SecondaryStorage'};
|
||||||
|
@ -262,7 +266,7 @@ class Event extends ZM_Object {
|
||||||
if ( is_null($new) or ( $new != '' ) ) {
|
if ( is_null($new) or ( $new != '' ) ) {
|
||||||
$this->{'DiskSpace'} = $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());
|
$this->{'DiskSpace'} = folder_size($this->Path());
|
||||||
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
|
dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'}));
|
||||||
}
|
}
|
||||||
|
@ -298,7 +302,7 @@ class Event extends ZM_Object {
|
||||||
} // end function createListThumbnail
|
} // end function createListThumbnail
|
||||||
|
|
||||||
function ThumbnailWidth( ) {
|
function ThumbnailWidth( ) {
|
||||||
if ( ! ( array_key_exists('ThumbnailWidth', $this) ) ) {
|
if ( ! ( property_exists($this, 'ThumbnailWidth') ) ) {
|
||||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||||
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
||||||
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
|
||||||
|
@ -315,7 +319,7 @@ class Event extends ZM_Object {
|
||||||
} // end function ThumbnailWidth
|
} // end function ThumbnailWidth
|
||||||
|
|
||||||
function ThumbnailHeight( ) {
|
function ThumbnailHeight( ) {
|
||||||
if ( ! ( array_key_exists('ThumbnailHeight', $this) ) ) {
|
if ( ! ( property_exists($this, 'ThumbnailHeight') ) ) {
|
||||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||||
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
$this->{'ThumbnailWidth'} = ZM_WEB_LIST_THUMB_WIDTH;
|
||||||
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'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 capture file exists
|
||||||
} // end if analyze file exists
|
} // end if analyze file exists
|
||||||
}
|
} // end if frame or snapshot
|
||||||
|
|
||||||
$captPath = $eventPath.'/'.$captImage;
|
$captPath = $eventPath.'/'.$captImage;
|
||||||
if ( ! file_exists($captPath) ) {
|
if ( ! file_exists($captPath) ) {
|
||||||
Error("Capture file does not exist at $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']);
|
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']);
|
||||||
$analPath = $eventPath.'/'.$analImage;
|
$analPath = $eventPath.'/'.$analImage;
|
||||||
|
|
||||||
//echo "AI:$analImage, AP:$analPath, TAP:$analPath<br>";
|
|
||||||
|
|
||||||
$alarmFrame = $frame['Type']=='Alarm';
|
$alarmFrame = $frame['Type']=='Alarm';
|
||||||
|
|
||||||
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
||||||
$isAnalImage = $hasAnalImage && !$captureOnly;
|
$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;
|
$imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath;
|
||||||
$imageFile = $imagePath;
|
$imageFile = $imagePath;
|
||||||
$thumbFile = $thumbPath;
|
$thumbFile = $thumbPath;
|
||||||
|
@ -508,10 +508,11 @@ class Event extends ZM_Object {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
@ -555,10 +556,11 @@ class Event extends ZM_Object {
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
|
|
@ -11,6 +11,9 @@ class Filter extends ZM_Object {
|
||||||
'AutoExecute' => 0,
|
'AutoExecute' => 0,
|
||||||
'AutoExecuteCmd' => 0,
|
'AutoExecuteCmd' => 0,
|
||||||
'AutoEmail' => 0,
|
'AutoEmail' => 0,
|
||||||
|
'EmailTo' => '',
|
||||||
|
'EmailSubject' => '',
|
||||||
|
'EmailBody' => '',
|
||||||
'AutoDelete' => 0,
|
'AutoDelete' => 0,
|
||||||
'AutoArchive' => 0,
|
'AutoArchive' => 0,
|
||||||
'AutoVideo' => 0,
|
'AutoVideo' => 0,
|
||||||
|
@ -39,8 +42,8 @@ class Filter extends ZM_Object {
|
||||||
$this->{'Query'} = func_get_arg(0);;
|
$this->{'Query'} = func_get_arg(0);;
|
||||||
$this->{'Query_json'} = jsonEncode($this->{'Query'});
|
$this->{'Query_json'} = jsonEncode($this->{'Query'});
|
||||||
}
|
}
|
||||||
if ( !array_key_exists('Query', $this) ) {
|
if ( !property_exists($this, 'Query') ) {
|
||||||
if ( array_key_exists('Query_json', $this) and $this->{'Query_json'} ) {
|
if ( property_exists($this, 'Query_json') and $this->{'Query_json'} ) {
|
||||||
$this->{'Query'} = jsonDecode($this->{'Query_json'});
|
$this->{'Query'} = jsonDecode($this->{'Query_json'});
|
||||||
} else {
|
} else {
|
||||||
$this->{'Query'} = array();
|
$this->{'Query'} = array();
|
||||||
|
@ -115,13 +118,17 @@ class Filter extends ZM_Object {
|
||||||
|
|
||||||
public function control($command, $server_id=null) {
|
public function control($command, $server_id=null) {
|
||||||
$Servers = $server_id ? Server::find(array('Id'=>$server_id)) : Server::find(array('Status'=>'Running'));
|
$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
|
# This will be the non-multi-server case
|
||||||
$Servers = array(new Server());
|
$Servers = array(new Server());
|
||||||
|
} else {
|
||||||
|
Warning("Server not found for id $server_id");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach ( $Servers as $Server ) {
|
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
|
# Local
|
||||||
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
|
Logger::Debug("Controlling filter locally $command for server ".$Server->Id());
|
||||||
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
|
daemonControl($command, 'zmfilter.pl', '--filter_id='.$this->{'Id'}.' --daemon');
|
||||||
|
@ -139,7 +146,7 @@ class Filter extends ZM_Object {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$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");
|
Logger::Debug("sending command to $url");
|
||||||
$data = array();
|
$data = array();
|
||||||
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Group extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
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('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'}));
|
||||||
dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'}));
|
dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'}));
|
||||||
dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'}));
|
dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'}));
|
||||||
|
@ -35,7 +35,7 @@ class Group extends ZM_Object {
|
||||||
if ( isset($new) ) {
|
if ( isset($new) ) {
|
||||||
$this->{'depth'} = $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;
|
$this->{'depth'} = 0;
|
||||||
if ( $this->{'ParentId'} != null ) {
|
if ( $this->{'ParentId'} != null ) {
|
||||||
$Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
|
$Parent = Group::find_one(array('Id'=>$this->{'ParentId'}));
|
||||||
|
@ -46,7 +46,7 @@ class Group extends ZM_Object {
|
||||||
} // end public function depth
|
} // end public function depth
|
||||||
|
|
||||||
public function MonitorIds( ) {
|
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'}));
|
$this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'}));
|
||||||
}
|
}
|
||||||
return $this->{'MonitorIds'};
|
return $this->{'MonitorIds'};
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Monitor extends ZM_Object {
|
||||||
protected $defaults = array(
|
protected $defaults = array(
|
||||||
'Id' => null,
|
'Id' => null,
|
||||||
'Name' => '',
|
'Name' => '',
|
||||||
|
'Notes' => '',
|
||||||
'ServerId' => 0,
|
'ServerId' => 0,
|
||||||
'StorageId' => 0,
|
'StorageId' => 0,
|
||||||
'Type' => 'Ffmpeg',
|
'Type' => 'Ffmpeg',
|
||||||
|
@ -45,6 +46,7 @@ protected $defaults = array(
|
||||||
'SaveJPEGs' => 3,
|
'SaveJPEGs' => 3,
|
||||||
'VideoWriter' => '0',
|
'VideoWriter' => '0',
|
||||||
'OutputCodec' => null,
|
'OutputCodec' => null,
|
||||||
|
'Encoder' => 'auto',
|
||||||
'OutputContainer' => null,
|
'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",
|
'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),
|
'RecordAudio' => array('type'=>'boolean', 'default'=>0),
|
||||||
|
@ -58,10 +60,10 @@ protected $defaults = array(
|
||||||
'LabelX' => 0,
|
'LabelX' => 0,
|
||||||
'LabelY' => 0,
|
'LabelY' => 0,
|
||||||
'LabelSize' => 1,
|
'LabelSize' => 1,
|
||||||
'ImageBufferCount' => 100,
|
'ImageBufferCount' => 20,
|
||||||
'WarmupCount' => 0,
|
'WarmupCount' => 0,
|
||||||
'PreEventCount' => 0,
|
'PreEventCount' => 5,
|
||||||
'PostEventCount' => 0,
|
'PostEventCount' => 5,
|
||||||
'StreamReplayBuffer' => 0,
|
'StreamReplayBuffer' => 0,
|
||||||
'AlarmFrameCount' => 1,
|
'AlarmFrameCount' => 1,
|
||||||
'SectionLength' => 600,
|
'SectionLength' => 600,
|
||||||
|
@ -69,10 +71,6 @@ protected $defaults = array(
|
||||||
'FrameSkip' => 0,
|
'FrameSkip' => 0,
|
||||||
'MotionFrameSkip' => 0,
|
'MotionFrameSkip' => 0,
|
||||||
'AnalysisFPSLimit' => null,
|
'AnalysisFPSLimit' => null,
|
||||||
'OutputCodec' => '0',
|
|
||||||
'Encoder' => 'auto',
|
|
||||||
'OutputContainer' => 'auto',
|
|
||||||
'Triggers' => null,
|
|
||||||
'AnalysisUpdateDelay' => 0,
|
'AnalysisUpdateDelay' => 0,
|
||||||
'MaxFPS' => null,
|
'MaxFPS' => null,
|
||||||
'AlarmMaxFPS' => null,
|
'AlarmMaxFPS' => null,
|
||||||
|
@ -92,7 +90,7 @@ protected $defaults = array(
|
||||||
'DefaultScale' => 100,
|
'DefaultScale' => 100,
|
||||||
'SignalCheckPoints' => 0,
|
'SignalCheckPoints' => 0,
|
||||||
'SignalCheckColour' => '#0000BE',
|
'SignalCheckColour' => '#0000BE',
|
||||||
'WebColour' => 'red',
|
'WebColour' => '#ff0000',
|
||||||
'Exif' => array('type'=>'boolean','default'=>0),
|
'Exif' => array('type'=>'boolean','default'=>0),
|
||||||
'Sequence' => null,
|
'Sequence' => null,
|
||||||
'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
'TotalEvents' => array('type'=>'integer', 'default'=>null, 'do_not_update'=>1),
|
||||||
|
@ -120,18 +118,25 @@ private $status_fields = array(
|
||||||
);
|
);
|
||||||
|
|
||||||
public function Control() {
|
public function Control() {
|
||||||
if ( !array_key_exists('Control', $this) ) {
|
if ( !property_exists($this, 'Control') ) {
|
||||||
if ( $this->ControlId() )
|
if ( $this->ControlId() )
|
||||||
$this->{'Control'} = Control::find_one(array('Id'=>$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();
|
$this->{'Control'} = new Control();
|
||||||
}
|
}
|
||||||
return $this->{'Control'};
|
return $this->{'Control'};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Server() {
|
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){
|
public function __call($fn, array $args){
|
||||||
|
@ -142,7 +147,7 @@ private $status_fields = array(
|
||||||
$this->{$fn} = $args[0];
|
$this->{$fn} = $args[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( array_key_exists($fn, $this) ) {
|
if ( property_exists($this, $fn) ) {
|
||||||
return $this->{$fn};
|
return $this->{$fn};
|
||||||
} else if ( array_key_exists($fn, $this->defaults) ) {
|
} else if ( array_key_exists($fn, $this->defaults) ) {
|
||||||
if ( is_array($this->defaults[$fn]) ) {
|
if ( is_array($this->defaults[$fn]) ) {
|
||||||
|
@ -215,9 +220,9 @@ private $status_fields = array(
|
||||||
$this->{'Width'} = $new;
|
$this->{'Width'} = $new;
|
||||||
|
|
||||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Height' : 'Width';
|
$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->{$field};
|
||||||
return $this->defaults{$field};
|
return $this->defaults[$field];
|
||||||
} // end function Width
|
} // end function Width
|
||||||
|
|
||||||
public function ViewHeight($new=null) {
|
public function ViewHeight($new=null) {
|
||||||
|
@ -225,9 +230,9 @@ private $status_fields = array(
|
||||||
$this->{'Height'} = $new;
|
$this->{'Height'} = $new;
|
||||||
|
|
||||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Width' : 'Height';
|
$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->{$field};
|
||||||
return $this->defaults{$field};
|
return $this->defaults[$field];
|
||||||
} // end function Height
|
} // end function Height
|
||||||
|
|
||||||
public function SignalCheckColour($new=null) {
|
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).
|
// Validate that it's a valid colour (we seem to allow color names, not just hex).
|
||||||
// This also helps prevent XSS.
|
// 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->{$field};
|
||||||
}
|
}
|
||||||
return $this->defaults{$field};
|
return $this->defaults[$field];
|
||||||
} // end function SignalCheckColour
|
} // end function SignalCheckColour
|
||||||
|
|
||||||
public static function find( $parameters = array(), $options = array() ) {
|
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');
|
Warning('Attempt to control a monitor with no Id');
|
||||||
return;
|
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' ) {
|
if ( $this->Type() == 'Local' ) {
|
||||||
$zmcArgs = '-d '.$this->{'Device'};
|
$zmcArgs = '-d '.$this->{'Device'};
|
||||||
} else {
|
} else {
|
||||||
|
@ -282,10 +287,11 @@ private $status_fields = array(
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
@ -310,7 +316,7 @@ private $status_fields = array(
|
||||||
return;
|
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 ( $this->{'Function'} == 'None' || $this->{'Function'} == 'Monitor' || $mode == 'stop' ) {
|
||||||
if ( ZM_OPT_CONTROL ) {
|
if ( ZM_OPT_CONTROL ) {
|
||||||
daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'});
|
daemonControl('stop', 'zmtrack.pl', '-m '.$this->{'Id'});
|
||||||
|
@ -340,10 +346,11 @@ private $status_fields = array(
|
||||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url .= '?user='.$_SESSION['username'];
|
||||||
$url = '?pass='.$_SESSION['password'];
|
$url .= '?pass='.$_SESSION['password'];
|
||||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
} else {
|
||||||
$url = '?user='.$_SESSION['username'];
|
Error('Multi-Server requires AUTH_RELAY be either HASH or PLAIN');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
|
@ -371,8 +378,8 @@ private $status_fields = array(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !array_key_exists('GroupIds', $this) ) {
|
if ( !property_exists($this, 'GroupIds') ) {
|
||||||
if ( array_key_exists('Id', $this) and $this->{'Id'} ) {
|
if ( property_exists($this, 'Id') and $this->{'Id'} ) {
|
||||||
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) );
|
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) );
|
||||||
if ( ! $this->{'GroupIds'} )
|
if ( ! $this->{'GroupIds'} )
|
||||||
$this->{'GroupIds'} = array();
|
$this->{'GroupIds'} = array();
|
||||||
|
@ -421,7 +428,7 @@ private $status_fields = array(
|
||||||
if ( $new ) {
|
if ( $new ) {
|
||||||
$this->{'Storage'} = $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'}) ?
|
$this->{'Storage'} = isset($this->{'StorageId'}) ?
|
||||||
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
|
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
|
||||||
new Storage(NULL);
|
new Storage(NULL);
|
||||||
|
@ -471,8 +478,8 @@ private $status_fields = array(
|
||||||
return $source;
|
return $source;
|
||||||
} // end function Source
|
} // end function Source
|
||||||
|
|
||||||
public function UrlToIndex() {
|
public function UrlToIndex($port=null) {
|
||||||
return $this->Server()->UrlToIndex();
|
return $this->Server()->UrlToIndex($port);
|
||||||
//ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
//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
|
# Local
|
||||||
Logger::Debug('Trying to send options ' . print_r($options, true));
|
Logger::Debug('Trying to send options ' . print_r($options, true));
|
||||||
|
|
||||||
|
|
|
@ -1,133 +1,22 @@
|
||||||
<?php
|
<?php
|
||||||
namespace ZM;
|
namespace ZM;
|
||||||
|
require_once('Object.php');
|
||||||
|
|
||||||
require_once('database.php');
|
class MontageLayout extends ZM_Object {
|
||||||
|
protected static $table = 'MontageLayouts';
|
||||||
class MontageLayout {
|
protected $defaults = array(
|
||||||
|
|
||||||
private $defaults = array(
|
|
||||||
'Id' => null,
|
'Id' => null,
|
||||||
'Name' => '',
|
'Name' => '',
|
||||||
'Positions' => 0,
|
'Positions' => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct( $IdOrRow = NULL ) {
|
public static function find( $parameters = array(), $options = array() ) {
|
||||||
if ( $IdOrRow ) {
|
return ZM_Object::_find(get_class(), $parameters, $options);
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $row ) {
|
public static function find_one( $parameters = array(), $options = array() ) {
|
||||||
foreach ($row as $k => $v) {
|
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||||
$this->{$k} = $v;
|
|
||||||
}
|
}
|
||||||
} 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
|
} // end class MontageLayout
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -45,7 +45,7 @@ class ZM_Object {
|
||||||
$this->{$fn} = $args[0];
|
$this->{$fn} = $args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( array_key_exists($fn, $this) ) {
|
if ( property_exists($this, $fn) ) {
|
||||||
return $this->{$fn};
|
return $this->{$fn};
|
||||||
} else {
|
} else {
|
||||||
if ( array_key_exists($fn, $this->defaults) ) {
|
if ( array_key_exists($fn, $this->defaults) ) {
|
||||||
|
@ -110,8 +110,9 @@ class ZM_Object {
|
||||||
|
|
||||||
public static function _find_one($class, $parameters = array(), $options = array() ) {
|
public static function _find_one($class, $parameters = array(), $options = array() ) {
|
||||||
global $object_cache;
|
global $object_cache;
|
||||||
if ( ! isset($object_cache[$class]) )
|
if ( ! isset($object_cache[$class]) ) {
|
||||||
$object_cache[$class] = array();
|
$object_cache[$class] = array();
|
||||||
|
}
|
||||||
$cache = &$object_cache[$class];
|
$cache = &$object_cache[$class];
|
||||||
if (
|
if (
|
||||||
( count($parameters) == 1 ) and
|
( count($parameters) == 1 ) and
|
||||||
|
@ -127,6 +128,11 @@ class ZM_Object {
|
||||||
return $results[0];
|
return $results[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function _clear_cache($class) {
|
||||||
|
global $object_cache;
|
||||||
|
$object_cache[$class] = array();
|
||||||
|
}
|
||||||
|
|
||||||
public static function Objects_Indexed_By_Id($class) {
|
public static function Objects_Indexed_By_Id($class) {
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) {
|
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) {
|
foreach ($this->defaults as $key => $value) {
|
||||||
if ( is_callable(array($this, $key)) ) {
|
if ( is_callable(array($this, $key)) ) {
|
||||||
$json[$key] = $this->$key();
|
$json[$key] = $this->$key();
|
||||||
} else if ( array_key_exists($key, $this) ) {
|
} else if ( property_exists($this, $key) ) {
|
||||||
$json[$key] = $this->{$key};
|
$json[$key] = $this->{$key};
|
||||||
} else {
|
} else {
|
||||||
$json[$key] = $this->defaults{$key};
|
$json[$key] = $this->defaults[$key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return json_encode($json);
|
return json_encode($json);
|
||||||
|
@ -158,14 +164,24 @@ class ZM_Object {
|
||||||
# perhaps should turn into a comma-separated string
|
# perhaps should turn into a comma-separated string
|
||||||
$this->{$k} = implode(',', $v);
|
$this->{$k} = implode(',', $v);
|
||||||
} else if ( is_string($v) ) {
|
} else if ( is_string($v) ) {
|
||||||
if ( $v == '' and array_key_exists($k, $this->defaults) ) {
|
if ( 0 ) {
|
||||||
if ( is_array($this->defaults[$k]) )
|
# 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'];
|
$this->{$k} = $this->defaults[$k]['default'];
|
||||||
else
|
} else {
|
||||||
$this->{$k} = $this->defaults[$k];
|
$this->{$k} = $this->defaults[$k];
|
||||||
|
Logger::Debug("$k => Have default for $v: " . $this->{$k});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->{$k} = trim($v);
|
$this->{$k} = trim($v);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$this->{$k} = trim($v);
|
||||||
|
}
|
||||||
|
|
||||||
} else if ( is_integer($v) ) {
|
} else if ( is_integer($v) ) {
|
||||||
$this->{$k} = $v;
|
$this->{$k} = $v;
|
||||||
} else if ( is_bool($v) ) {
|
} else if ( is_bool($v) ) {
|
||||||
|
@ -192,7 +208,7 @@ class ZM_Object {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->defaults[$field] ) {
|
if ( isset($this->defaults[$field]) ) {
|
||||||
if ( is_array($this->defaults[$field]) ) {
|
if ( is_array($this->defaults[$field]) ) {
|
||||||
$new_values[$field] = $this->defaults[$field]['default'];
|
$new_values[$field] = $this->defaults[$field]['default'];
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,7 +231,7 @@ class ZM_Object {
|
||||||
} else if ( $this->$field() != $value ) {
|
} else if ( $this->$field() != $value ) {
|
||||||
$changes[$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';
|
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
||||||
Logger::Debug("Checking field $field => current ".
|
Logger::Debug("Checking field $field => current ".
|
||||||
(is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' .
|
(is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' .
|
||||||
|
@ -280,6 +296,18 @@ class ZM_Object {
|
||||||
$this->set($new_values);
|
$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(
|
$fields = array_filter(
|
||||||
$this->defaults,
|
$this->defaults,
|
||||||
function($v) {
|
function($v) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ class Server extends ZM_Object {
|
||||||
|
|
||||||
public function PathToZMS( $new = null ) {
|
public function PathToZMS( $new = null ) {
|
||||||
if ( $new != null )
|
if ( $new != null )
|
||||||
$this{'PathToZMS'} = $new;
|
$this->{'PathToZMS'} = $new;
|
||||||
if ( $this->Id() and $this->{'PathToZMS'} ) {
|
if ( $this->Id() and $this->{'PathToZMS'} ) {
|
||||||
return $this->{'PathToZMS'};
|
return $this->{'PathToZMS'};
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -58,6 +58,13 @@ class Storage extends ZM_Object {
|
||||||
return $this->{'Events'};
|
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() {
|
public function disk_usage_percent() {
|
||||||
$path = $this->Path();
|
$path = $this->Path();
|
||||||
if ( ! $path ) {
|
if ( ! $path ) {
|
||||||
|
@ -80,7 +87,7 @@ class Storage extends ZM_Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function disk_total_space() {
|
public function disk_total_space() {
|
||||||
if ( !array_key_exists('disk_total_space', $this) ) {
|
if ( !property_exists($this, 'disk_total_space') ) {
|
||||||
$path = $this->Path();
|
$path = $this->Path();
|
||||||
if ( file_exists($path) ) {
|
if ( file_exists($path) ) {
|
||||||
$this->{'disk_total_space'} = disk_total_space($path);
|
$this->{'disk_total_space'} = disk_total_space($path);
|
||||||
|
@ -94,7 +101,7 @@ class Storage extends ZM_Object {
|
||||||
|
|
||||||
public function disk_used_space() {
|
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.
|
# 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' ) {
|
if ( $this->{'Type'} == 's3fs' ) {
|
||||||
$this->{'disk_used_space'} = $this->event_disk_space();
|
$this->{'disk_used_space'} = $this->event_disk_space();
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,17 +119,18 @@ class Storage extends ZM_Object {
|
||||||
|
|
||||||
public function event_disk_space() {
|
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.
|
# 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()));
|
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()));
|
||||||
|
|
||||||
do {
|
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));
|
$events = Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null), array('limit'=>1000));
|
||||||
foreach ( $events as $Event ) {
|
foreach ( $events as $Event ) {
|
||||||
$Event->Storage($this); // Prevent further db hit
|
$Event->Storage($this); // Prevent further db hit
|
||||||
# DiskSpace will update the event
|
# DiskSpace will update the event
|
||||||
$used += $Event->DiskSpace();
|
$used += $Event->DiskSpace();
|
||||||
} #end foreach
|
} #end foreach
|
||||||
|
Event::clear_cache();
|
||||||
} while ( count($events) == 1000 );
|
} while ( count($events) == 1000 );
|
||||||
$this->{'DiskSpace'} = $used;
|
$this->{'DiskSpace'} = $used;
|
||||||
}
|
}
|
||||||
|
@ -130,8 +138,8 @@ class Storage extends ZM_Object {
|
||||||
} // end function event_disk_space
|
} // end function event_disk_space
|
||||||
|
|
||||||
public function Server() {
|
public function Server() {
|
||||||
if ( ! array_key_exists('Server',$this) ) {
|
if ( ! property_exists($this, 'Server') ) {
|
||||||
if ( array_key_exists('ServerId', $this) ) {
|
if ( property_exists($this, 'ServerId') ) {
|
||||||
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
||||||
|
|
||||||
if ( !$this->{'Server'} ) {
|
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