diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 3f3889b27..d084cda2a 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -645,7 +645,7 @@ CREATE INDEX `Monitors_ServerId_idx` ON `Monitors` (`ServerId`); DROP TABLE IF EXISTS `Monitor_Status`; CREATE TABLE `Monitor_Status` ( `MonitorId` int(10) unsigned NOT NULL, - `Status` enum('Unknown','NotRunning','Running','NoSignal','Signal') NOT NULL default 'Unknown', + `Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown', `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, PRIMARY KEY (`MonitorId`) diff --git a/db/zm_update-1.31.30.sql b/db/zm_update-1.31.30.sql new file mode 100644 index 000000000..c87b4409a --- /dev/null +++ b/db/zm_update-1.31.30.sql @@ -0,0 +1,20 @@ +DROP TABLE IF EXISTS `Monitor_Status`; +CREATE TABLE `Monitor_Status` ( + `MonitorId` int(10) unsigned NOT NULL, + `Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown', + `CaptureFPS` DECIMAL(10,2) NOT NULL default 0, + `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, + PRIMARY KEY (`MonitorId`) +) ENGINE=MEMORY; + +SET SESSION sql_mode='NO_AUTO_VALUE_ON_ZERO'; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM Storage WHERE Name = 'Default' AND Id=0 AND Path='/var/cache/zoneminder/events' + ) > 0, + "SELECT 'Default Storage Area already exists.'", + "INSERT INTO Storage (Id,Name,Path,Scheme,ServerId) VALUES (0,'Default','/var/cache/zoneminder/events','Medium',NULL)" + )); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/onvif/CMakeLists.txt b/onvif/CMakeLists.txt index e1ad01556..a6b9bde4e 100644 --- a/onvif/CMakeLists.txt +++ b/onvif/CMakeLists.txt @@ -3,5 +3,3 @@ # Process the perl modules subdirectory add_subdirectory(proxy) add_subdirectory(modules) -add_subdirectory(scripts) - diff --git a/onvif/scripts/CMakeLists.txt b/onvif/scripts/CMakeLists.txt deleted file mode 100644 index 3405fa756..000000000 --- a/onvif/scripts/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# CMakeLists.txt for the ZoneMinder perl scripts. - -# If this is an out-of-source build, copy the files we need to the binary directory -if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) - file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/zmonvif-probe.pl" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") -endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) - -# Install the perl scripts -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/onvif/scripts/zmonvif-probe.pl b/onvif/scripts/zmonvif-probe.pl deleted file mode 100755 index de6c18aa3..000000000 --- a/onvif/scripts/zmonvif-probe.pl +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/perl -w -use strict; -# -# ========================================================================== -# -# ZoneMinder ONVIF Control Protocol Module -# Copyright (C) 2014 Jan M. Hochstein -# -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# ========================================================================== -# -# This module contains the implementation of the ONVIF capability prober -# - -use Getopt::Std; -use Data::UUID; - -require ONVIF::Client; - -require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort; -require WSDiscovery10::Elements::Header; -require WSDiscovery10::Elements::Types; -require WSDiscovery10::Elements::Scopes; - -require WSDiscovery::TransportUDP; - -# -# ======================================================================== -# Globals - -my $verbose = 0; -my $soap_version = undef; -my $client; - -# ========================================================================= -# internal functions - -sub deserialize_message -{ - my ($wsdl_client, $response) = @_; - - # copied and adapted from SOAP::WSDL::Client - - # get deserializer - my $deserializer = $wsdl_client->get_deserializer(); - - if(! $deserializer) { - $deserializer = SOAP::WSDL::Factory::Deserializer->get_deserializer({ - soap_version => $wsdl_client->get_soap_version(), - %{ $wsdl_client->get_deserializer_args() }, - }); - } - # set class resolver if serializer supports it - $deserializer->set_class_resolver( $wsdl_client->get_class_resolver() ) - if ( $deserializer->can('set_class_resolver') ); - - # Try deserializing response - there may be some, - # even if transport did not succeed (got a 500 response) - if ( $response ) { - # as our faults are false, returning a success marker is the only - # reliable way of determining whether the deserializer succeeded. - # Custom deserializers may return an empty list, or undef, - # and $@ is not guaranteed to be undefined. - my ($success, $result_body, $result_header) = eval { - (1, $deserializer->deserialize( $response )); - }; - if (defined $success) { - return wantarray - ? ($result_body, $result_header) - : $result_body; - } - elsif (blessed $@) { #}&& $@->isa('SOAP::WSDL::SOAP::Typelib::Fault11')) { - return $@; - } - else { - return $deserializer->generate_fault({ - code => 'soap:Server', - role => 'urn:localhost', - message => "Error deserializing message: $@. \n" - . "Message was: \n$response" - }); - } - }; -} - - -sub interpret_messages -{ - my ($svc_discover, $services, @responses ) = @_; - - foreach my $response ( @responses ) { - - if($verbose) { - print "Received message:\n" . $response . "\n"; - } - - my $result = deserialize_message($svc_discover, $response); - if(not $result) { - if($verbose) { - print "Error deserializing message. No message returned from deserializer.\n"; - } - next; - } - - my $xaddr; - foreach my $l_xaddr (split ' ', $result->get_ProbeMatch()->get_XAddrs()) { - # find IPv4 address - if($verbose) { - print "l_xaddr = $l_xaddr\n"; - } - if($l_xaddr =~ m|//[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[:/]|) { - $xaddr = $l_xaddr; - last; - } else { - print STDERR "Unable to find IPv4 address from xaddr $l_xaddr\n"; - } - } - - # No usable address found - next if not $xaddr; - - # ignore multiple responses from one service - next if defined $services->{$xaddr}; - $services->{$xaddr} = 1; - - print "$xaddr, " . $svc_discover->get_soap_version() . ", "; - - print "("; - my $scopes = $result->get_ProbeMatch()->get_Scopes(); - my $count = 0; - foreach my $scope(split ' ', $scopes) { - if($scope =~ m|onvif://www\.onvif\.org/(.+)/(.*)|) { - my ($attr, $value) = ($1,$2); - if( 0 < $count ++) { - print ", "; - } - print $attr . "=\'" . $value . "\'"; - } - } - print ")\n"; - } -} - -# ========================================================================= -# functions - -sub discover -{ - ## collect all responses - my @responses = (); - - no warnings 'redefine'; - - *WSDiscovery::TransportUDP::_notify_response = sub { - my ($transport, $response) = @_; - push @responses, $response; - }; - - ## try both soap versions - my %services; - - my $uuid_gen = Data::UUID->new(); - - if ( ( ! $soap_version ) or ( $soap_version eq '1.1' ) ) { - - if($verbose) { - print "Probing for SOAP 1.1\n" - } - my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({ -# no_dispatch => '1', - }); - $svc_discover->set_soap_version('1.1'); - - my $uuid = $uuid_gen->create_str(); - - my $result = $svc_discover->ProbeOp( - { # WSDiscovery::Types::ProbeType - Types => 'http://www.onvif.org/ver10/network/wsdl:NetworkVideoTransmitter http://www.onvif.org/ver10/device/wsdl:Device', # QNameListType - Scopes => { value => '' }, - }, - WSDiscovery10::Elements::Header->new({ - Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' }, - MessageID => { value => "urn:uuid:$uuid" }, - To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }, - }) - ); - print $result . "\n" if $verbose; - - interpret_messages($svc_discover, \%services, @responses); - @responses = (); - } # end if doing soap 1.1 - - if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) { - if($verbose) { - print "Probing for SOAP 1.2\n" - } - my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({ -# no_dispatch => '1', - }); - $svc_discover->set_soap_version('1.2'); - -# copies of the same Probe message must have the same MessageID. -# This is not a copy. So we generate a new uuid. - my $uuid = $uuid_gen->create_str(); - -# Everyone else, like the nodejs onvif code and odm only ask for NetworkVideoTransmitter - my $result = $svc_discover->ProbeOp( - { # WSDiscovery::Types::ProbeType - xmlattr => { 'xmlns:dn' => 'http://www.onvif.org/ver10/network/wsdl', }, - Types => 'dn:NetworkVideoTransmitter', # QNameListType - Scopes => { value => '' }, - }, - WSDiscovery10::Elements::Header->new({ - Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' }, - MessageID => { value => "urn:uuid:$uuid" }, - To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }, - }) - ); - print $result . "\n" if $verbose; - interpret_messages($svc_discover, \%services, @responses); - } # end if doing soap 1.2 - -} - - -sub profiles -{ -# my $result = $services{media}{ep}->GetVideoSources( { } ,, ); -# die $result if not $result; -# print $result . "\n"; - - my $result = $client->get_endpoint('media')->GetProfiles( { } ,, ); - die $result if not $result; - if($verbose) { - print "Received message:\n" . $result . "\n"; - } - - my $profiles = $result->get_Profiles(); - - foreach my $profile ( @{ $profiles } ) { - - my $token = $profile->attr()->get_token() ; - - # Specification gives conflicting values for unicast stream types, try both. - # http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri - foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) { - $result = $client->get_endpoint('media')->GetStreamUri( { - StreamSetup => { # ONVIF::Media::Types::StreamSetup - Stream => $streamtype, # StreamType - Transport => { # ONVIF::Media::Types::Transport - Protocol => 'RTSP', # TransportProtocol - }, - }, - ProfileToken => $token, # ReferenceToken - } ,, ); - last if $result; - } - die $result if not $result; - # print $result . "\n"; - - my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration(); - print join(', ', $token, - $profile->get_Name(), - ( $VideoEncoderConfiguration ? ( - $VideoEncoderConfiguration->get_Encoding(), - $VideoEncoderConfiguration->get_Resolution()->get_Width(), - $VideoEncoderConfiguration->get_Resolution()->get_Height(), - $VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(), - ) : () ), - $result->get_MediaUri()->get_Uri() , - ). "\n"; - } # end foreach profile - -# -# use message parser without schema validation ??? -# - -} - -sub move -{ - my ($dir) = @_; - - - my $result = $client->get_endpoint('ptz')->GetNodes( { } ,, ); - - die $result if not $result; - print $result . "\n"; - -} - -sub metadata -{ - my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, ); - die $result if not $result; - print $result . "\n"; - - $result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, ); - die $result if not $result; - print $result . "\n"; - -# $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, ); -# die $result if not $result; -# print $result . "\n"; - -} - -# ======================================================================== -# options processing - -$Getopt::Std::STANDARD_HELP_VERSION = 1; - -our ($opt_v); - -my $OPTIONS = "v"; - -sub HELP_MESSAGE -{ - my ($fh, $pkg, $ver, $opts) = @_; - print $fh "Usage: " . __FILE__ . " [-v] probe \n"; - print $fh " " . __FILE__ . " [-v] \n"; - print $fh <new( { - 'url_svc_device' => $url_svc_device, - 'soap_version' => $soap_version } ); - - $client->set_credentials($username, $password, 1); - - $client->create_services(); - - - if($action eq "profiles") { - - profiles(); - } - elsif($action eq "move") { - my $dir = shift; - move($dir); - } - elsif($action eq "metadata") { - metadata(); - } - else { - print("Error: Unknown command\"$action\""); - exit(1); - } -} diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 81cfdaa56..f1bfa2ed1 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -8,6 +8,7 @@ configure_file(zmaudit.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" @ONLY) configure_file(zmcontrol.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" @ONLY) configure_file(zmdc.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" @ONLY) configure_file(zmfilter.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" @ONLY) +configure_file(zmonvif-probe.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" @ONLY) configure_file(zmpkg.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" @ONLY) configure_file(zmtrack.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" @ONLY) configure_file(zmtrigger.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" @ONLY) @@ -34,7 +35,7 @@ FOREACH(PERLSCRIPT ${perlscripts}) ENDFOREACH(PERLSCRIPT ${perlscripts}) # Install the perl scripts -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(NOT ZM_NO_X10) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif(NOT ZM_NO_X10) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in index 23914bdd2..9ce556beb 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in @@ -45,7 +45,6 @@ our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; -use Getopt::Std; use Data::UUID; use vars qw( $verbose $soap_version ); @@ -185,11 +184,11 @@ sub discover { }; ## try both soap versions - my %services; my $uuid_gen = Data::UUID->new(); if ( ( ! $soap_version ) or ( $soap_version eq '1.1' ) ) { + my %services; if($verbose) { print "Probing for SOAP 1.1\n" @@ -219,6 +218,7 @@ sub discover { } # end if doing soap 1.1 if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) { + my %services; if($verbose) { print "Probing for SOAP 1.2\n" } @@ -253,8 +253,17 @@ sub discover { sub profiles { my ( $client ) = @_; - my $result = $client->get_endpoint('media')->GetProfiles( { } ,, ); - die $result if not $result; + my $endpoint = $client->get_endpoint('media'); + if ( ! $endpoint ) { + print "No media enpoint for client.\n"; + return; + } + + my $result = $endpoint->GetProfiles( { } ,, ); + if ( ! $result ) { + print "No result from GetProfiles\n"; + return; + } if($verbose) { print "Received message:\n" . $result . "\n"; } diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index ee63a0811..cdd283b28 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -269,7 +269,7 @@ MAIN: while( $loop ) { #Event path is hour/minute/sec my $event_path = readlink( $event_link ); - if ( !-e $event_path ) { + if ( !($event_path and -e $event_path) ) { aud_print( "Event link $day_dir/$event_link does not point to valid target" ); if ( confirm() ) { ( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 8c1357601..e606be872 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -183,39 +183,39 @@ use Sys::CpuLoad; socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); my $attempts = 0; - while( !connect( CLIENT, $saddr ) ) { + while( !connect(CLIENT, $saddr) ) { $attempts++; - Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts" ); - Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); + Debug("Waiting for zmdc.pl server process at ".SOCK_FILE.", attempt $attempts"); + Fatal("Can't connect: $!") if $attempts > MAX_CONNECT_DELAY; usleep(200000); } # end while } elsif ( defined($cpid) ) { ZMServer::run(); } else { - Fatal( "Can't fork: $!" ); + Fatal("Can't fork: $!"); } } # end if ! server is up -if ( $command eq 'check' && ! $daemon ) { - print( "running\n" ); +if ( ($command eq 'check') && !$daemon ) { + print("running\n"); exit(); } elsif ( $command eq 'startup' ) { # Our work here is done - exit() if ( !$server_up ); + exit() if !$server_up; } # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); my $message = join(';', $command, ( $daemon ? $daemon : () ), @args ); -print( CLIENT $message ); -shutdown( CLIENT, 1 ); +print(CLIENT $message); +shutdown(CLIENT, 1); while( my $line = ) { - chomp( $line ); - print( "$line\n" ); + chomp($line); + print("$line\n"); } # And we're done! -close( CLIENT ); +close(CLIENT); #print( "Finished writing, bye\n" ); exit; @@ -344,9 +344,9 @@ sub run { } else { dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); } - close( CLIENT ); + close(CLIENT); } else { - Fatal( 'Bogus descriptor' ); + Fatal('Bogus descriptor'); } } elsif ( $nfound < 0 ) { if ( $! == EINTR ) { @@ -355,9 +355,9 @@ sub run { # See if it needs to start up again restartPending(); } elsif ( $! == EPIPE ) { - Error( "Can't select: $!" ); + Error("Can't select: $!"); } else { - Fatal( "Can't select: $!" ); + Fatal("Can't select: $!"); } } else { #print( "Select timed out\n" ); @@ -566,19 +566,19 @@ sub restart { my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if @args; - dPrint ( ZoneMinder::Logger::WARNING, "Restarting $command\n"); + dPrint ( ZoneMinder::Logger::DEBUG, "Restarting $command\n"); my $process = $cmd_hash{$command}; if ( $process ) { - dPrint ( ZoneMinder::Logger::WARNING, "Have process" ); + dPrint( ZoneMinder::Logger::DEBUG, "Have process" ); if ( $process->{pid} ) { - dPrint ( ZoneMinder::Logger::WARNING, "Have process pid " .$process->{pid} ); + dPrint( ZoneMinder::Logger::DEBUG, "Have process pid " .$process->{pid} ); my $cpid = $process->{pid}; if ( defined($pid_hash{$cpid}) ) { - dPrint ( ZoneMinder::Logger::WARNING, "Have process pid hash " .$process->{pid} ); + dPrint( ZoneMinder::Logger::DEBUG, "Have process pid hash " .$process->{pid} ); _stop( 0, $process ); return; } else { - dPrint ( ZoneMinder::Logger::WARNING, "Not sending stop" ); + dPrint( ZoneMinder::Logger::DEBUG, "Not sending stop" ); } } } diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 1099d5fc2..12dae98cf 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -73,6 +73,7 @@ use autouse 'Data::Dumper'=>qw(Dumper); my $filter_name = ''; my $filter_id; my $version = 0; +my $zm_terminate = 0; GetOptions( 'filter=s' =>\$filter_name, @@ -94,7 +95,20 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) ; logInit(); -logSetSignal(); +sub HupHandler { + Info("Received HUP, reloading"); + &ZoneMinder::Logger::logHupHandler(); +} +sub TermHandler { + Info("Received TERM, exiting"); + $zm_terminate = 1; +} +sub Term { + exit( 0 ); +} +$SIG{HUP} = \&HupHandler; +$SIG{TERM} = \&TermHandler; +$SIG{INT} = \&TermHandler; if ( $Config{ZM_OPT_UPLOAD} ) { # Comment these out if you don't have them and don't want to upload @@ -166,7 +180,7 @@ if ( ! ( $filter_name or $filter_id ) ) { my @filters; my $last_action = 0; -while( 1 ) { +while( ! $zm_terminate ) { my $now = time; if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { Debug( "Reloading filters\n" ); @@ -175,6 +189,7 @@ while( 1 ) { } foreach my $filter ( @filters ) { + last if $zm_terminate; if ( $$filter{Concurrent} and ! ( $filter_id or $filter_name ) ) { my ( $proc ) = $0 =~ /(\S+)/; my ( $id ) = $$filter{Id} =~ /(\d+)/; @@ -186,7 +201,7 @@ while( 1 ) { } } - last if ( $filter_name or $filter_id ); + last if $filter_name or $filter_id or $zm_terminate; Debug( "Sleeping for $delay seconds\n" ); sleep( $delay ); @@ -266,6 +281,7 @@ sub checkFilter { ) ); foreach my $event ( @Events ) { + last if $zm_terminate; Debug( "Checking event $event->{Id}" ); my $delete_ok = !undef; $dbh->ping(); diff --git a/scripts/zmonvif-probe.pl.in b/scripts/zmonvif-probe.pl.in new file mode 100755 index 000000000..001d89f83 --- /dev/null +++ b/scripts/zmonvif-probe.pl.in @@ -0,0 +1,155 @@ +#!/usr/bin/perl -w +use strict; +# +# ========================================================================== +# +# ZoneMinder ONVIF Control Protocol Module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# ========================================================================== +# +# This module contains the implementation of the ONVIF capability prober +# + +use Getopt::Std; + +require ONVIF::Client; + + +# ======================================================================== +# options processing + +$Getopt::Std::STANDARD_HELP_VERSION = 1; + +our ($opt_v); + +my $OPTIONS = "v"; + +sub HELP_MESSAGE +{ + my ($fh, $pkg, $ver, $opts) = @_; + print $fh "Usage: " . __FILE__ . " [-v] probe \n"; + print $fh " " . __FILE__ . " [-v] \n"; + print $fh <new( { + 'url_svc_device' => $url_svc_device, + 'soap_version' => $soap_version } ); + + $client->set_credentials($username, $password, 1); + + $client->create_services(); + + + if ( $action eq "profiles" ) { + ZoneMinder::ONVIF::profiles($client); + } elsif( $action eq "move" ) { + my $dir = shift; + ZoneMinder::ONVIF::move($client, $dir); + } elsif ( $action eq "metadata" ) { + ZoneMinder::ONVIF::metadata($client); + } else { + print("Error: Unknown command\"$action\""); + exit(1); + } +} + +1; +__END__ + +=head1 NAME + +zmonvif-probe.pl - ZoneMinder ONVIF probing tool + +=head1 SYNOPSIS + + zmonfig-probe.pl [-v] probe + [-v] \n"; + + Commands are: + probe - scan for devices on the local network and list them + profiles - print the device's supported stream configurations + metadata - print some of the device's configuration settings + move - move the device (only ptz cameras) + Common parameters: + -v - increase verbosity + Device access parameters (for all commands but 'probe'): + device URL - the ONVIF Device service URL + soap version - SOAP version (1.1 or 1.2) + user - username of a user with access to the device + password - password for the user + +=head1 DESCRIPTION + + +=head1 OPTIONS + + -c, --continuous - Run continuously + -f, --force - Run even if pid file exists + -i, --interactive - Ask before applying any changes + -m, --monitor_id - Only consider the given monitor + -r, --report - Just report don't actually do anything + -s, --storage_id - Specify a storage area to audit instead of all + -v, --version - Print the installed version of ZoneMinder + +=cut + diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 25fa3faf0..09aa70a36 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -58,10 +58,16 @@ Event::Event( std::string notes; createNotes(notes); + struct timeval now; + gettimeofday(&now, 0); + bool untimedEvent = false; if ( !start_time.tv_sec ) { untimedEvent = true; - gettimeofday(&start_time, 0); + start_time = now; + } else if ( start_time.tv_sec > now.tv_sec ) { + Error("StartTime in the future"); + start_time = now; } Storage * storage = monitor->getStorage(); @@ -420,7 +426,8 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); - if ( delta_time.sec > 99999999 ) { + // Delta is Decimal(8,2) so 6 integer digits and 2 decimal digits + if ( delta_time.sec > 999999 ) { Warning("Invalid delta_time from_unixtime(%ld), %s%ld.%02ld", timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); delta_time.sec = 0; diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 5ddd8ee82..77e3c90aa 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -458,5 +458,5 @@ void dumpPacket(AVPacket *pkt, const char *text) { pkt->flags & AV_PKT_FLAG_KEY, pkt->pos, pkt->duration); - Debug(1, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); + Debug(2, "%s:%d:%s: %s", __FILE__, __LINE__, text, b); } diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index ed1e5dce6..b42caa39e 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -121,6 +121,9 @@ FfmpegCamera::~FfmpegCamera() { } int FfmpegCamera::PrimeCapture() { + if ( mCanCapture ) { + CloseFfmpeg(); + } mVideoStreamId = -1; mAudioStreamId = -1; Info( "Priming capture from %s", mPath.c_str() ); @@ -129,6 +132,7 @@ int FfmpegCamera::PrimeCapture() { } int FfmpegCamera::PreCapture() { + Debug(1, "PreCapture"); // If Reopen was called, then ffmpeg is closed and we need to reopen it. if ( ! mCanCapture ) return OpenFfmpeg(); @@ -177,7 +181,7 @@ int FfmpegCamera::PostCapture() { int FfmpegCamera::OpenFfmpeg() { - Debug ( 2, "OpenFfmpeg called." ); + Debug(2, "OpenFfmpeg called."); int ret; mOpenStart = time(NULL); @@ -221,7 +225,7 @@ int FfmpegCamera::OpenFfmpeg() { #endif { mIsOpening = false; - Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); + Error("Unable to open input %s due to: %s", mPath.c_str(), strerror(errno)); return -1; } @@ -235,16 +239,16 @@ int FfmpegCamera::OpenFfmpeg() { mIsOpening = false; Debug ( 1, "Opened input" ); - Info( "Stream open %s", mPath.c_str() ); + Info( "Stream open %s, parsing streams...", mPath.c_str() ); #if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) if ( av_find_stream_info( mFormatContext ) < 0 ) #else if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) #endif - Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); + Fatal("Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno)); - Debug ( 1, "Got stream info" ); + Debug(4, "Got stream info"); // Find first video stream present // The one we want Might not be the first @@ -289,11 +293,11 @@ int FfmpegCamera::OpenFfmpeg() { if ( mAudioStreamId == -1 ) Debug( 3, "Unable to locate audio stream in %s", mPath.c_str() ); - Debug ( 3, "Found video stream at index %d", mVideoStreamId ); - Debug ( 3, "Found audio stream at index %d", mAudioStreamId ); + Debug(3, "Found video stream at index %d", mVideoStreamId); + Debug(3, "Found audio stream at index %d", mAudioStreamId); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - mVideoCodecContext = avcodec_alloc_context3( NULL ); + mVideoCodecContext = avcodec_alloc_context3(NULL); avcodec_parameters_to_context( mVideoCodecContext, mFormatContext->streams[mVideoStreamId]->codecpar ); #else mVideoCodecContext = mFormatContext->streams[mVideoStreamId]->codec; @@ -345,7 +349,8 @@ int FfmpegCamera::OpenFfmpeg() { if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) { // Try and get the codec from the codec context - Fatal("Can't find codec for video stream from %s", mPath.c_str()); + Error("Can't find codec for video stream from %s", mPath.c_str()); + return -1; } else { Debug(1, "Video Found decoder"); zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0); @@ -388,7 +393,7 @@ int FfmpegCamera::OpenFfmpeg() { } else { Debug(1, "Audio Found decoder"); zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0); - // Open the codec + // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) Debug ( 1, "Calling avcodec_open" ); if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index 4902c6524..1a09893c7 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -99,6 +99,7 @@ void VideoStream::SetupFormat( ) { } #endif } else { + Debug(1,"No allocating priv_data"); s->priv_data = NULL; } @@ -120,7 +121,6 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei /* ffmpeg format matching */ switch ( colours ) { case ZM_COLOUR_RGB24: - { if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) { /* BGR subpixel order */ pf = AV_PIX_FMT_BGR24; @@ -129,9 +129,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei pf = AV_PIX_FMT_RGB24; } break; - } case ZM_COLOUR_RGB32: - { if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB subpixel order */ pf = AV_PIX_FMT_ARGB; @@ -146,7 +144,6 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei pf = AV_PIX_FMT_RGBA; } break; - } case ZM_COLOUR_GRAY8: pf = AV_PIX_FMT_GRAY8; break; @@ -159,6 +156,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei // RTP must have a packet_size. // Not sure what this value should be really... ofc->packet_size = width*height; + Debug(1,"Setting packet_size to %d", ofc->packet_size); if ( of->video_codec == AV_CODEC_ID_NONE ) { // RTP does not have a default codec in ffmpeg <= 0.8. @@ -171,6 +169,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei AVCodec *a = avcodec_find_encoder_by_name(codec_name); if ( a ) { codec_id = a->id; + Debug( 1, "Using codec \"%s\"", codec_name ); } else { #if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) ); @@ -209,13 +208,13 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei Fatal( "Could not alloc stream" ); return; } + Debug( 1, "Allocated stream (%d) !=? (%d)", ost->id , ofc->nb_streams - 1 ); ost->id = ofc->nb_streams - 1; - Debug( 1, "Allocated stream" ); - #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - codec_context = avcodec_alloc_context3(NULL); - avcodec_parameters_to_context(codec_context, ost->codecpar); + + codec_context = avcodec_alloc_context3(NULL); + //avcodec_parameters_to_context(codec_context, ost->codecpar); #else codec_context = ost->codec; #endif @@ -223,7 +222,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei codec_context->codec_id = codec->id; codec_context->codec_type = codec->type; - codec_context->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; + codec_context->pix_fmt = strcmp("mjpeg", ofc->oformat->name) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; if ( bitrate <= 100 ) { // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. // This gets rid of artifacts in the beginning of the movie; and well, even quality. @@ -246,8 +245,11 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei identically 1. */ codec_context->time_base.den = frame_rate; codec_context->time_base.num = 1; + ost->time_base.den = frame_rate; + ost->time_base.num = 1; + - Debug( 1, "Will encode in %d fps.", codec_context->time_base.den ); + Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height ); /* emit one intra frame every second */ codec_context->gop_size = frame_rate; @@ -258,6 +260,10 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER; +#endif +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + avcodec_parameters_from_context(ost->codecpar, codec_context); + zm_dump_codecpar(ost->codecpar); #endif } else { Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); @@ -271,7 +277,7 @@ const char *VideoStream::MimeType( ) const { for ( unsigned int i = 0; i < sizeof (mime_data) / sizeof (*mime_data); i++ ) { if ( strcmp( format, mime_data[i].format ) == 0 ) { Debug( 1, "MimeType is \"%s\"", mime_data[i].mime_type ); - return ( mime_data[i].mime_type); + return mime_data[i].mime_type; } } const char *mime_type = of->mime_type; @@ -282,9 +288,9 @@ const char *VideoStream::MimeType( ) const { Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); } - Debug( 1, "MimeType is \"%s\"", mime_type ); + Debug(1, "MimeType is \"%s\"", mime_type ); - return ( mime_type); + return mime_type; } void VideoStream::OpenStream( ) { @@ -293,11 +299,13 @@ void VideoStream::OpenStream( ) { /* now that all the parameters are set, we can open the video codecs and allocate the necessary encode buffers */ if ( ost ) { + Debug(1,"Opening codec"); + /* open the codec */ #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - if ( (ret = avcodec_open( codec_context, codec )) < 0 ) + if ( (ret = avcodec_open(codec_context, codec)) < 0 ) #else - if ( (ret = avcodec_open2( codec_context, codec, 0 )) < 0 ) + if ( (ret = avcodec_open2(codec_context, codec, 0)) < 0 ) #endif { Fatal( "Could not open codec. Error code %d \"%s\"", ret, av_err2str(ret) ); @@ -310,6 +318,9 @@ void VideoStream::OpenStream( ) { if ( !opicture ) { Panic( "Could not allocate opicture" ); } + opicture->width = codec_context->width; + opicture->height = codec_context->height; + opicture->format = codec_context->pix_fmt; #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) int size = av_image_get_buffer_size( codec_context->pix_fmt, codec_context->width, codec_context->height, 1 ); @@ -362,7 +373,7 @@ void VideoStream::OpenStream( ) { tmp_opicture_buf, pf, codec_context->width, codec_context->height ); #endif } - } + } // end if ost /* open the output file, if needed */ if ( !(of->flags & AVFMT_NOFILE) ) { @@ -384,8 +395,8 @@ void VideoStream::OpenStream( ) { video_outbuf = NULL; #if LIBAVFORMAT_VERSION_CHECK(57, 0, 0, 0, 0) - if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && - codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) { + if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && + codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) { #else if ( !(of->flags & AVFMT_RAWPICTURE) ) { #endif @@ -408,7 +419,7 @@ void VideoStream::OpenStream( ) { #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) ret = av_write_header( ofc ); #else - ret = avformat_write_header( ofc, NULL ); + ret = avformat_write_header(ofc, NULL); #endif if ( ret < 0 ) { @@ -451,6 +462,7 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi } } + codec_context = NULL; SetupFormat( ); SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); SetParameters( ); @@ -466,7 +478,6 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi Fatal("pthread_mutex_init failed"); } - codec_context = NULL; } VideoStream::~VideoStream( ) { @@ -627,7 +638,6 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, opicture_ptr->quality = codec_context->global_quality; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Put encoder into flushing mode avcodec_send_frame(codec_context, opicture_ptr); int ret = avcodec_receive_packet(codec_context, pkt); if ( ret < 0 ) { diff --git a/src/zmc.cpp b/src/zmc.cpp index 9f3dae3a5..518ec3db6 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -224,7 +224,7 @@ int main(int argc, char *argv[]) { Info("Starting Capture version %s", ZM_VERSION); static char sql[ZM_SQL_SML_BUFSIZ]; for ( int i = 0; i < n_monitors; i ++ ) { - snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (Id, Status ) VALUES ('%d','Running')", monitors[i]->Id() ); + snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", monitors[i]->Id() ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); } @@ -239,128 +239,144 @@ int main(int argc, char *argv[]) { sigaddset(&block_set, SIGUSR1); sigaddset(&block_set, SIGUSR2); - monitors[0]->setStartupTime((time_t)time(NULL)); - if ( monitors[0]->PrimeCapture() < 0 ) { - Error("Failed to prime capture of initial monitor"); - exit(-1); - } - - AnalysisThread **analysis_threads = new AnalysisThread *[n_monitors]; - long *capture_delays = new long[n_monitors]; - long *alarm_capture_delays = new long[n_monitors]; - long *next_delays = new long[n_monitors]; - struct timeval * last_capture_times = new struct timeval[n_monitors]; - for ( int i = 0; i < n_monitors; i++ ) { - last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; - capture_delays[i] = monitors[i]->GetCaptureDelay(); - alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); - Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)", capture_delays[i], alarm_capture_delays[i] ); - - Monitor::Function function = monitors[0]->GetFunction(); - if ( function == Monitor::MODECT || function == Monitor::MOCORD || function == Monitor::RECORD) { - Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id()); - analysis_threads[i] = new AnalysisThread(monitors[i]); - analysis_threads[i]->start(); - } else { - analysis_threads[i] = NULL; - } - } // end foreach monitor - int result = 0; - struct timeval now; - struct DeltaTimeval delta_time; - while ( !zm_terminate ) { - //Debug(2,"blocking"); - sigprocmask(SIG_BLOCK, &block_set, 0); + while( ! zm_terminate ) { + for ( int i = 0; i < n_monitors; i ++ ) { + time_t now = (time_t)time(NULL); + monitors[i]->setStartupTime(now); + } + // Outer primary loop, handles connection to camera + if ( monitors[0]->PrimeCapture() < 0 ) { + Error("Failed to prime capture of initial monitor"); + sleep(10); + continue; + } + static char sql[ZM_SQL_SML_BUFSIZ]; + for ( int i = 0; i < n_monitors; i ++ ) { + snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", monitors[i]->Id() ); + if ( mysql_query( &dbconn, sql ) ) { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + } + } + + AnalysisThread **analysis_threads = new AnalysisThread *[n_monitors]; + long *capture_delays = new long[n_monitors]; + long *alarm_capture_delays = new long[n_monitors]; + long *next_delays = new long[n_monitors]; + struct timeval * last_capture_times = new struct timeval[n_monitors]; for ( int i = 0; i < n_monitors; i++ ) { - long min_delay = MAXINT; + last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; + capture_delays[i] = monitors[i]->GetCaptureDelay(); + alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); + Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)", capture_delays[i], alarm_capture_delays[i] ); - gettimeofday(&now, NULL); - for ( int j = 0; j < n_monitors; j++ ) { - if ( last_capture_times[j].tv_sec ) { - // We pretty much know this is positive. - DELTA_TIMEVAL(delta_time, now, last_capture_times[j], DT_PREC_3); - // capture_delay is the amount of time we should sleep to achieve the desired framerate. - if ( monitors[i]->GetState() == Monitor::ALARM ) - next_delays[j] = alarm_capture_delays[j] - delta_time.delta; - else - next_delays[j] = capture_delays[j] - delta_time.delta; - if ( next_delays[j] < 0 ) - next_delays[j] = 0; - } else { - next_delays[j] = 0; - } - if ( next_delays[j] <= min_delay ) { - min_delay = next_delays[j]; - } + Monitor::Function function = monitors[0]->GetFunction(); + if ( function == Monitor::MODECT || function == Monitor::MOCORD || function == Monitor::RECORD) { + Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id()); + analysis_threads[i] = new AnalysisThread(monitors[i]); + analysis_threads[i]->start(); + } else { + analysis_threads[i] = NULL; } + } // end foreach monitor - monitors[i]->CheckAction(); - - if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) { - if ( monitors[i]->PreCapture() < 0 ) { - Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - zm_terminate = true; - result = -1; - break; - } - if ( monitors[i]->Capture() < 0 ) { - Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - result = -1; - break; - } - if ( monitors[i]->PostCapture() < 0 ) { - Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); - zm_terminate = true; - result = -1; - break; - } - - if ( next_delays[i] > 0 ) { - gettimeofday(&now, NULL); - DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3); - long sleep_time = next_delays[i] - delta_time.delta; - if ( sleep_time > 0 ) { - Debug(2,"usleeping (%d)", sleep_time*(DT_MAXGRAN/DT_PREC_3) ); - usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3)); - } - last_capture_times[i] = now; - } else { - gettimeofday(&(last_capture_times[i]), NULL); - } - } // end if next_delay <= min_delay || next_delays[i] <= 0 ) - - } // end foreach n_monitors - //Debug(2,"unblocking"); - sigprocmask(SIG_UNBLOCK, &block_set, 0); - if ( zm_reload ) { + struct timeval now; + struct DeltaTimeval delta_time; + while ( !zm_terminate ) { + //Debug(2,"blocking"); + sigprocmask(SIG_BLOCK, &block_set, 0); for ( int i = 0; i < n_monitors; i++ ) { - monitors[i]->Reload(); + long min_delay = MAXINT; + + gettimeofday(&now, NULL); + for ( int j = 0; j < n_monitors; j++ ) { + if ( last_capture_times[j].tv_sec ) { + // We pretty much know this is positive. + DELTA_TIMEVAL(delta_time, now, last_capture_times[j], DT_PREC_3); + // capture_delay is the amount of time we should sleep to achieve the desired framerate. + if ( monitors[i]->GetState() == Monitor::ALARM ) + next_delays[j] = alarm_capture_delays[j] - delta_time.delta; + else + next_delays[j] = capture_delays[j] - delta_time.delta; + if ( next_delays[j] < 0 ) + next_delays[j] = 0; + } + if ( next_delays[j] <= min_delay ) { + min_delay = next_delays[j]; + } + } // end foreach monitor + + monitors[i]->CheckAction(); + + if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) { + if ( monitors[i]->PreCapture() < 0 ) { + Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); + result = -1; + break; + } + if ( monitors[i]->Capture() < 0 ) { + Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); + result = -1; + break; + } + if ( monitors[i]->PostCapture() < 0 ) { + Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); + result = -1; + break; + } + + if ( next_delays[i] > 0 ) { + gettimeofday(&now, NULL); + DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3); + long sleep_time = next_delays[i] - delta_time.delta; + if ( sleep_time > 0 ) { + Debug(2,"usleeping (%d)", sleep_time*(DT_MAXGRAN/DT_PREC_3) ); + usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3)); + } + last_capture_times[i] = now; + } else { + gettimeofday(&(last_capture_times[i]), NULL); + } + } // end if next_delay <= min_delay || next_delays[i] <= 0 ) + + } // end foreach n_monitors + //Debug(2,"unblocking"); + sigprocmask(SIG_UNBLOCK, &block_set, 0); + if ( zm_reload ) { + for ( int i = 0; i < n_monitors; i++ ) { + monitors[i]->Reload(); + } + logTerm(); + logInit( log_id_string ); + zm_reload = false; + if ( result < 0 ) { + // Failure, try reconnecting + break; + } + } // end if zm_reload + } // end while ! zm_terminate and connected + + delete [] alarm_capture_delays; + delete [] capture_delays; + delete [] next_delays; + delete [] last_capture_times; + + // Killoff the analysis threads. Don't need them spinning while we try to reconnect + for ( int i = 0; i < n_monitors; i++ ) { + if ( analysis_threads[i] ) { + analysis_threads[i]->stop(); + analysis_threads[i]->join(); + delete analysis_threads[i]; + analysis_threads[i] = 0; } - logTerm(); - logInit( log_id_string ); - zm_reload = false; - } - } // end while ! zm_terminate - for ( int i = 0; i < n_monitors; i++ ) { - if ( analysis_threads[i] ) { - analysis_threads[i]->stop(); - analysis_threads[i]->join(); - delete analysis_threads[i]; - analysis_threads[i] = 0; - } - delete monitors[i]; - } - delete [] analysis_threads; + } // end foreach monitor + delete [] analysis_threads; + } // end while ! zm_terminate outer connection loop delete [] monitors; - delete [] alarm_capture_delays; - delete [] capture_delays; - delete [] next_delays; - delete [] last_capture_times; Image::Deinitialise(); logTerm(); zmDbClose(); - return( result ); + return result; } diff --git a/version b/version index 2ec5fa98f..e9c3366c8 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.29 +1.31.30 diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 5dbfae6eb..57e138254 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -3,19 +3,19 @@ $start_time = time(); -define( 'MSG_TIMEOUT', ZM_WEB_AJAX_TIMEOUT ); +define( 'MSG_TIMEOUT', ZM_WEB_AJAX_TIMEOUT/2 ); define( 'MSG_DATA_SIZE', 4+256 ); if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) { ajaxError( "Unexpected received message type '$type'" ); } -if ( !($socket = @socket_create( AF_UNIX, SOCK_DGRAM, 0 )) ) { - ajaxError( 'socket_create() failed: '.socket_strerror(socket_last_error()) ); -} $key = ftok(ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'w.lock', 'Z'); $semaphore = sem_get($key,1); if ( sem_acquire($semaphore,1) !== false ) { + if ( !($socket = @socket_create( AF_UNIX, SOCK_DGRAM, 0 )) ) { + ajaxError( 'socket_create() failed: '.socket_strerror(socket_last_error()) ); + } $localSocketFile = ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'w.sock'; if ( file_exists( $localSocketFile ) ) { @@ -59,9 +59,12 @@ if ( sem_acquire($semaphore,1) !== false ) { $remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock'; $max_socket_tries = 10; // FIXME This should not exceed web_ajax_timeout - while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second. + while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second. + // WHY? We will just send another one... + // ANSWER: Because otherwise we get a log of errors logged + //Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' ); - usleep(200000); + usleep(1000); } if ( !file_exists($remSockFile) ) { diff --git a/web/api/app/Plugin/CakePHP-Enum-Behavior b/web/api/app/Plugin/CakePHP-Enum-Behavior index 7108489f2..ca91b87fd 160000 --- a/web/api/app/Plugin/CakePHP-Enum-Behavior +++ b/web/api/app/Plugin/CakePHP-Enum-Behavior @@ -1 +1 @@ -Subproject commit 7108489f218c54d36d235d3af91d6da2f8311237 +Subproject commit ca91b87fda8e006e4fca2ed870f24f9a29c2905d diff --git a/web/includes/functions.php b/web/includes/functions.php index 73c2cd447..7ef1b054c 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -269,14 +269,14 @@ function getVideoStreamHTML( $id, $src, $width, $height, $format, $title='' ) { if ( isWindows() ) { return ' @@ -309,13 +309,13 @@ function getVideoStreamHTML( $id, $src, $width, $height, $format, $title='' ) { { return ' - - /> + />--> + diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 73d0b132b..152fdcd9b 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -56,64 +56,66 @@ function Monitor( monitorData ) { var stream = $j('#liveStream'+this.id)[0]; if ( respObj.result == 'Ok' ) { - this.status = respObj.status; - this.alarmState = this.status.state; + if ( respObj.status ) { + this.status = respObj.status; + this.alarmState = this.status.state; - var stateClass = ""; - if ( this.alarmState == STATE_ALARM ) - stateClass = "alarm"; - else if ( this.alarmState == STATE_ALERT ) - stateClass = "alert"; - else - stateClass = "idle"; + var stateClass = ""; + if ( this.alarmState == STATE_ALARM ) + stateClass = "alarm"; + else if ( this.alarmState == STATE_ALERT ) + stateClass = "alert"; + else + stateClass = "idle"; - if ( !COMPACT_MONTAGE ) { - $('fpsValue'+this.id).set( 'text', this.status.fps ); - $('stateValue'+this.id).set( 'text', stateStrings[this.alarmState] ); - this.setStateClass( $('monitorState'+this.id), stateClass ); - } - this.setStateClass( $('monitor'+this.id), stateClass ); + if ( !COMPACT_MONTAGE ) { + $('fpsValue'+this.id).set( 'text', this.status.fps ); + $('stateValue'+this.id).set( 'text', stateStrings[this.alarmState] ); + this.setStateClass( $('monitorState'+this.id), stateClass ); + } + this.setStateClass( $('monitor'+this.id), stateClass ); - /*Stream could be an applet so can't use moo tools*/ - stream.className = stateClass; + /*Stream could be an applet so can't use moo tools*/ + stream.className = stateClass; - var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ); - var wasAlarmed = ( this.lastAlarmState == STATE_ALARM || this.lastAlarmState == STATE_ALERT ); + var isAlarmed = ( this.alarmState == STATE_ALARM || this.alarmState == STATE_ALERT ); + var wasAlarmed = ( this.lastAlarmState == STATE_ALARM || this.lastAlarmState == STATE_ALERT ); - var newAlarm = ( isAlarmed && !wasAlarmed ); - var oldAlarm = ( !isAlarmed && wasAlarmed ); + var newAlarm = ( isAlarmed && !wasAlarmed ); + var oldAlarm = ( !isAlarmed && wasAlarmed ); - if ( newAlarm ) { + if ( newAlarm ) { + if ( false && SOUND_ON_ALARM ) { + // Enable the alarm sound + $('alarmSound').removeClass( 'hidden' ); + } + if ( POPUP_ON_ALARM ) { + windowToFront(); + } + } if ( false && SOUND_ON_ALARM ) { - // Enable the alarm sound - $('alarmSound').removeClass( 'hidden' ); + if ( oldAlarm ) { + // Disable alarm sound + $('alarmSound').addClass( 'hidden' ); + } } - if ( POPUP_ON_ALARM ) { - windowToFront(); - } - } - if ( false && SOUND_ON_ALARM ) { - if ( oldAlarm ) { - // Disable alarm sound - $('alarmSound').addClass( 'hidden' ); - } - } - if ( this.status.auth ) { - if ( this.status.auth != auth_hash ) { - // Try to reload the image stream. - if ( stream ) - stream.src = stream.src.replace( /auth=\w+/i, 'auth='+this.status.auth ); - console.log("Changed auth from " + auth_hash + " to " + this.status.auth ); - auth_hash = this.status.auth; - } - } // end if have a new auth hash + if ( this.status.auth ) { + if ( this.status.auth != auth_hash ) { + // Try to reload the image stream. + if ( stream ) + stream.src = stream.src.replace( /auth=\w+/i, 'auth='+this.status.auth ); + console.log("Changed auth from " + auth_hash + " to " + this.status.auth ); + auth_hash = this.status.auth; + } + } // end if have a new auth hash + } // end if has state } else { console.error( respObj.message ); // Try to reload the image stream. if ( stream ) { if ( stream.src ) { - console.log('Reloading stream: ' + stream.src ); - stream.src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); + console.log('Reloading stream: ' + stream.src ); + stream.src = stream.src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); } else { } } else {