Add HikVision ptz control sciprt
This commit is contained in:
parent
ae0afaed0c
commit
4be668f838
|
@ -595,6 +595,7 @@ INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Libvlc','IPCC7210W', 1, 1, 1,
|
|||
INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,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,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 6, 0, 1, 1, 1, 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,'HikVision','Local','',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,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
|
||||
|
||||
--
|
||||
-- Add some monitor preset values
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder HikVision Control Protocol Module
|
||||
# Copyright (C) 2016 Terry Sanders
|
||||
#
|
||||
# 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 an implementation of the HikVision ISAPI camera control
|
||||
# protocol
|
||||
#
|
||||
package ZoneMinder::Control::HikVision;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
|
||||
our @ISA = qw(ZoneMinder::Control);
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# HiKVision ISAPI Control Protocol
|
||||
#
|
||||
# Set the following:
|
||||
# ControlAddress: username:password@camera_webaddress:port
|
||||
# ControlDevice: IP Camera Model
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
use LWP::UserAgent;
|
||||
use HTTP::Cookies;
|
||||
|
||||
my $ChannelID = 1; # Usually...
|
||||
my $DefaultFocusSpeed = 50; # Should be between 1 and 100
|
||||
my $DefaultIrisSpeed = 50; # Should be between 1 and 100
|
||||
|
||||
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();
|
||||
#
|
||||
# Create a UserAgent for the requests
|
||||
#
|
||||
$self->{UA} = LWP::UserAgent->new();
|
||||
$self->{UA}->cookie_jar( {} );
|
||||
#
|
||||
# Extract the username/password host/port from ControlAddress
|
||||
#
|
||||
my ($user,$pass,$host,$port);
|
||||
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ ) { # user:pass@host...
|
||||
$user = $1;
|
||||
$pass = $2;
|
||||
$host = $3;
|
||||
}
|
||||
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ ) { # user@host...
|
||||
$user = $1;
|
||||
$host = $2;
|
||||
}
|
||||
else { # Just a host
|
||||
$host = $self->{Monitor}{ControlAddress};
|
||||
}
|
||||
# Check if it is a host and port or just a host
|
||||
if( $host =~ /([^:]+):(.+)/ ) {
|
||||
$host = $1;
|
||||
$port = $2;
|
||||
}
|
||||
else {
|
||||
$port = 80;
|
||||
}
|
||||
# Save the credentials
|
||||
if( defined($user) ) {
|
||||
$self->{UA}->credentials( "$host:$port", $self->{Monitor}{ControlDevice}, $user, $pass );
|
||||
}
|
||||
# Save the base url
|
||||
$self->{BaseURL} = "http://$host:$port";
|
||||
}
|
||||
sub PutCmd {
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $content = shift;
|
||||
my $req = HTTP::Request->new(PUT => "$self->{BaseURL}/$cmd");
|
||||
if(defined($content)) {
|
||||
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
|
||||
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||
}
|
||||
my $res = $self->{UA}->request($req);
|
||||
unless( $res->is_success ) {
|
||||
#
|
||||
# The camera timeouts connections at short intervals. When this
|
||||
# happens the user agent connects again and uses the same auth tokens.
|
||||
# The camera rejects this and asks for another token but the UserAgent
|
||||
# just gives up. Because of this I try the request again and it should
|
||||
# succeed the second time if the credentials are correct.
|
||||
#
|
||||
if($res->code == 401) {
|
||||
$res = $self->{UA}->request($req);
|
||||
unless( $res->is_success ) {
|
||||
#
|
||||
# It has failed authentication. The odds are
|
||||
# that the user has set some paramater incorrectly
|
||||
# so check the realm against the ControlDevice
|
||||
# entry and send a message if different
|
||||
#
|
||||
my $auth = $res->headers->www_authenticate;
|
||||
foreach (split(/\s*,\s*/,$auth)) {
|
||||
if( $_ =~ /^realm\s*=\s*"([^"]+)"/i ) {
|
||||
if( $self->{Monitor}{ControlDevice} ne $1 ) {
|
||||
Info "Control Device appears to be incorrect.";
|
||||
Info "Control Device should be set to \"$1\".";
|
||||
Info "Control Device currently set to \"$self->{Monitor}{ControlDevice}\".";
|
||||
}
|
||||
}
|
||||
}
|
||||
#
|
||||
# Check for username/password
|
||||
#
|
||||
if( $self->{Monitor}{ControlAddress} =~ /.+:(.+)@.+/ ) {
|
||||
Info "Check username/password is correct";
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^[^:]+@.+/ ) {
|
||||
Info "No password in Control Address. Should there be one?";
|
||||
} elsif ( $self->{Monitor}{ControlAddress} =~ /^:.+@.+/ ) {
|
||||
Info "Password but no username in Control Address.";
|
||||
} else {
|
||||
Info "Missing username and password in Control Address.";
|
||||
}
|
||||
Fatal $res->status_line;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Fatal $res->status_line;
|
||||
}
|
||||
}
|
||||
}
|
||||
#
|
||||
# The move continuous functions all call moveVector
|
||||
# with the direction to move in. This includes zoom
|
||||
#
|
||||
sub moveVector {
|
||||
my $self = shift;
|
||||
my $pandirection = shift;
|
||||
my $tiltdirection = shift;
|
||||
my $zoomdirection = shift;
|
||||
my $params = shift;
|
||||
my $command; # The ISAPI/PTZ command
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Change from microseconds to milliseconds
|
||||
$duration = int($duration/1000);
|
||||
my $momentxml;
|
||||
if( $duration ) {
|
||||
$momentxml = "<Momentary><duration>$duration</duration></Momentary>";
|
||||
$command = "ISAPI/PTZCtrl/channels/$ChannelID/momentary";
|
||||
}
|
||||
else {
|
||||
$momentxml = "";
|
||||
$command = "ISAPI/PTZCtrl/channels/$ChannelID/continuous";
|
||||
}
|
||||
# Calculate movement speeds
|
||||
my $x = $pandirection * $self->getParam( $params, 'panspeed', 0 );
|
||||
my $y = $tiltdirection * $self->getParam( $params, 'tiltspeed', 0 );
|
||||
my $z = $zoomdirection * $self->getParam( $params, 'speed', 0 );
|
||||
# Create the XML
|
||||
my $xml = "<PTZData><pan>$x</pan><tilt>$y</tilt><zoom>$z</zoom>$momentxml</PTZData>";
|
||||
# Send it to the camera
|
||||
$self->PutCmd($command,$xml);
|
||||
}
|
||||
sub moveStop { $_[0]->moveVector( 0, 0, 0, splice(@_,1)); }
|
||||
sub moveConUp { $_[0]->moveVector( 0, 1, 0, splice(@_,1)); }
|
||||
sub moveConUpRight { $_[0]->moveVector( 1, 1, 0, splice(@_,1)); }
|
||||
sub moveConRight { $_[0]->moveVector( 1, 0, 0, splice(@_,1)); }
|
||||
sub moveConDownRight { $_[0]->moveVector( 1, -1, 0, splice(@_,1)); }
|
||||
sub moveConDown { $_[0]->moveVector( 0, -1, 0, splice(@_,1)); }
|
||||
sub moveConDownLeft { $_[0]->moveVector( -1, -1, 0, splice(@_,1)); }
|
||||
sub moveConLeft { $_[0]->moveVector( -1, 0, 0, splice(@_,1)); }
|
||||
sub moveConUpLeft { $_[0]->moveVector( -1, 1, 0, splice(@_,1)); }
|
||||
sub zoomConTele { $_[0]->moveVector( 0, 0, 1, splice(@_,1)); }
|
||||
sub zoomConWide { $_[0]->moveVector( 0, 0,-1, splice(@_,1)); }
|
||||
#
|
||||
# Presets including Home set and clear
|
||||
#
|
||||
sub presetGoto {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params,'preset');
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/presets/$preset/goto");
|
||||
}
|
||||
sub presetSet {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam($params,'preset');
|
||||
my $xml = "<PTZPreset><id>$preset</id></PTZPreset>";
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/presets/$preset",$xml);
|
||||
}
|
||||
sub presetHome {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->PutCmd("ISAPI/PTZCtrl/channels/$ChannelID/homeposition/goto");
|
||||
}
|
||||
#
|
||||
# Focus controls all call Focus with a +/- speed
|
||||
#
|
||||
sub Focus {
|
||||
my $self = shift;
|
||||
my $speed = shift;
|
||||
my $xml = "<FocusData><focus>$speed</focus></FocusData>";
|
||||
$self->PutCmd("ISAPI/System/Video/inputs/channels/$ChannelID/focus",$xml);
|
||||
}
|
||||
sub focusConNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Near {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->Focus(-$DefaultFocusSpeed);
|
||||
}
|
||||
sub focusAbsNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
}
|
||||
sub focusRelNear {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus(-$speed);
|
||||
}
|
||||
sub focusConFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Far {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->Focus($DefaultFocusSpeed);
|
||||
}
|
||||
sub focusAbsFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
}
|
||||
sub focusRelFar {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the focus speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultFocusSpeed );
|
||||
$self->Focus($speed);
|
||||
}
|
||||
#
|
||||
# Iris controls all call Iris with a +/- speed
|
||||
#
|
||||
sub Iris {
|
||||
my $self = shift;
|
||||
my $speed = shift;
|
||||
|
||||
my $xml = "<IrisData><iris>$speed</iris></IrisData>";
|
||||
$self->PutCmd("ISAPI/System/Video/inputs/channels/$ChannelID/iris",$xml);
|
||||
}
|
||||
sub irisConClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Close {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
$self->Iris(-$DefaultIrisSpeed);
|
||||
}
|
||||
sub irisAbsClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
}
|
||||
sub irisRelClose {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris(-$speed);
|
||||
}
|
||||
sub irisConOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Calculate autostop time
|
||||
my $duration = $self->getParam( $params, 'autostop', 0 ) * $self->{Monitor}{AutoStopTimeout};
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
if($duration) {
|
||||
usleep($duration);
|
||||
$self->moveStop($params);
|
||||
}
|
||||
}
|
||||
sub Open {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
$self->Iris($DefaultIrisSpeed);
|
||||
}
|
||||
sub irisAbsOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
}
|
||||
sub irisRelOpen {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
# Get the iris speed
|
||||
my $speed = $self->getParam( $params, 'speed', $DefaultIrisSpeed );
|
||||
$self->Iris($speed);
|
||||
}
|
||||
#
|
||||
# reset (reboot) the device
|
||||
#
|
||||
sub reset {
|
||||
my $self = shift;
|
||||
|
||||
$self->PutCmd("ISAPI/System/reboot");
|
||||
}
|
||||
|
||||
1;
|
||||
|
Loading…
Reference in New Issue