Merge branch 'master' of github.com:ZoneMinder/zoneminder

This commit is contained in:
Isaac Connor 2021-12-10 16:20:54 -05:00
commit b4511a8fc3
52 changed files with 1218 additions and 677 deletions

View File

@ -167,6 +167,8 @@ set(ZM_NO_X10 "OFF" CACHE BOOL
set(ZM_ONVIF "ON" CACHE BOOL set(ZM_ONVIF "ON" CACHE BOOL
"Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not "Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
work with all cameras claiming to be ONVIF compliant. default: ON") work with all cameras claiming to be ONVIF compliant. default: ON")
set(ZM_NO_PCRE "OFF" CACHE BOOL
"Set to ON to skip libpcre3 checks and force building ZM without libpcre3. default: OFF")
set(ZM_NO_RTSPSERVER "OFF" CACHE BOOL set(ZM_NO_RTSPSERVER "OFF" CACHE BOOL
"Set to ON to skip building ZM with rtsp server support. default: OFF") "Set to ON to skip building ZM with rtsp server support. default: OFF")
set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING
@ -407,9 +409,11 @@ else()
message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system") message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system")
endif() endif()
# pcre (using find_library and find_path) # Do not check for cURL if ZM_NO_CURL is on
find_library(PCRE_LIBRARIES pcre) if(NOT ZM_NO_PRCE)
if(PCRE_LIBRARIES) # pcre (using find_library and find_path)
find_library(PCRE_LIBRARIES pcre)
if(PCRE_LIBRARIES)
set(HAVE_LIBPCRE 1) set(HAVE_LIBPCRE 1)
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}") list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
find_path(PCRE_INCLUDE_DIR pcre.h) find_path(PCRE_INCLUDE_DIR pcre.h)
@ -420,8 +424,9 @@ if(PCRE_LIBRARIES)
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR) mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
check_include_file("pcre.h" HAVE_PCRE_H) check_include_file("pcre.h" HAVE_PCRE_H)
set(optlibsfound "${optlibsfound} PCRE") set(optlibsfound "${optlibsfound} PCRE")
else() else()
set(optlibsnotfound "${optlibsnotfound} PCRE") set(optlibsnotfound "${optlibsnotfound} PCRE")
endif()
endif() endif()
# mysqlclient (using find_library and find_path) # mysqlclient (using find_library and find_path)
@ -540,6 +545,7 @@ set(ZM_PCRE 0)
if(HAVE_LIBPCRE AND HAVE_PCRE_H) if(HAVE_LIBPCRE AND HAVE_PCRE_H)
set(ZM_PCRE 1) set(ZM_PCRE 1)
endif() endif()
# Check for mmap and enable in all components # Check for mmap and enable in all components
set(ZM_MEM_MAPPED 0) set(ZM_MEM_MAPPED 0)
set(ENABLE_MMAP no) set(ENABLE_MMAP no)

100
cmake/Modules/FindFmt.cmake Normal file
View File

@ -0,0 +1,100 @@
# FindFmt
# -------
# Finds the Fmt library
#
# This will define the following variables::
#
# FMT_FOUND - system has Fmt
# FMT_INCLUDE_DIRS - the Fmt include directory
# FMT_LIBRARIES - the Fmt libraries
#
# and the following imported targets::
#
# Fmt::Fmt - The Fmt library
if(ENABLE_INTERNAL_FMT)
include(ExternalProject)
file(STRINGS ${CMAKE_SOURCE_DIR}/tools/depends/target/libfmt/Makefile VER REGEX "^[ ]*VERSION[ ]*=.+$")
string(REGEX REPLACE "^[ ]*VERSION[ ]*=[ ]*" "" FMT_VERSION "${VER}")
# allow user to override the download URL with a local tarball
# needed for offline build envs
if(FMT_URL)
get_filename_component(FMT_URL "${FMT_URL}" ABSOLUTE)
else()
set(FMT_URL http://mirrors.kodi.tv/build-deps/sources/fmt-${FMT_VERSION}.tar.gz)
endif()
if(VERBOSE)
message(STATUS "FMT_URL: ${FMT_URL}")
endif()
if(APPLE)
set(EXTRA_ARGS "-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}")
endif()
set(FMT_LIBRARY ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/lib/libfmt.a)
set(FMT_INCLUDE_DIR ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/include)
externalproject_add(fmt
URL ${FMT_URL}
DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/download
PREFIX ${CORE_BUILD_DIR}/fmt
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_INSTALL_LIBDIR=lib
-DFMT_DOC=OFF
-DFMT_TEST=OFF
"${EXTRA_ARGS}"
BUILD_BYPRODUCTS ${FMT_LIBRARY})
set_target_properties(fmt PROPERTIES FOLDER "External Projects")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Fmt
REQUIRED_VARS FMT_LIBRARY FMT_INCLUDE_DIR
VERSION_VAR FMT_VERSION)
set(FMT_LIBRARIES ${FMT_LIBRARY})
set(FMT_INCLUDE_DIRS ${FMT_INCLUDE_DIR})
else()
find_package(FMT 6.1.2 CONFIG REQUIRED QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_FMT libfmt QUIET)
if(PC_FMT_VERSION AND NOT FMT_VERSION)
set(FMT_VERSION ${PC_FMT_VERSION})
endif()
endif()
find_path(FMT_INCLUDE_DIR NAMES fmt/format.h
PATHS ${PC_FMT_INCLUDEDIR})
find_library(FMT_LIBRARY_RELEASE NAMES fmt
PATHS ${PC_FMT_LIBDIR})
find_library(FMT_LIBRARY_DEBUG NAMES fmtd
PATHS ${PC_FMT_LIBDIR})
include(SelectLibraryConfigurations)
select_library_configurations(FMT)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Fmt
REQUIRED_VARS FMT_LIBRARY FMT_INCLUDE_DIR FMT_VERSION
VERSION_VAR FMT_VERSION)
if(FMT_FOUND)
set(FMT_LIBRARIES ${FMT_LIBRARY})
set(FMT_INCLUDE_DIRS ${FMT_INCLUDE_DIR})
if(NOT TARGET fmt)
add_library(fmt UNKNOWN IMPORTED)
set_target_properties(fmt PROPERTIES
IMPORTED_LOCATION "${FMT_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${FMT_INCLUDE_DIR}")
endif()
endif()
endif()
mark_as_advanced(FMT_INCLUDE_DIR FMT_LIBRARY)

View File

@ -456,6 +456,8 @@ CREATE TABLE `Monitors` (
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1', `DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
`LinkedMonitors` varchar(255), `LinkedMonitors` varchar(255),
`Triggers` set('X10') NOT NULL default '', `Triggers` set('X10') NOT NULL default '',
`EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '',
`EventEndCommand` VARCHAR(255) NOT NULL DEFAULT '',
`ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '', `ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '',
`ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '', `ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '',
`ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '', `ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '',

31
db/zm_update-1.37.5.sql Normal file
View File

@ -0,0 +1,31 @@
--
-- This update adds EventStartCommand and EventEndCommand
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'EventEndCommand'
) > 0,
"SELECT 'Column EventEndCommand already exists in Monitors'",
"ALTER TABLE `Monitors` ADD COLUMN `EventEndCommand` VARCHAR(255) NOT NULL DEFAULT '' AFTER `Triggers`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'EventStartCommand'
) > 0,
"SELECT 'Column EventStartCommand already exists in Monitors'",
"ALTER TABLE `Monitors` ADD COLUMN `EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '' AFTER `Triggers`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -36,7 +36,7 @@
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.37.3 Version: 1.37.5
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

View File

@ -16,7 +16,6 @@ Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev ,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev
,libturbojpeg0-dev ,libturbojpeg0-dev
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
,libpcre3-dev
,libpolkit-gobject-1-dev ,libpolkit-gobject-1-dev
,libv4l-dev [!hurd-any] ,libv4l-dev [!hurd-any]
,libvlc-dev ,libvlc-dev
@ -70,7 +69,6 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,policykit-1 ,policykit-1
,rsyslog | system-log-daemon ,rsyslog | system-log-daemon
,zip ,zip
,libpcre3
,libcrypt-eksblowfish-perl ,libcrypt-eksblowfish-perl
,libdata-entropy-perl ,libdata-entropy-perl
,libvncclient1|libvncclient0 ,libvncclient1|libvncclient0

View File

@ -19,6 +19,7 @@ override_dh_auto_configure:
-DCMAKE_VERBOSE_MAKEFILE=ON \ -DCMAKE_VERBOSE_MAKEFILE=ON \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DBUILD_MAN=0 \ -DBUILD_MAN=0 \
-DZM_NO_PCRE=ON \
-DZM_CONFIG_DIR="/etc/zm" \ -DZM_CONFIG_DIR="/etc/zm" \
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_RUNDIR="/run/zm" \ -DZM_RUNDIR="/run/zm" \

View File

@ -4,7 +4,7 @@ Debian
.. contents:: .. contents::
Easy Way: Debian 11 (Bullseye) Easy Way: Debian 11 (Bullseye)
------------------------ ------------------------------
This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye). This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye).
@ -104,7 +104,7 @@ Add the following to the /etc/apt/sources.list.d/zoneminder.list file
You can do this using: You can do this using:
.. code-block:: ::
echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list

View File

@ -43,6 +43,7 @@ require Date::Parse;
require POSIX; require POSIX;
use Date::Format qw(time2str); use Date::Format qw(time2str);
use Time::HiRes qw(gettimeofday tv_interval stat); use Time::HiRes qw(gettimeofday tv_interval stat);
use Scalar::Util qw(looks_like_number);
#our @ISA = qw(ZoneMinder::Object); #our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object);
@ -601,7 +602,7 @@ sub CopyTo {
# First determine if we can move it to the dest. # First determine if we can move it to the dest.
# We do this before bothering to lock the event # We do this before bothering to lock the event
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) { if ( ! looks_like_number($$NewStorage{Id}) ) {
return 'New storage does not have an id. Moving will not happen.'; return 'New storage does not have an id. Moving will not happen.';
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) { } elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
return 'Event is already located at ' . $NewPath; return 'Event is already located at ' . $NewPath;
@ -733,19 +734,22 @@ sub MoveTo {
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit}; my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
$ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction; $ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction;
$self->lock_and_load(); # The fact that we are in a transaction might not imply locking if (!$self->lock_and_load()) {
Warning('Unable to lock event record '.$$self{Id}); # The fact that we are in a transaction might not imply locking
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
return 'Unable to lock event record';
}
my $OldStorage = $self->Storage(undef); my $OldStorage = $self->Storage(undef);
my $error = $self->CopyTo($NewStorage); my $error = $self->CopyTo($NewStorage);
return $error if $error; if (!$error) {
# Succeeded in copying all files, so we may now update the Event. # Succeeded in copying all files, so we may now update the Event.
$$self{StorageId} = $$NewStorage{Id}; $$self{StorageId} = $$NewStorage{Id};
$self->Storage($NewStorage); $self->Storage($NewStorage);
$error .= $self->save(); $error .= $self->save();
# Going to leave it to upper layer as to whether we rollback or not # Going to leave it to upper layer as to whether we rollback or not
}
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction; $ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
return $error if $error; return $error if $error;

View File

@ -230,8 +230,8 @@ sub Sql {
# PostCondition, so no further SQL # PostCondition, so no further SQL
} else { } else {
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { # Empty value will result in () from split
foreach my $temp_value ( $stripped_value ? split( /["'\s]*?,["'\s]*?/, $stripped_value ) : $stripped_value ) {
if ( $term->{attr} eq 'AlarmedZoneId' ) { if ( $term->{attr} eq 'AlarmedZoneId' ) {
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')'; $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
} elsif ( $term->{attr} =~ /^MonitorName/ ) { } elsif ( $term->{attr} =~ /^MonitorName/ ) {
@ -250,7 +250,8 @@ sub Sql {
$$self{Server} = new ZoneMinder::Server($temp_value); $$self{Server} = new ZoneMinder::Server($temp_value);
} }
} elsif ( $term->{attr} eq 'StorageId' ) { } elsif ( $term->{attr} eq 'StorageId' ) {
$value = "'$temp_value'"; # Empty means NULL, otherwise must be an integer
$value = $temp_value ne '' ? int($temp_value) : 'NULL';
$$self{Storage} = new ZoneMinder::Storage($temp_value); $$self{Storage} = new ZoneMinder::Storage($temp_value);
} elsif ( $term->{attr} eq 'Name' } elsif ( $term->{attr} eq 'Name'
|| $term->{attr} eq 'Cause' || $term->{attr} eq 'Cause'

View File

@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# will save memory. # will save memory.
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (
constants => [ qw( constants => [ qw(
STATE_UNKNOWN
STATE_IDLE STATE_IDLE
STATE_PREALARM STATE_PREALARM
STATE_ALARM STATE_ALARM
@ -98,11 +99,12 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use constant STATE_IDLE => 0; use constant STATE_UNKNOWN => 0;
use constant STATE_PREALARM => 1; use constant STATE_IDLE => 1;
use constant STATE_ALARM => 2; use constant STATE_PREALARM => 2;
use constant STATE_ALERT => 3; use constant STATE_ALARM => 3;
use constant STATE_TAPE => 4; use constant STATE_ALERT => 4;
use constant STATE_TAPE => 5;
use constant ACTION_GET => 1; use constant ACTION_GET => 1;
use constant ACTION_SET => 2; use constant ACTION_SET => 2;

View File

@ -326,17 +326,23 @@ sub resumeMotionDetection {
sub Control { sub Control {
my $self = shift; my $self = shift;
if ( ! exists $$self{Control}) { if (!exists $$self{Control}) {
if ($$self{ControlId}) { if ($$self{ControlId}) {
require ZoneMinder::Control; require ZoneMinder::Control;
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId}); my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
if ($Control) { if ($Control) {
my $Protocol = $$Control{Protocol};
if (!$Protocol) {
Error("No protocol set in control $$Control{Id}, trying Name $$Control{Name}");
$Protocol = $$Control{Name};
}
require Module::Load::Conditional; require Module::Load::Conditional;
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$$Control{Protocol} => undef})) { if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$Protocol => undef})) {
Error("Can't load ZoneMinder::Control::$$Control{Protocol}\n$Module::Load::Conditional::ERROR"); Error("Can't load ZoneMinder::Control::$Protocol\n$Module::Load::Conditional::ERROR");
return undef; return undef;
} }
bless $Control, 'ZoneMinder::Control::'.$$Control{Protocol}; bless $Control, 'ZoneMinder::Control::'.$Protocol;
$$Control{MonitorId} = $$self{Id}; $$Control{MonitorId} = $$self{Id};
$$self{Control} = $Control; $$self{Control} = $Control;
} else { } else {

View File

@ -429,10 +429,20 @@ sub start {
# It's not running, or at least it's not been started by us # It's not running, or at least it's not been started by us
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef }; $process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) { } elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
if ($process->{term_sent_at}) {
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' was told to term at "
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{term_sent_at}))
.", pid = $process->{pid}\n"
);
$process->{keepalive} = !undef;
$process->{delay} = 0;
delete $terminating_processes{$command};
} else {
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at " dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started})) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}\n" .", pid = $process->{pid}\n"
); );
}
return; return;
} }
@ -523,7 +533,7 @@ sub send_stop {
."\n" ."\n"
); );
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
return(); return ();
} }
my $pid = $process->{pid}; my $pid = $process->{pid};
@ -586,7 +596,7 @@ sub check_for_processes_to_kill {
sub stop { sub stop {
my ( $daemon, @args ) = @_; my ( $daemon, @args ) = @_;
my $command = join(' ', $daemon, @args ); my $command = join(' ', $daemon, @args);
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( !$process ) { if ( !$process ) {
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'"); dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'");

View File

@ -21,28 +21,6 @@
# #
# ========================================================================== # ==========================================================================
=head1 NAME
zmfilter.pl - ZoneMinder tool to filter events
=head1 SYNOPSIS
zmfilter.pl [-f <filter name>,--filter=<filter name>] [--filter_id=<filter id>] | -v, --version
=head1 DESCRIPTION
This script continuously monitors the recorded events for the given
monitor and applies any filters which would delete and/or upload
matching events.
=head1 OPTIONS
-f{filter name}, --filter={filter name} - The name of a specific filter to run
--filter_id={filter id} - The id of a specific filter to run
-v, --version - Print ZoneMinder version
=cut
use strict; use strict;
use bytes; use bytes;
@ -163,7 +141,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL}; my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $event_id = 0; my $event_id = 0;
if ( !EVENT_PATH ) { if (!EVENT_PATH) {
Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS}); Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS});
die; die;
} }
@ -195,22 +173,22 @@ if ( ! ( $filter_name or $filter_id ) ) {
my @filters; my @filters;
my $last_action = 0; 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 });
} }
foreach my $filter ( @filters ) { foreach my $filter (@filters) {
last if $zm_terminate; last if $zm_terminate;
if ( $$filter{Concurrent} and ! ( $filter_id or $filter_name ) ) { if ($$filter{Concurrent} and !($filter_id or $filter_name)) {
my ( $proc ) = $0 =~ /(\S+)/; my ( $proc ) = $0 =~ /(\S+)/;
my ( $id ) = $$filter{Id} =~ /(\d+)/; my ( $id ) = $$filter{Id} =~ /(\d+)/;
Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}"); Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}");
system(qq`$proc --filter "$$filter{Name}" &`); system(qq`$proc --filter_id $id &`);
} else { } else {
checkFilter($filter); checkFilter($filter);
} }
@ -1051,9 +1029,7 @@ sub executeCommand {
my $filter = shift; my $filter = shift;
my $Event = shift; my $Event = shift;
my $event_path = $Event->Path(); my $command = $filter->{AutoExecuteCmd}.' '.$Event->Path();
my $command = $filter->{AutoExecuteCmd}.' '.$event_path;
$command = substituteTags($command, $filter, $Event); $command = substituteTags($command, $filter, $Event);
Info("Executing '$command'"); Info("Executing '$command'");
@ -1063,15 +1039,37 @@ sub executeCommand {
chomp($output); chomp($output);
Debug("Output: $output"); Debug("Output: $output");
} }
if ( $status ) { if ($status) {
Error("Command '$command' exited with status: $status"); Error("Command '$command' exited with status: $status");
return 0; return 0;
} else { } else {
my $sql = 'UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?'; zmSQLExecute('UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?', $Event->{Id});
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $Event->{Id} )
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
} }
return( 1 ); return 1;
} }
1;
__END__
=head1 NAME
zmfilter.pl - ZoneMinder tool to select events and perform actions on them
=head1 SYNOPSIS
zmfilter.pl [-f <filter name>,--filter=<filter name>] [--filter_id=<filter id>] [--daemon] | -v, --version
=head1 DESCRIPTION
This script performs a specified database query to select recorded events and performs specified actions on them
such as email reporting, deleting, moving, etc. If the --daemon option is given it will remain resident, repeating
the query and applying actions. This is normally managed by zmdc.pl however it can be used manually as well.
=head1 OPTIONS
-f{filter name}, --filter={filter name} - The name of a specific filter to run
--filter_id={filter id} - The id of a specific filter to run
--daemon - Causes zmfilter.pl to stay running endlessly repeating the filter(s).
-v, --version - Print ZoneMinder version
=cut

View File

@ -166,13 +166,9 @@ while (!$zm_terminate) {
foreach my $connection ( values(%spawned_connections) ) { foreach my $connection ( values(%spawned_connections) ) {
if ( vec($rout, $connection->fileno(), 1) ) { if ( vec($rout, $connection->fileno(), 1) ) {
Debug('Got input from spawned connection ' Debug('Got input from spawned connection '
.$connection->name() .$connection->name().' ('.$connection->fileno().')');
.' ('
.$connection->fileno()
.')'
);
my $messages = $connection->getMessages(); my $messages = $connection->getMessages();
if ( defined($messages) ) { if (defined($messages)) {
foreach my $message ( @$messages ) { foreach my $message ( @$messages ) {
handleMessage($connection, $message); handleMessage($connection, $message);
} }
@ -199,34 +195,32 @@ while (!$zm_terminate) {
# Check polled connections # Check polled connections
foreach my $connection ( @in_poll_connections ) { foreach my $connection ( @in_poll_connections ) {
my $messages = $connection->getMessages(); my $messages = $connection->getMessages();
if ( defined($messages) ) { if (defined($messages)) {
foreach my $message ( @$messages ) { foreach my $message (@$messages) handleMessage($connection, $message);
handleMessage($connection, $message);
}
} }
} }
# Check for alarms that might have happened # Check for alarms that might have happened
my @out_messages; my @out_messages;
foreach my $monitor ( values %monitors ) { foreach my $monitor ( values %monitors ) {
if ($$monitor{Function} eq 'None') {
$monitor_reload_time = 0;
next;
}
if ( ! zmMemVerify($monitor) ) { if (!zmMemVerify($monitor)) {
# Our attempt to verify the memory handle failed. We should reload the monitors. # Our attempt to verify the memory handle failed. We should reload the monitors.
# Don't need to zmMemInvalidate because the monitor reload will do it. # Don't need to zmMemInvalidate because the monitor reload will do it.
push @needsReload, $monitor; push @needsReload, $monitor;
next; next;
} }
my ( $state, $last_event ) = zmMemRead( $monitor, my ($state, $last_event) = zmMemRead($monitor, [
[
'shared_data:state', 'shared_data:state',
'shared_data:last_event' 'shared_data:last_event'
] ]);
);
#print( "$monitor->{Id}: S:$state, LE:$last_event" ); if ($state == STATE_ALARM or $state == STATE_ALERT) {
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
# In alarm state # In alarm state
if ( !defined($monitor->{LastEvent}) if ( !defined($monitor->{LastEvent})
or ($last_event != $monitor->{LastEvent}) or ($last_event != $monitor->{LastEvent})

View File

@ -251,11 +251,11 @@ void zmDbQueue::process() {
mCondition.wait(lock); mCondition.wait(lock);
} }
while (!mQueue.empty()) { while (!mQueue.empty()) {
if (mQueue.size() > 10) { if (mQueue.size() > 20) {
Logger *log = Logger::fetch(); Logger *log = Logger::fetch();
Logger::Level db_level = log->databaseLevel(); Logger::Level db_level = log->databaseLevel();
log->databaseLevel(Logger::NOLOG); log->databaseLevel(Logger::NOLOG);
Warning("db queue size has grown larger %zu than 10 entries", mQueue.size()); Warning("db queue size has grown larger %zu than 20 entries", mQueue.size());
log->databaseLevel(db_level); log->databaseLevel(db_level);
} }
std::string sql = mQueue.front(); std::string sql = mQueue.front();
@ -271,8 +271,10 @@ void zmDbQueue::process() {
void zmDbQueue::push(std::string &&sql) { void zmDbQueue::push(std::string &&sql) {
if (mTerminate) return; if (mTerminate) return;
{
std::unique_lock<std::mutex> lock(mMutex); std::unique_lock<std::mutex> lock(mMutex);
mQueue.push(std::move(sql)); mQueue.push(std::move(sql));
}
mCondition.notify_all(); mCondition.notify_all();
} }

View File

@ -546,7 +546,7 @@ void Event::AddFrame(Image *image,
or or
(frame_type == BULK) (frame_type == BULK)
or or
(fps and (frame_data.size() > fps))) { (fps and (frame_data.size() > 5*fps))) {
Debug(1, "Adding %zu frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)", Debug(1, "Adding %zu frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)",
frame_data.size(), write_to_db, fps, (frame_type == BULK)); frame_data.size(), write_to_db, fps, (frame_type == BULK));
WriteDbFrames(); WriteDbFrames();

View File

@ -23,7 +23,7 @@ void bind_libvnc_symbols() {
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL); libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
if (!libvnc_lib) { if (!libvnc_lib) {
Error("Error loading libvncclient: %s", dlerror()); Error("Error loading libvncclient.so: %s", dlerror());
return; return;
} }
@ -135,11 +135,6 @@ VncCamera::VncCamera(
} }
VncCamera::~VncCamera() { VncCamera::~VncCamera() {
if (capture and mRfb) {
if (mRfb->frameBuffer)
free(mRfb->frameBuffer);
(*rfbClientCleanup_f)(mRfb);
}
if (libvnc_lib) { if (libvnc_lib) {
dlclose(libvnc_lib); dlclose(libvnc_lib);
libvnc_lib = nullptr; libvnc_lib = nullptr;
@ -253,6 +248,12 @@ int VncCamera::PostCapture() {
} }
int VncCamera::Close() { int VncCamera::Close() {
if (capture and mRfb) {
if (mRfb->frameBuffer)
free(mRfb->frameBuffer);
(*rfbClientCleanup_f)(mRfb);
mRfb = nullptr;
}
return 1; return 1;
} }
#endif #endif

View File

@ -70,7 +70,7 @@
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended // It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
std::string load_monitor_sql = std::string load_monitor_sql =
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `DecodingEnabled`, " "SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Enabled`, `DecodingEnabled`, "
"`LinkedMonitors`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`," "`LinkedMonitors`, `EventStartCommand`, `EventEndCommand`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings "`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, " "`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, " "`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
@ -435,7 +435,7 @@ Monitor::Monitor()
/* /*
std::string load_monitor_sql = std::string load_monitor_sql =
"SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, DecodingEnabled, LinkedMonitors, " "SELECT Id, Name, ServerId, StorageId, Type, Function+0, Enabled, DecodingEnabled, LinkedMonitors, `EventStartCommand`, `EventEndCommand`, "
"AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS,"
"Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings "Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
"Protocol, Method, Options, User, Pass, Host, Port, Path, SecondPath, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, " "Protocol, Method, Options, User, Pass, Host, Port, Path, SecondPath, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, "
@ -489,6 +489,8 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
// See below after save_jpegs for a recalculation of decoding_enabled // See below after save_jpegs for a recalculation of decoding_enabled
ReloadLinkedMonitors(dbrow[col]); col++; ReloadLinkedMonitors(dbrow[col]); col++;
event_start_command = dbrow[col] ? dbrow[col] : ""; col++;
event_end_command = dbrow[col] ? dbrow[col] : ""; col++;
/* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */ /* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */
analysis_fps_limit = dbrow[col] ? strtod(dbrow[col], nullptr) : 0.0; col++; analysis_fps_limit = dbrow[col] ? strtod(dbrow[col], nullptr) : 0.0; col++;
@ -1873,19 +1875,27 @@ bool Monitor::Analyse() {
if (snap->image) { if (snap->image) {
// decoder may not have been able to provide an image // decoder may not have been able to provide an image
if (!ref_image.Buffer()) { if (!ref_image.Buffer()) {
Debug(1, "Assigning instead of Dectecting"); Debug(1, "Assigning instead of Detecting");
ref_image.Assign(*(snap->image)); ref_image.Assign(*(snap->image));
} else { } else {
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image); Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
// Get new score. // Get new score.
int motion_score = DetectMotion(*(snap->image), zoneSet); int motion_score = DetectMotion(*(snap->image), zoneSet);
// lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause;
snap->zone_stats.reserve(zones.size()); snap->zone_stats.reserve(zones.size());
for (const Zone &zone : zones) { for (const Zone &zone : zones) {
const ZoneStats &stats = zone.GetStats(); const ZoneStats &stats = zone.GetStats();
stats.DumpToLog("After detect motion"); stats.DumpToLog("After detect motion");
snap->zone_stats.push_back(stats); snap->zone_stats.push_back(stats);
if (zone.Alarmed()) {
if (!alarm_cause.empty()) alarm_cause += ",";
alarm_cause += std::string(zone.Label());
} }
}
if (!alarm_cause.empty())
cause = cause+" "+alarm_cause;
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)", Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score); score, last_motion_score, motion_score);
@ -1911,6 +1921,7 @@ bool Monitor::Analyse() {
); );
} // end if active and doing motion detection } // end if active and doing motion detection
if (function == RECORD or function == MOCORD) { if (function == RECORD or function == MOCORD) {
// If doing record, check to see if we need to close the event or not. // If doing record, check to see if we need to close the event or not.
if (event) { if (event) {
@ -1933,69 +1944,9 @@ bool Monitor::Analyse() {
} // end if event } // end if event
if (!event) { if (!event) {
Debug(2, "Creating continuous event"); event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
if (!snap->keyframe and (videowriter == PASSTHROUGH)) {
// Must start on a keyframe so rewind. Only for passthrough though I guess.
// FIXME this iterator is not protected from invalidation
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
*analysis_it, 0 /* pre_event_count */
);
// This gets a lock on the starting packet Info("%s: %03d - Opened new event %" PRIu64 ", continuous section start",
ZMLockedPacket *starting_packet_lock = nullptr;
std::shared_ptr<ZMPacket> starting_packet = nullptr;
if (*start_it != *analysis_it) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) {
Warning("Unable to get starting packet lock");
delete packet_lock;
return false;
}
starting_packet = starting_packet_lock->packet_;
} else {
starting_packet = snap;
}
event = new Event(this, starting_packet->timestamp, "Continuous", noteSetMap);
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while (starting_packet and ((*start_it) != *analysis_it)) {
event->AddPacket(starting_packet);
// Have added the packet, don't want to unlock it until we have locked the next
packetqueue.increment_it(start_it);
if ((*start_it) == *analysis_it) {
if (starting_packet_lock) delete starting_packet_lock;
break;
}
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
delete starting_packet_lock;
if (!lp) return false;
starting_packet_lock = lp;
starting_packet = lp->packet_;
}
packetqueue.free_it(start_it);
delete start_it;
start_it = nullptr;
} else {
// Create event from current snap
event = new Event(this, timestamp, "Continuous", noteSetMap);
}
shared_data->last_event_id = event->Id();
// lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause;
for (const Zone &zone : zones) {
if (zone.Alarmed()) {
if (!alarm_cause.empty()) alarm_cause += ",";
alarm_cause += std::string(zone.Label());
}
}
alarm_cause = cause+" Continuous "+alarm_cause;
strncpy(shared_data->alarm_cause, alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
SetVideoWriterStartTime(event->StartTime());
Info("%s: %03d - Opened new event %" PRIu64 ", section start",
name.c_str(), analysis_image_count, event->Id()); name.c_str(), analysis_image_count, event->Id());
/* To prevent cancelling out an existing alert\prealarm\alarm state */ /* To prevent cancelling out an existing alert\prealarm\alarm state */
if (state == IDLE) { if (state == IDLE) {
@ -2027,63 +1978,12 @@ bool Monitor::Analyse() {
(event_close_mode == CLOSE_ALARM)); (event_close_mode == CLOSE_ALARM));
} }
if ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1)) { if ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1)) {
// lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause = "";
for (const Zone &zone : zones) {
if (zone.Alarmed()) {
alarm_cause = alarm_cause + "," + std::string(zone.Label());
}
}
if (!alarm_cause.empty()) alarm_cause[0] = ' ';
alarm_cause = cause + alarm_cause;
strncpy(shared_data->alarm_cause, alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s", Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s",
name.c_str(), image_count, Event::PreAlarmCount(), alarm_frame_count, shared_data->alarm_cause); name.c_str(), image_count, Event::PreAlarmCount(), alarm_frame_count, cause.c_str());
if (!event) { if (!event) {
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it( event = openEvent(snap, cause, noteSetMap);
*analysis_it,
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
);
ZMLockedPacket *starting_packet_lock = nullptr;
std::shared_ptr<ZMPacket> starting_packet = nullptr;
if (*start_it != *analysis_it) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) return false;
starting_packet = starting_packet_lock->packet_;
} else {
starting_packet = snap;
}
event = new Event(this, starting_packet->timestamp, cause, noteSetMap);
shared_data->last_event_id = event->Id();
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
SetVideoWriterStartTime(event->StartTime());
shared_data->state = state = ALARM; shared_data->state = state = ALARM;
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while (*start_it != *analysis_it) {
event->AddPacket(starting_packet);
packetqueue.increment_it(start_it);
if ( (*start_it) == (*analysis_it) ) {
if (starting_packet_lock) delete starting_packet_lock;
break;
}
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
delete starting_packet_lock;
if (!lp) {
// Shutting down event will be closed by ~Monitor()
// Perhaps we shouldn't do this.
return false;
}
starting_packet_lock = lp;
starting_packet = lp->packet_;
}
packetqueue.free_it(start_it);
delete start_it;
start_it = nullptr;
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id()); Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
} else { } else {
shared_data->state = state = ALARM; shared_data->state = state = ALARM;
@ -2193,11 +2093,7 @@ bool Monitor::Analyse() {
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(section_length).count())); static_cast<int64>(Seconds(section_length).count()));
closeEvent(); closeEvent();
event = new Event(this, timestamp, cause, noteSetMap); event = openEvent(snap, cause, noteSetMap);
shared_data->last_event_id = event->Id();
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
SetVideoWriterStartTime(event->StartTime());
} }
} else { } else {
Error("ALARM but no event"); Error("ALARM but no event");
@ -2776,6 +2672,67 @@ void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
Debug(2, "done annotating %s", label_text); Debug(2, "done annotating %s", label_text);
} // end void Monitor::TimestampImage } // end void Monitor::TimestampImage
Event * Monitor::openEvent(
const std::shared_ptr<ZMPacket> &snap,
const std::string &cause,
const Event::StringSetMap noteSetMap) {
// FIXME this iterator is not protected from invalidation
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
*analysis_it,
(cause == "Continuous" ? 0 : (pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count))
);
// This gets a lock on the starting packet
ZMLockedPacket *starting_packet_lock = nullptr;
std::shared_ptr<ZMPacket> starting_packet = nullptr;
if (*start_it != *analysis_it) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) {
Warning("Unable to get starting packet lock");
return nullptr;
}
starting_packet = starting_packet_lock->packet_;
} else {
starting_packet = snap;
}
event = new Event(this, starting_packet->timestamp, cause, noteSetMap);
shared_data->last_event_id = event->Id();
strncpy(shared_data->alarm_cause, cause.c_str(), sizeof(shared_data->alarm_cause)-1);
if (!event_start_command.empty()) {
if (fork() == 0) {
execlp(event_start_command.c_str(), event_start_command.c_str(), std::to_string(event->Id()).c_str(), nullptr);
Error("Error execing %s", event_start_command.c_str());
}
}
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while (starting_packet and ((*start_it) != *analysis_it)) {
event->AddPacket(starting_packet);
// Have added the packet, don't want to unlock it until we have locked the next
packetqueue.increment_it(start_it);
if ((*start_it) == *analysis_it) {
if (starting_packet_lock) delete starting_packet_lock;
break;
}
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
delete starting_packet_lock;
if (!lp) return nullptr; // only on terminate FIXME
starting_packet_lock = lp;
starting_packet = lp->packet_;
}
packetqueue.free_it(start_it);
delete start_it;
start_it = nullptr;
return event;
}
void Monitor::closeEvent() { void Monitor::closeEvent() {
if (!event) return; if (!event) return;
@ -2786,7 +2743,18 @@ void Monitor::closeEvent() {
Debug(1, "close event thread is not joinable"); Debug(1, "close event thread is not joinable");
} }
Debug(1, "Starting thread to close event"); Debug(1, "Starting thread to close event");
close_event_thread = std::thread([](Event *e){ delete e; }, event); close_event_thread = std::thread([](Event *e, const std::string &command){
int64_t event_id = e->Id();
delete e;
if (!command.empty()) {
if (fork() == 0) {
execlp(command.c_str(), command.c_str(), std::to_string(event_id).c_str(), nullptr);
Error("Error execing %s", command.c_str());
}
}
}, event, event_end_command);
Debug(1, "Nulling event"); Debug(1, "Nulling event");
event = nullptr; event = nullptr;
if (shared_data) video_store_data->recording = {}; if (shared_data) video_store_data->recording = {};
@ -3079,9 +3047,6 @@ int Monitor::PrimeCapture() {
int Monitor::PreCapture() const { return camera->PreCapture(); } int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture(); } int Monitor::PostCapture() const { return camera->PostCapture(); }
int Monitor::Close() { int Monitor::Close() {
if (close_event_thread.joinable()) {
close_event_thread.join();
}
// Because the stream indexes may change we have to clear out the packetqueue // Because the stream indexes may change we have to clear out the packetqueue
if (decoder) { if (decoder) {
decoder->Stop(); decoder->Stop();
@ -3099,10 +3064,14 @@ int Monitor::Close() {
video_fifo = nullptr; video_fifo = nullptr;
} }
if (close_event_thread.joinable()) {
close_event_thread.join();
}
std::lock_guard<std::mutex> lck(event_mutex); std::lock_guard<std::mutex> lck(event_mutex);
if (event) { if (event) {
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id()); Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id());
closeEvent(); closeEvent();
close_event_thread.join();
} }
if (camera) camera->Close(); if (camera) camera->Close();
return 1; return 1;

View File

@ -404,6 +404,8 @@ protected:
int n_linked_monitors; int n_linked_monitors;
MonitorLink **linked_monitors; MonitorLink **linked_monitors;
std::string event_start_command;
std::string event_end_command;
std::vector<Group *> groups; std::vector<Group *> groups;
@ -591,6 +593,10 @@ public:
bool Decode(); bool Decode();
void DumpImage( Image *dump_image ) const; void DumpImage( Image *dump_image ) const;
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const; void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
Event *openEvent(
const std::shared_ptr<ZMPacket> &snap,
const std::string &cause,
const Event::StringSetMap noteSetMap);
void closeEvent(); void closeEvent();
void Reload(); void Reload();

View File

@ -118,12 +118,13 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
for ( for (
auto it = ++pktQueue.begin(); auto it = ++pktQueue.begin();
it != pktQueue.end() and *it != add_packet; it != pktQueue.end() and *it != add_packet;
// iterator is incremented by erase
) { ) {
std::shared_ptr <ZMPacket>zm_packet = *it; std::shared_ptr <ZMPacket>zm_packet = *it;
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
if (!lp->trylock()) { if (!lp->trylock()) {
Debug(1, "Found locked packet when trying to free up video packets. Skipping to next one"); Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up.");
delete lp; delete lp;
++it; ++it;
continue; continue;
@ -312,7 +313,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
pktQueue.size()); pktQueue.size());
pktQueue.pop_front(); pktQueue.pop_front();
packet_counts[zm_packet->packet.stream_index] -= 1; packet_counts[zm_packet->packet.stream_index] -= 1;
//delete zm_packet;
} }
} // end if have at least max_video_packet_count video packets remaining } // end if have at least max_video_packet_count video packets remaining
// We signal on every packet because someday we may analyze sound // We signal on every packet because someday we may analyze sound

View File

@ -252,8 +252,15 @@ void HwCapsDetect() {
#elif defined(__arm__) #elif defined(__arm__)
// ARM processor in 32bit mode // ARM processor in 32bit mode
// To see if it supports NEON, we need to get that information from the kernel // To see if it supports NEON, we need to get that information from the kernel
#ifdef __linux__
unsigned long auxval = getauxval(AT_HWCAP); unsigned long auxval = getauxval(AT_HWCAP);
if (auxval & HWCAP_ARM_NEON) { if (auxval & HWCAP_ARM_NEON) {
#elif defined(__FreeBSD__)
unsigned long auxval = 0;
elf_aux_info(AT_HWCAP, &auxval, sizeof(auxval));
if (auxval & HWCAP_NEON) {
#error Unsupported OS.
#endif
Debug(1,"Detected ARM (AArch32) processor with Neon"); Debug(1,"Detected ARM (AArch32) processor with Neon");
neonversion = 1; neonversion = 1;
} else { } else {

View File

@ -878,16 +878,23 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
continue; continue;
} }
if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width()) if (polygon.Extent().Lo().x_ < 0
|| polygon.Extent().Lo().y_ < 0 || polygon.Extent().Hi().y_ > static_cast<int32>(monitor->Height())) { ||
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), fixing", polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width())
||
polygon.Extent().Lo().y_ < 0
||
polygon.Extent().Hi().y_ > static_cast<int32>(monitor->Height())) {
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d) != (%d,%d), fixing",
Id, Id,
Name, Name,
monitor->Name(), monitor->Name(),
polygon.Extent().Lo().x_, polygon.Extent().Lo().x_,
polygon.Extent().Lo().y_, polygon.Extent().Lo().y_,
polygon.Extent().Hi().x_, polygon.Extent().Hi().x_,
polygon.Extent().Hi().y_); polygon.Extent().Hi().y_,
monitor->Width(),
monitor->Height());
polygon.Clip(Box( polygon.Clip(Box(
{0, 0}, {0, 0},

View File

@ -220,12 +220,13 @@ int main(int argc, char *argv[]) {
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
sigset_t block_set; struct sigaction sa;
sigemptyset(&block_set); sa.sa_handler = SIG_IGN; //handle signal by ignoring
sigemptyset(&sa.sa_mask);
sigaddset(&block_set, SIGHUP); sa.sa_flags = 0;
sigaddset(&block_set, SIGUSR1); if (sigaction(SIGCHLD, &sa, 0) == -1) {
sigaddset(&block_set, SIGUSR2); Error("Unable to set SIGCHLD to ignore. There may be zombies.");
}
int result = 0; int result = 0;
int prime_capture_log_count = 0; int prime_capture_log_count = 0;
@ -286,7 +287,6 @@ int main(int argc, char *argv[]) {
Microseconds sleep_time = Microseconds(0); Microseconds sleep_time = Microseconds(0);
while (!zm_terminate) { while (!zm_terminate) {
//sigprocmask(SIG_BLOCK, &block_set, 0);
for (size_t i = 0; i < monitors.size(); i++) { for (size_t i = 0; i < monitors.size(); i++) {
monitors[i]->CheckAction(); monitors[i]->CheckAction();

View File

@ -116,7 +116,31 @@ else
echo "Defaulting to ZoneMinder upstream git" echo "Defaulting to ZoneMinder upstream git"
GITHUB_FORK="ZoneMinder" GITHUB_FORK="ZoneMinder"
fi; fi;
if [ "$SNAPSHOT" == "stable" ]; then fi;
# Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead.
if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; then
if [ -d "${GITHUB_FORK}_ZoneMinder.git" ]; then
echo "Using local clone ${GITHUB_FORK}_ZoneMinder.git to pull from."
cd "${GITHUB_FORK}_ZoneMinder.git"
echo "git fetch..."
git fetch
cd ../
echo "git clone ${GITHUB_FORK}_ZoneMinder.git ${GITHUB_FORK}_zoneminder_release"
git clone "${GITHUB_FORK}_ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release"
else
echo "git clone https://github.com/$GITHUB_FORK/ZoneMinder.git ${GITHUB_FORK}_zoneminder_release"
git clone "https://github.com/$GITHUB_FORK/ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release"
fi
else
echo "release dir already exists. Please remove it."
exit 0;
fi;
cd "${GITHUB_FORK}_zoneminder_release"
if [ "$SNAPSHOT" == "stable" ]; then
if [ "$BRANCH" == "" ]; then if [ "$BRANCH" == "" ]; then
#REV=$(git rev-list --tags --max-count=1) #REV=$(git rev-list --tags --max-count=1)
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`; BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
@ -131,7 +155,7 @@ else
fi fi
echo "Latest stable branch is $BRANCH"; echo "Latest stable branch is $BRANCH";
fi; fi;
else else
if [ "$BRANCH" == "" ]; then if [ "$BRANCH" == "" ]; then
echo "Defaulting to master branch"; echo "Defaulting to master branch";
BRANCH="master"; BRANCH="master";
@ -140,66 +164,34 @@ else
SNAPSHOT=`date +%Y%m%d%H%M%S`; SNAPSHOT=`date +%Y%m%d%H%M%S`;
else else
if [ "$SNAPSHOT" == "CURRENT" ]; then if [ "$SNAPSHOT" == "CURRENT" ]; then
# git the latest (short) commit hash of the version file
versionhash=$(git log -n1 --pretty=format:%h version)
# Number of commits since the version file was last changed
numcommits=$(git rev-list ${versionhash}..HEAD --count)
SNAPSHOT="`date +%Y%m%d.`$(git rev-list ${versionhash}..HEAD --count)" SNAPSHOT="`date +%Y%m%d.`$(git rev-list ${versionhash}..HEAD --count)"
fi; fi;
fi; fi;
fi;
fi
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
if [ "$PPA" == "" ]; then
if [ "$RELEASE" != "" ]; then
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
PPA="ppa:iconnor/zoneminder-stable"
else
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
fi;
else
if [ "$BRANCH" == "" ]; then
PPA="ppa:iconnor/zoneminder-master";
else
PPA="ppa:iconnor/zoneminder-$BRANCH";
fi;
fi;
fi; fi;
# Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead.
if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; then echo "git checkout $BRANCH"
if [ -d "${GITHUB_FORK}_ZoneMinder.git" ]; then git checkout $BRANCH
echo "Using local clone ${GITHUB_FORK}_ZoneMinder.git to pull from." if [ $? -ne 0 ]; then
cd "${GITHUB_FORK}_ZoneMinder.git"
echo "git fetch..."
git fetch
echo "git checkout $BRANCH"
git checkout $BRANCH
if [ $? -ne 0 ]; then
echo "Failed to switch to branch." echo "Failed to switch to branch."
exit 1; exit 1;
fi;
echo "git pull..."
git pull
cd ../
echo "git clone ${GITHUB_FORK}_ZoneMinder.git ${GITHUB_FORK}_zoneminder_release"
git clone "${GITHUB_FORK}_ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release"
else
echo "git clone https://github.com/$GITHUB_FORK/ZoneMinder.git ${GITHUB_FORK}_zoneminder_release"
git clone "https://github.com/$GITHUB_FORK/ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release"
fi
else
echo "release dir already exists. Please remove it."
exit 0;
fi; fi;
echo "git pull..."
cd "${GITHUB_FORK}_zoneminder_release" git pull
git checkout $BRANCH # Grab the ZoneMinder version from the contents of the version file
cd ../ VERSION=$(cat version)
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
if [ -z "$VERSION" ]; then if [ -z "$VERSION" ]; then
exit 1; exit 1;
fi; fi;
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
cd ../
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
VERSION="$VERSION~$SNAPSHOT"; VERSION="$VERSION~$SNAPSHOT";
fi; fi;
@ -230,8 +222,8 @@ rm .gitignore
cd ../ cd ../
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then if [ -e "$DIRECTORY.orig.tar.gz" ]; then
read -p "$DIRECTORY.orig.tar.gz does not exist, create it? [Y/n]" read -p "$DIRECTORY.orig.tar.gz exists, overwrite it? [Y/n]"
if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
fi; fi;
@ -357,6 +349,22 @@ EOF
fi; fi;
else else
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes"; SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes";
if [ "$PPA" == "" ]; then
if [ "$RELEASE" != "" ]; then
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
PPA="ppa:iconnor/zoneminder-stable"
else
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
fi;
else
if [ "$BRANCH" == "" ]; then
PPA="ppa:iconnor/zoneminder-master";
else
PPA="ppa:iconnor/zoneminder-$BRANCH";
fi;
fi;
fi;
dput="Y"; dput="Y";
if [ "$INTERACTIVE" != "no" ]; then if [ "$INTERACTIVE" != "no" ]; then

View File

@ -1 +1 @@
1.37.3 1.37.5

View File

@ -321,12 +321,20 @@ class MonitorsController extends AppController {
} }
$monitor = $this->Monitor->find('first', array( $monitor = $this->Monitor->find('first', array(
'fields' => array('Id', 'Type', 'Device'), 'fields' => array('Id', 'Type', 'Device', 'Function'),
'conditions' => array('Id' => $id) 'conditions' => array('Id' => $id)
)); ));
// Clean up the returned array // Clean up the returned array
$monitor = Set::extract('/Monitor/.', $monitor); $monitor = Set::extract('/Monitor/.', $monitor);
if ($monitor[0]['Function'] == 'None') {
$this->set(array(
'status' => false,
'statustext' => 'Monitor function is set to None',
'_serialize' => array('status','statustext'),
));
return;
}
// Pass -d for local, otherwise -m // Pass -d for local, otherwise -m
if ( $monitor[0]['Type'] == 'Local' ) { if ( $monitor[0]['Type'] == 'Local' ) {

View File

@ -167,9 +167,15 @@ class Group extends ZM_Object {
public function Parents() { public function Parents() {
$Parents = array(); $Parents = array();
$Parent = $this->Parent(); $Parent = $this->Parent();
while( $Parent ) { $seen_parents = array();
while ($Parent) {
$seen_parents[$Parent->Id()] = $Parent;
array_unshift($Parents, $Parent); array_unshift($Parents, $Parent);
$Parent = $Parent->Parent(); $Parent = $Parent->Parent();
if ($Parent and isset($seen_parents[$Parent->Id()])) {
Warning("Detected hierarchy loop in group {$Parent->Name()}");
break;
}
} }
return $Parents; return $Parents;
} }
@ -189,6 +195,9 @@ class Group extends ZM_Object {
public function canView($u=null) { public function canView($u=null) {
global $user; global $user;
if (!$u) $u = $user; if (!$u) $u = $user;
if (!count($this->Monitors()) and !count($this->Children())) {
return true;
}
# Can view if we can view any of the monitors in it. # Can view if we can view any of the monitors in it.
foreach ($this->Monitors() as $monitor) { foreach ($this->Monitors() as $monitor) {
if ($monitor->canView($u)) return true; if ($monitor->canView($u)) return true;

View File

@ -57,6 +57,8 @@ class Monitor extends ZM_Object {
'DecodingEnabled' => array('type'=>'boolean','default'=>1), 'DecodingEnabled' => array('type'=>'boolean','default'=>1),
'LinkedMonitors' => array('type'=>'set', 'default'=>null), 'LinkedMonitors' => array('type'=>'set', 'default'=>null),
'Triggers' => array('type'=>'set','default'=>''), 'Triggers' => array('type'=>'set','default'=>''),
'EventStartCommand' => '',
'EventEndCommand' => '',
'ONVIF_URL' => '', 'ONVIF_URL' => '',
'ONVIF_Username' => '', 'ONVIF_Username' => '',
'ONVIF_Password' => '', 'ONVIF_Password' => '',

View File

@ -13,6 +13,10 @@ class Zone extends ZM_Object {
'Name' => '', 'Name' => '',
'Type' => 'Active', 'Type' => 'Active',
'Units' => 'Pixels', 'Units' => 'Pixels',
'NumCoords' => '0',
'Coords' => 0,
'Area' => '0',
'AlarmRGB' => '0',
'CheckMethod' => 'Blobs', 'CheckMethod' => 'Blobs',
'MinPixelThreshold' => null, 'MinPixelThreshold' => null,
'MaxPixelThreshold' => null, 'MaxPixelThreshold' => null,
@ -46,5 +50,17 @@ class Zone extends ZM_Object {
return new Monitor(); return new Monitor();
} }
public function Points() {
return coordsToPoints($this->Coords());
}
public function AreaCoords() {
return preg_replace('/\s+/', ',', $this->Coords());
}
public function svg_polygon() {
return '<polygon points="'.$this->AreaCoords().'" class="'.$this->Type().'" />';
}
} # end class Zone } # end class Zone
?> ?>

View File

@ -87,11 +87,12 @@ define('ZM_PCRE', '@ZM_PCRE@'); // PCRE support enabled
// //
// Alarm states // Alarm states
// //
define('STATE_IDLE', 0); define('STATE_UNKNOWN', 0);
define('STATE_PREALARM', 1); define('STATE_IDLE', 1);
define('STATE_ALARM', 2); define('STATE_PREALARM', 2);
define('STATE_ALERT', 3); define('STATE_ALARM', 3);
define('STATE_TAPE', 4); define('STATE_ALERT', 4);
define('STATE_TAPE', 5);
// //
// DVR Control Commands // DVR Control Commands

View File

@ -77,7 +77,7 @@ height: 100%;
position: relative; position: relative;
} }
#imageFeed { #videoFeed {
display: inline-block; display: inline-block;
position: relative; position: relative;
text-align: center; text-align: center;
@ -263,3 +263,17 @@ height: 100%;
height: 100%; height: 100%;
background-color: #999999; background-color: #999999;
} }
svg.zones {
position:absolute;
top: 0;
left: 0;
background: none;
width: 100%;
/*
height: 100%;
*/
}
#videoobj {
width: 100%;
height: 100%;
}

View File

@ -1,6 +1,8 @@
#header { #header {
display: flex; }
justify-content: space-between;
#sidebar {
min-width: 140px;
} }
#menuControls { #menuControls {
@ -19,19 +21,22 @@
#monitorStatus { #monitorStatus {
margin: 4px auto; margin: 4px auto;
text-align: center; text-align: center;
} display: inline-block;
#monitorStatus #enableDisableAlarms {
float: left;
}
#monitorStatus #forceCancelAlarm {
float: right;
} }
#monitorStatus #monitorState { #monitorStatus #monitorState {
} }
#replayStatus {
margin: 3px 0 2px;
text-align: center;
display: inline-block;
}
#replayStatus > span {
padding: 0 4px;
}
#dvrControls { #dvrControls {
margin-top: 3px; margin-top: 3px;
margin-bottom: 2px; margin-bottom: 2px;
@ -67,15 +72,6 @@
cursor: default; cursor: default;
} }
#replayStatus {
margin: 3px 0 2px;
text-align: center;
clear: both;
}
#replayStatus > span {
padding: 0 4px;
}
#events { #events {
margin: 0 auto; margin: 0 auto;
@ -116,3 +112,22 @@ span.alert {
background-color: #DCDCDC; background-color: #DCDCDC;
} }
.controlHeader {
width: 100%;
}
#viewingFPS,
#captureFPS,
#analysisFPS
{
display: inline-block;
}
#stateValue,
#viewingFPSValue,
#captureFPSValue,
#analysisFPSValue
{
display: inline-block;
min-width: 40px;
text-align: right;
}

View File

@ -0,0 +1,19 @@
.zones polygon {
fill-opacity: 0.25;
}
.Active {
stroke: #ff0000;
fill: #ff0000;
}
.Inclusive {
stroke: #FFA500;
fill: #FFA500;
}
.Exclusive {
stroke: #800080;
fill: #800080;
}
.Preclusive {
stroke: #0000FF;
fill: #0000FF;
}

View File

@ -90,3 +90,18 @@ unset($user_without_password['Password']);
echo json_encode($user_without_password); echo json_encode($user_without_password);
?>; ?>;
var running = <?php echo daemonCheck()?'true':'false' ?>; var running = <?php echo daemonCheck()?'true':'false' ?>;
var STATE_UNKNOWN = <?php echo STATE_UNKNOWN ?>;
var STATE_IDLE = <?php echo STATE_IDLE ?>;
var STATE_PREALARM = <?php echo STATE_PREALARM ?>;
var STATE_ALARM = <?php echo STATE_ALARM ?>;
var STATE_ALERT = <?php echo STATE_ALERT ?>;
var STATE_TAPE = <?php echo STATE_TAPE ?>;
var stateStrings = new Array();
stateStrings[STATE_UNKNOWN] = "<?php echo translate('Unknown') ?>";
stateStrings[STATE_IDLE] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_PREALARM] = "<?php echo translate('Prealarm') ?>";
stateStrings[STATE_ALARM] = "<?php echo translate('Alarm') ?>";
stateStrings[STATE_ALERT] = "<?php echo translate('Alert') ?>";
stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";

View File

@ -260,31 +260,37 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
?> ?>
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>"> <tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
<?php <?php
$source_class = 'infoText';
$source_class_reason = '';
if ( (!$monitor['Status'] || $monitor['Status'] == 'NotRunning') && $monitor['Type']!='WebSite' ) { if ( (!$monitor['Status'] || $monitor['Status'] == 'NotRunning') && $monitor['Type']!='WebSite' ) {
$source_class = 'errorText'; $source_class = 'errorText';
$source_class_reason = translate('Not Running');
} else { } else {
if ( $monitor['CaptureFPS'] == '0.00' ) { if ( $monitor['CaptureFPS'] == '0.00' ) {
$source_class = 'errorText'; $source_class = 'errorText';
$source_class_reason = translate('No capture FPS');
} else if ( (!$monitor['AnalysisFPS']) && ($monitor['Function']!='Monitor') && ($monitor['Function'] != 'Nodect') ) { } else if ( (!$monitor['AnalysisFPS']) && ($monitor['Function']!='Monitor') && ($monitor['Function'] != 'Nodect') ) {
$source_class = 'warnText'; $source_class = 'warnText';
} else { $source_class_reason = translate('No analysis FPS');
$source_class = 'infoText';
} }
} }
if ( $monitor['Function'] == 'None' )
$function_class = 'errorText';
else
$function_class = 'infoText';
$function_class = 'infoText';
if ($monitor['Function'] == 'None') {
$function_class = 'errorText';
}
$dot_class = $source_class;
$dot_class_reason = $source_class_reason;
if ( $function_class != 'infoText' ) {
$dot_class = $function_class;
} else if (($monitor['Function'] == 'Modect' || $monitor['Function'] == 'Mocord') and !$monitor['Enabled']) {
$dot_class .= ' warnText';
$dot_class_reason .= ' '.translate('Analysis is disabled');
}
$scale = max(reScale(SCALE_BASE, $monitor['DefaultScale'], ZM_WEB_DEFAULT_SCALE), SCALE_BASE); $scale = max(reScale(SCALE_BASE, $monitor['DefaultScale'], ZM_WEB_DEFAULT_SCALE), SCALE_BASE);
$stream_available = canView('Stream') and $monitor['Type']=='WebSite' or ($monitor['CaptureFPS'] && $monitor['Function'] != 'None'); $stream_available = canView('Stream') and $monitor['Type']=='WebSite' or ($monitor['CaptureFPS'] && $monitor['Function'] != 'None');
$dot_class = $source_class;
if ( $function_class != 'infoText' ) {
$dot_class = $function_class;
} else if ( !$monitor['Enabled'] ) {
$dot_class .= ' warnText';
}
if ( ZM_WEB_ID_ON_CONSOLE ) { if ( ZM_WEB_ID_ON_CONSOLE ) {
?> ?>
@ -317,7 +323,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
} }
?> ?>
<td class="colName"> <td class="colName">
<i class="material-icons md-18 <?php echo $dot_class ?>">lens</i> <i class="material-icons md-18 <?php echo $dot_class ?>" title="<?php echo $dot_class_reason ?>">lens</i>
<a <?php echo ($stream_available ? 'href="?view=watch&amp;mid='.$monitor['Id'].'">' : '>') . validHtmlStr($monitor['Name']) ?></a><br/> <a <?php echo ($stream_available ? 'href="?view=watch&amp;mid='.$monitor['Id'].'">' : '>') . validHtmlStr($monitor['Name']) ?></a><br/>
<?php echo $imgHTML ?> <?php echo $imgHTML ?>
<div class="small text-nowrap text-muted"> <div class="small text-nowrap text-muted">

View File

@ -25,47 +25,57 @@ if ( !canView('Events') ) {
require_once('includes/Event.php'); require_once('includes/Event.php');
require_once('includes/Filter.php'); require_once('includes/Filter.php');
require_once('includes/Zone.php');
$eid = validInt($_REQUEST['eid']); $eid = validInt($_REQUEST['eid']);
$fid = !empty($_REQUEST['fid']) ? validInt($_REQUEST['fid']) : 1; $fid = !empty($_REQUEST['fid']) ? validInt($_REQUEST['fid']) : 1;
$Event = new ZM\Event($eid); $Event = new ZM\Event($eid);
if ( $user['MonitorIds'] ) { $monitor = $Event->Monitor();
$monitor_ids = explode(',', $user['MonitorIds']);
if ( count($monitor_ids) and ! in_array($Event->MonitorId(), $monitor_ids) ) { if (!$monitor->canView()) {
$view = 'error'; $view = 'error';
return; return;
}
} }
$Monitor = $Event->Monitor();
if ( isset($_REQUEST['rate']) ) { zm_session_start();
if (isset($_REQUEST['rate']) ) {
$rate = validInt($_REQUEST['rate']); $rate = validInt($_REQUEST['rate']);
} else if ( isset($_COOKIE['zmEventRate']) ) { } else if (isset($_COOKIE['zmEventRate'])) {
$rate = $_COOKIE['zmEventRate']; $rate = $_COOKIE['zmEventRate'];
} else { } else {
$rate = reScale(RATE_BASE, $Monitor->DefaultRate(), ZM_WEB_DEFAULT_RATE); $rate = reScale(RATE_BASE, $monitor->DefaultRate(), ZM_WEB_DEFAULT_RATE);
} }
if ( isset($_REQUEST['scale']) ) { if (isset($_REQUEST['scale'])) {
$scale = validInt($_REQUEST['scale']); $scale = validInt($_REQUEST['scale']);
} else if ( isset($_COOKIE['zmEventScale'.$Event->MonitorId()]) ) { } else if (isset($_COOKIE['zmEventScale'.$Event->MonitorId()])) {
$scale = $_COOKIE['zmEventScale'.$Event->MonitorId()]; $scale = $_COOKIE['zmEventScale'.$Event->MonitorId()];
} else { } else {
$scale = $Monitor->DefaultScale(); $scale = $monitor->DefaultScale();
}
$showZones = false;
if (isset($_REQUEST['showZones'])) {
$showZones = $_REQUEST['showZones'] == 1;
$_SESSION['zmEventShowZones'.$monitor->Id()] = $showZones;
} else if (isset($_COOKIE['zmEventShowZones'.$monitor->Id()])) {
$showZones = $_COOKIE['zmEventShowZones'.$monitor->Id()] == 1;
} else if (isset($_SESSION['zmEventShowZones'.$monitor->Id()]) ) {
$showZones = $_SESSION['zmEventShowZones'.$monitor->Id()];
} }
$codec = 'auto'; $codec = 'auto';
if ( isset($_REQUEST['codec']) ) { if (isset($_REQUEST['codec'])) {
$codec = $_REQUEST['codec']; $codec = $_REQUEST['codec'];
zm_session_start();
$_SESSION['zmEventCodec'.$Event->MonitorId()] = $codec; $_SESSION['zmEventCodec'.$Event->MonitorId()] = $codec;
session_write_close();
} else if ( isset($_SESSION['zmEventCodec'.$Event->MonitorId()]) ) { } else if ( isset($_SESSION['zmEventCodec'.$Event->MonitorId()]) ) {
$codec = $_SESSION['zmEventCodec'.$Event->MonitorId()]; $codec = $_SESSION['zmEventCodec'.$Event->MonitorId()];
} else { } else {
$codec = $Monitor->DefaultCodec(); $codec = $monitor->DefaultCodec();
} }
session_write_close();
$codecs = array( $codecs = array(
'auto' => translate('Auto'), 'auto' => translate('Auto'),
'MP4' => translate('MP4'), 'MP4' => translate('MP4'),
@ -79,32 +89,30 @@ $replayModes = array(
'gapless' => translate('ReplayGapless'), 'gapless' => translate('ReplayGapless'),
); );
if ( isset($_REQUEST['streamMode']) ) if (isset($_REQUEST['streamMode']))
$streamMode = validHtmlStr($_REQUEST['streamMode']); $streamMode = validHtmlStr($_REQUEST['streamMode']);
else else
$streamMode = 'video'; $streamMode = 'video';
$replayMode = ''; $replayMode = '';
if ( isset($_REQUEST['replayMode']) ) if (isset($_REQUEST['replayMode']))
$replayMode = validHtmlStr($_REQUEST['replayMode']); $replayMode = validHtmlStr($_REQUEST['replayMode']);
if ( isset($_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) ) if (isset($_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']))
$replayMode = validHtmlStr($_COOKIE['replayMode']); $replayMode = validHtmlStr($_COOKIE['replayMode']);
if ( ( !$replayMode ) or ( !$replayModes[$replayMode] ) ) { if ((!$replayMode) or !$replayModes[$replayMode]) {
$replayMode = 'none'; $replayMode = 'none';
} }
$video_tag = false; $video_tag = ($Event->DefaultVideo() and ($codec == 'MP4' or $codec == 'auto'));
if ( $Event->DefaultVideo() and ( $codec == 'MP4' or $codec == 'auto' ) ) {
$video_tag = true;
}
// videojs zoomrotate only when direct recording // videojs zoomrotate only when direct recording
$Zoom = 1; $Zoom = 1;
$Rotation = 0; $Rotation = 0;
if ( $Monitor->VideoWriter() == '2' ) { if ($monitor->VideoWriter() == '2') {
# Passthrough # Passthrough
$Rotation = $Event->Orientation(); $Rotation = $Event->Orientation();
if ( in_array($Event->Orientation(),array('90','270')) ) if (in_array($Event->Orientation(),array('90','270')))
$Zoom = $Event->Height()/$Event->Width(); $Zoom = $Event->Height()/$Event->Width();
} }
@ -143,7 +151,7 @@ if ( $Event->Id() and !file_exists($Event->Path()) )
<div id="toolbar" > <div id="toolbar" >
<button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button> <button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button> <button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
<?php if ( $Event->Id() ) { ?> <?php if ($Event->Id()) { ?>
<button id="renameBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Rename') ?>" disabled><i class="fa fa-font"></i></button> <button id="renameBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Rename') ?>" disabled><i class="fa fa-font"></i></button>
<button id="archiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Archive') ?>" disabled><i class="fa fa-archive"></i></button> <button id="archiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Archive') ?>" disabled><i class="fa fa-archive"></i></button>
<button id="unarchiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Unarchive') ?>" disabled><i class="fa fa-file-archive-o"></i></button> <button id="unarchiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Unarchive') ?>" disabled><i class="fa fa-file-archive-o"></i></button>
@ -158,7 +166,13 @@ if ( $Event->Id() and !file_exists($Event->Path()) )
<button id="statsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Stats') ?>" ><i class="fa fa-info"></i></button> <button id="statsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Stats') ?>" ><i class="fa fa-info"></i></button>
<button id="framesBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Frames') ?>" ><i class="fa fa-picture-o"></i></button> <button id="framesBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Frames') ?>" ><i class="fa fa-picture-o"></i></button>
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>"><i class="fa fa-trash"></i></button> <button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>"><i class="fa fa-trash"></i></button>
<?php } // end if Event->Id ?> <?php
if (canView('System')) { ?>
<button id="toggleZonesButton" class="btn btn-<?php echo $showZones?'normal':'secondary'?>" title="<?php echo translate(($showZones?'Hide':'Show').' Zones')?>" ><span class="material-icons"><?php echo $showZones?'layers_clear':'layers'?></span</button>
<?php
}
} // end if Event->Id
?>
</div> </div>
<h2><?php echo translate('Event').' '.$Event->Id() ?></h2> <h2><?php echo translate('Event').' '.$Event->Id() ?></h2>
@ -190,10 +204,10 @@ if ( $Event->Id() and !file_exists($Event->Path()) )
<div class=""> <div class="">
<div id="eventVideo"> <div id="eventVideo">
<!-- VIDEO CONTENT --> <!-- VIDEO CONTENT -->
<?php
if ( $video_tag ) {
?>
<div id="videoFeed"> <div id="videoFeed">
<?php
if ($video_tag) {
?>
<video autoplay id="videoobj" class="video-js vjs-default-skin" <video autoplay id="videoobj" class="video-js vjs-default-skin"
style="transform: matrix(1, 0, 0, 1, 0, 0);" style="transform: matrix(1, 0, 0, 1, 0, 0);"
<?php echo $scale ? 'width="'.reScale($Event->Width(), $scale).'"' : '' ?> <?php echo $scale ? 'width="'.reScale($Event->Width(), $scale).'"' : '' ?>
@ -209,21 +223,17 @@ if ( $video_tag ) {
<track id="monitorCaption" kind="captions" label="English" srclang="en" src='data:plain/text;charset=utf-8,"WEBVTT\n\n 00:00:00.000 --> 00:00:01.000 ZoneMinder"' default/> <track id="monitorCaption" kind="captions" label="English" srclang="en" src='data:plain/text;charset=utf-8,"WEBVTT\n\n 00:00:00.000 --> 00:00:01.000 ZoneMinder"' default/>
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
</div><!--videoFeed-->
<?php <?php
} else { } else {
?>
<div id="imageFeed">
<?php
if ( (ZM_WEB_STREAM_METHOD == 'mpeg') && ZM_MPEG_LIVE_FORMAT ) { if ( (ZM_WEB_STREAM_METHOD == 'mpeg') && ZM_MPEG_LIVE_FORMAT ) {
$streamSrc = $Event->getStreamSrc(array('mode'=>'mpeg', 'scale'=>$scale, 'rate'=>$rate, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_REPLAY_FORMAT, 'replay'=>$replayMode),'&amp;'); $streamSrc = $Event->getStreamSrc(array('mode'=>'mpeg', 'scale'=>$scale, 'rate'=>$rate, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_REPLAY_FORMAT, 'replay'=>$replayMode),'&amp;');
outputVideoStream('evtStream', $streamSrc, reScale( $Event->Width(), $scale ).'px', reScale( $Event->Height(), $scale ).'px', ZM_MPEG_LIVE_FORMAT ); outputVideoStream('evtStream', $streamSrc, reScale( $Event->Width(), $scale ).'px', reScale( $Event->Height(), $scale ).'px', ZM_MPEG_LIVE_FORMAT );
} else { } else {
$streamSrc = $Event->getStreamSrc(array('mode'=>'jpeg', 'frame'=>$fid, 'scale'=>$scale, 'rate'=>$rate, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>$replayMode),'&amp;'); $streamSrc = $Event->getStreamSrc(array('mode'=>'jpeg', 'frame'=>$fid, 'scale'=>$scale, 'rate'=>$rate, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>$replayMode),'&amp;');
if ( canStreamNative() ) { if ( canStreamNative() ) {
outputImageStream('evtStream', $streamSrc, reScale($Event->Width(), $scale).'px', reScale($Event->Height(), $scale).'px', validHtmlStr($Event->Name())); outputImageStream('evtStream', $streamSrc, '100%', '100%', validHtmlStr($Event->Name()));
} else { } else {
outputHelperStream('evtStream', $streamSrc, reScale($Event->Width(), $scale).'px', reScale($Event->Height(), $scale).'px' ); outputHelperStream('evtStream', $streamSrc, '100%', '100%');
} }
} // end if stream method } // end if stream method
?> ?>
@ -231,10 +241,18 @@ if ( (ZM_WEB_STREAM_METHOD == 'mpeg') && ZM_MPEG_LIVE_FORMAT ) {
<div id="progressBar" style="width: <?php echo reScale($Event->Width(), $scale);?>px;"> <div id="progressBar" style="width: <?php echo reScale($Event->Width(), $scale);?>px;">
<div class="progressBox" id="progressBox" title="" style="width: 0%;"></div> <div class="progressBox" id="progressBox" title="" style="width: 0%;"></div>
</div><!--progressBar--> </div><!--progressBar-->
</div><!--imageFeed-->
<?php <?php
} /*end if !DefaultVideo*/ } /*end if !DefaultVideo*/
?> ?>
<svg class="zones" id="zones<?php echo $monitor->Id() ?>" style="display:<?php echo $showZones ? 'block' : 'none'; ?>" viewBox="0 0 <?php echo $monitor->ViewWidth().' '.$monitor->ViewHeight() ?>" preserveAspectRatio="none">
<?php
foreach (ZM\Zone::find(array('MonitorId'=>$monitor->Id()), array('order'=>'Area DESC')) as $zone) {
echo $zone->svg_polygon();
} // end foreach zone
?>
Sorry, your browser does not support inline SVG
</svg>
</div><!--videoFeed-->
<p id="dvrControls"> <p id="dvrControls">
<button type="button" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" data-on-click-true="streamPrev"> <button type="button" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" data-on-click-true="streamPrev">
<i class="material-icons md-18">skip_previous</i> <i class="material-icons md-18">skip_previous</i>
@ -268,8 +286,8 @@ if ( (ZM_WEB_STREAM_METHOD == 'mpeg') && ZM_MPEG_LIVE_FORMAT ) {
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue">Replay</span></span> <span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue">Replay</span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rate"><?php echo translate('Rate') ?>:
<?php <?php
#rates are defined in skins/classic/includes/config.php #rates are defined in skins/classic/includes/config.php
echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue')); echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue'));
?> ?>
<!--<span id="rateValue"><?php echo $rate/100 ?></span>x</span>--> <!--<span id="rateValue"><?php echo $rate/100 ?></span>x</span>-->
<span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue">0</span>s</span> <span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue">0</span>s</span>
@ -283,4 +301,7 @@ echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue'));
</div><!--content--> </div><!--content-->
</div><!--page--> </div><!--page-->
<?php xhtmlFooter() ?> <?php
echo output_link_if_exists(array('css/base/zones.css'));
xhtmlFooter();
?>

View File

@ -152,7 +152,7 @@ $booleanValues = array(
$focusWindow = true; $focusWindow = true;
$storageareas = array('' => 'All') + ZM\ZM_Object::Objects_Indexed_By_Id('ZM\Storage'); $storageareas = array('' => array('Name'=>'NULL Unspecified'), '0' => array('Name'=>'Zero')) + ZM\ZM_Object::Objects_Indexed_By_Id('ZM\Storage');
$weekdays = array(); $weekdays = array();
for ( $i = 0; $i < 7; $i++ ) { for ( $i = 0; $i < 7; $i++ ) {

View File

@ -177,7 +177,7 @@ function changeScale() {
var newWidth; var newWidth;
var newHeight; var newHeight;
var autoScale; var autoScale;
var eventViewer= $j(vid ? '#videoobj' : '#evtStream'); var eventViewer= $j(vid ? '#videoobj' : '#videoFeed');
var alarmCue = $j('div.alarmCue'); var alarmCue = $j('div.alarmCue');
var bottomEl = $j('#replayStatus'); var bottomEl = $j('#replayStatus');
@ -773,8 +773,9 @@ function manageDelConfirmModalBtns() {
return; return;
} }
pauseClicked();
evt.preventDefault(); evt.preventDefault();
$j.getJSON(thisUrl + '?request=event&task=delete&id='+eventData.Id) $j.getJSON(thisUrl + '?request=event&action=delete&id='+eventData.Id)
.done(function(data) { .done(function(data) {
$j('#deleteConfirm').modal('hide'); $j('#deleteConfirm').modal('hide');
streamNext(true); streamNext(true);
@ -909,12 +910,12 @@ function initPage() {
progressBarNav(); progressBarNav();
streamCmdTimer = setTimeout(streamQuery, 500); streamCmdTimer = setTimeout(streamQuery, 500);
if (canStreamNative) { if (canStreamNative) {
if (!$j('#imageFeed')) { if (!$j('#videoFeed')) {
console.log('No element with id tag imageFeed found.'); console.log('No element with id tag videoFeed found.');
} else { } else {
var streamImg = $j('#imageFeed img'); var streamImg = $j('#videoFeed img');
if (!streamImg) { if (!streamImg) {
streamImg = $j('#imageFeed object'); streamImg = $j('#videoFeed object');
} }
$j(streamImg).click(function(event) { $j(streamImg).click(function(event) {
handleClick(event); handleClick(event);
@ -1070,5 +1071,27 @@ function initPage() {
}); });
} // end initPage } // end initPage
document.getElementById('toggleZonesButton').addEventListener('click', toggleZones);
function toggleZones(e) {
const zones = $j('#zones'+eventData.MonitorId);
const button = document.getElementById('toggleZonesButton');
if (zones.length) {
if (zones.is(":visible")) {
zones.hide();
button.setAttribute('title', showZonesString);
button.innerHTML = '<span class="material-icons">layers</span>';
setCookie('zmEventShowZones'+eventData.MonitorId, '0', 3600);
} else {
zones.show();
button.setAttribute('title', hideZonesString);
button.innerHTML = '<span class="material-icons">layers_clear</span>';
setCookie('zmEventShowZones'+eventData.MonitorId, '1', 3600);
}
} else {
console.error("Zones svg not found");
}
}
// Kick everything off // Kick everything off
$j(document).ready(initPage); $j(document).ready(initPage);

View File

@ -1,7 +1,7 @@
<?php <?php
global $connkey; global $connkey;
global $Event; global $Event;
global $Monitor; global $monitor;
global $filterQuery; global $filterQuery;
global $sortQuery; global $sortQuery;
global $rates; global $rates;
@ -43,8 +43,9 @@ var eventData = {
Id: '<?php echo $Event->Id() ?>', Id: '<?php echo $Event->Id() ?>',
Name: '<?php echo $Event->Name() ?>', Name: '<?php echo $Event->Name() ?>',
MonitorId: '<?php echo $Event->MonitorId() ?>', MonitorId: '<?php echo $Event->MonitorId() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>', MonitorName: '<?php echo validJsStr($monitor->Name()) ?>',
Cause: '<?php echo validHtmlStr($Event->Cause()) ?>', Cause: '<?php echo validHtmlStr($Event->Cause()) ?>',
Notes: '<?php echo $Event->Notes()?>',
Width: '<?php echo $Event->Width() ?>', Width: '<?php echo $Event->Width() ?>',
Height: '<?php echo $Event->Height() ?>', Height: '<?php echo $Event->Height() ?>',
Length: '<?php echo $Event->Length() ?>', Length: '<?php echo $Event->Length() ?>',
@ -72,6 +73,7 @@ var eventDataStrings = {
MonitorId: '<?php echo translate('AttrMonitorId') ?>', MonitorId: '<?php echo translate('AttrMonitorId') ?>',
MonitorName: '<?php echo translate('AttrMonitorName') ?>', MonitorName: '<?php echo translate('AttrMonitorName') ?>',
Cause: '<?php echo translate('Cause') ?>', Cause: '<?php echo translate('Cause') ?>',
Notes: '<?php echo translate('Notes') ?>',
StartDateTimeShort: '<?php echo translate('AttrStartTime') ?>', StartDateTimeShort: '<?php echo translate('AttrStartTime') ?>',
Length: '<?php echo translate('Duration') ?>', Length: '<?php echo translate('Duration') ?>',
Frames: '<?php echo translate('AttrFrames') ?>', Frames: '<?php echo translate('AttrFrames') ?>',
@ -93,7 +95,7 @@ var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode
var rates = <?php echo json_encode(array_keys($rates)) ?>; var rates = <?php echo json_encode(array_keys($rates)) ?>;
var rate = '<?php echo $rate ?>'; // really only used when setting up initial playback rate. var rate = '<?php echo $rate ?>'; // really only used when setting up initial playback rate.
var scale = "<?php echo $scale ?>"; var scale = "<?php echo $scale ?>";
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>"; var LabelFormat = "<?php echo validJsStr($monitor->LabelFormat())?>";
var streamTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>; var streamTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
@ -105,6 +107,8 @@ var streamMode = '<?php echo $streamMode ?>';
// //
var deleteString = "<?php echo validJsStr(translate('Delete')) ?>"; var deleteString = "<?php echo validJsStr(translate('Delete')) ?>";
var causeString = "<?php echo validJsStr(translate('AttrCause')) ?>"; var causeString = "<?php echo validJsStr(translate('AttrCause')) ?>";
var showZonesString = "<?php echo validJsStr(translate('Show Zones'))?>";
var hideZonesString = "<?php echo validJsStr(translate('Hide Zones'))?>";
var WEB_LIST_THUMB_WIDTH = '<?php echo ZM_WEB_LIST_THUMB_WIDTH ?>'; var WEB_LIST_THUMB_WIDTH = '<?php echo ZM_WEB_LIST_THUMB_WIDTH ?>';
var WEB_LIST_THUMB_HEIGHT = '<?php echo ZM_WEB_LIST_THUMB_HEIGHT ?>'; var WEB_LIST_THUMB_HEIGHT = '<?php echo ZM_WEB_LIST_THUMB_HEIGHT ?>';
var popup = '<?php echo $popup ?>'; var popup = '<?php echo $popup ?>';

View File

@ -63,13 +63,13 @@ function processRows(rows) {
row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>'; row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>' + row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>' +
'<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>'; '<br/><div class="small text-muted">' + archived + emailed + '</div>';
if ( canEdit.Monitors ) row.Monitor = '<a href="?view=event&amp;eid=' + eid + '">' + row.Monitor + '</a>'; if ( canEdit.Monitors ) row.Monitor = '<a href="?view=event&amp;eid=' + eid + '">' + row.Monitor + '</a>';
if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>'; if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( row.Notes.indexOf('detected:') >= 0 ) { if ( row.Notes.indexOf('detected:') >= 0 ) {
row.Cause = row.Cause + '<a href="#" class="objDetectLink" data-eid=' +eid+ '><div class="small text-nowrap text-muted"><u>' + row.Notes + '</u></div></div></a>'; row.Cause = row.Cause + '<a href="#" class="objDetectLink" data-eid=' +eid+ '><div class="small text-muted"><u>' + row.Notes + '</u></div></div></a>';
} else if ( row.Notes != 'Forced Web: ' ) { } else if ( row.Notes != 'Forced Web: ' ) {
row.Cause = row.Cause + '<br/><div class="small text-nowrap text-muted">' + row.Notes + '</div>'; row.Cause = row.Cause + '<br/><div class="small text-muted">' + row.Notes + '</div>';
} }
row.Frames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.Frames + '</a>'; row.Frames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.Frames + '</a>';
row.AlarmFrames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.AlarmFrames + '</a>'; row.AlarmFrames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.AlarmFrames + '</a>';

View File

@ -1,18 +1,6 @@
// //
// Import constants // Import constants
// //
var STATE_IDLE = <?php echo STATE_IDLE ?>;
var STATE_PREALARM = <?php echo STATE_PREALARM ?>;
var STATE_ALARM = <?php echo STATE_ALARM ?>;
var STATE_ALERT = <?php echo STATE_ALERT ?>;
var STATE_TAPE = <?php echo STATE_TAPE ?>;
var stateStrings = new Array();
stateStrings[STATE_IDLE] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_PREALARM] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_ALARM] = "<?php echo translate('Alarm') ?>";
stateStrings[STATE_ALERT] = "<?php echo translate('Alert') ?>";
stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";
var CMD_QUERY = <?php echo CMD_QUERY ?>; var CMD_QUERY = <?php echo CMD_QUERY ?>;

View File

@ -57,7 +57,7 @@ function getFrame(monId, time, last_Frame) {
var events_for_monitor = events_by_monitor_id[monId]; var events_for_monitor = events_by_monitor_id[monId];
if ( !events_for_monitor ) { if ( !events_for_monitor ) {
console.log("No events for monitor " + monId); //console.log("No events for monitor " + monId);
return; return;
} }
@ -648,8 +648,11 @@ function setSpeed(speed_index) {
} }
function setLive(value) { function setLive(value) {
// When we submit the context etc goes away but we may still be trying to update
// So kill the timer.
clearInterval(timerObj);
liveMode = value; liveMode = value;
var form = $j('#montagereview_form')[0]; var form = document.getElementById('montagereview_form');
form.elements['live'].value = value; form.elements['live'].value = value;
form.submit(); form.submit();
return false; return false;
@ -985,6 +988,19 @@ function initPage() {
}); });
}); });
if ( !liveMode ) {
canvas = document.getElementById('timeline');
canvas.addEventListener('mousemove', mmove, false);
canvas.addEventListener('touchmove', tmove, false);
canvas.addEventListener('mousedown', mdown, false);
canvas.addEventListener('mouseup', mup, false);
canvas.addEventListener('mouseout', mout, false);
ctx = canvas.getContext('2d');
drawGraph();
}
for ( var i = 0, len = monitorPtr.length; i < len; i += 1 ) { for ( var i = 0, len = monitorPtr.length; i < len; i += 1 ) {
var monId = monitorPtr[i]; var monId = monitorPtr[i];
if ( !monId ) continue; if ( !monId ) continue;
@ -1006,18 +1022,6 @@ function initPage() {
} }
} // end foreach monitor } // end foreach monitor
if ( !liveMode ) {
canvas = document.getElementById('timeline');
canvas.addEventListener('mousemove', mmove, false);
canvas.addEventListener('touchmove', tmove, false);
canvas.addEventListener('mousedown', mdown, false);
canvas.addEventListener('mouseup', mup, false);
canvas.addEventListener('mouseout', mout, false);
ctx = canvas.getContext('2d');
drawGraph();
}
setSpeed(speedIndex); setSpeed(speedIndex);
//setFit(fitMode); // will redraw //setFit(fitMode); // will redraw
//setLive(liveMode); // will redraw //setLive(liveMode); // will redraw

View File

@ -239,6 +239,6 @@ echo "];\n";
var cWidth; // save canvas width var cWidth; // save canvas width
var cHeight; // save canvas height var cHeight; // save canvas height
var canvas; // global canvas definition so we don't have to keep looking it up var canvas; // global canvas definition so we don't have to keep looking it up
var ctx; var ctx = null;
var underSlider; // use this to hold what is hidden by the slider var underSlider; // use this to hold what is hidden by the slider
var underSliderX; // Where the above was taken from (left side, Y is zero) var underSliderX; // Where the above was taken from (left side, Y is zero)

View File

@ -44,7 +44,7 @@ function ajaxRequest(params) {
data.view = 'request'; data.view = 'request';
data.request = 'watch'; data.request = 'watch';
data.mid = monitorId; data.mid = monitorId;
if ( auth_hash ) data.auth = auth_hash; if (auth_hash) data.auth = auth_hash;
$j.getJSON(thisUrl, data) $j.getJSON(thisUrl, data)
.done(function(data) { .done(function(data) {
@ -94,8 +94,48 @@ function showPtzControls() {
showMode = 'control'; showMode = 'control';
} }
function changeSize() {
var width = $j('#width').val();
var height = $j('#height').val();
// Scale the frame
monitor_frame = $j('#imageFeed');
if (!monitor_frame) {
console.log('Error finding frame');
return;
}
if (width) monitor_frame.css('width', width);
if (height) monitor_frame.css('height', height);
var streamImg = document.getElementById('liveStream'+monitorData[monIdx].id);
if (streamImg) {
if (streamImg.nodeName == 'IMG') {
let src = streamImg.src;
streamImg.src = '';
src = src.replace(/width=[\.\d]+/i, 'width='+parseInt(width));
src = src.replace(/height=[\.\d]+/i, 'height='+parseInt(height));
src = src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.src = src;
}
streamImg.style.width = width ? width : null;
streamImg.style.height = height ? height : null;
} else {
console.log('Did not find liveStream'+monitorData[monIdx].id);
}
$j('#scale').val('');
setCookie('zmCycleScale', '', 3600);
setCookie('zmCycleWidth', width, 3600);
setCookie('zmCycleHeight', height, 3600);
} // end function changeSize()
function changeScale() { function changeScale() {
var scale = $j('#scale').val(); var scale = $j('#scale').val();
$j('#width').val('auto');
$j('#height').val('auto');
setCookie('zmCycleScale', scale, 3600);
setCookie('zmCycleWidth', 'auto', 3600);
setCookie('zmCycleHeight', 'auto', 3600);
var newWidth; var newWidth;
var newHeight; var newHeight;
var autoScale; var autoScale;
@ -104,7 +144,7 @@ function changeScale() {
// times and what the consequences would be // times and what the consequences would be
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active $j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
if (scale == '0' || scale == 'auto') { if (scale == '0' || scale == 'auto') {
var newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus')); const newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
newWidth = newSize.width; newWidth = newSize.width;
newHeight = newSize.height; newHeight = newSize.height;
autoScale = newSize.autoScale; autoScale = newSize.autoScale;
@ -118,16 +158,18 @@ function changeScale() {
var streamImg = $j('#liveStream'+monitorId); var streamImg = $j('#liveStream'+monitorId);
if (streamImg) { if (streamImg) {
var oldSrc = streamImg.attr('src'); if (streamImg.nodeName == 'IMG') {
const oldSrc = streamImg.attr('src');
streamImg.attr('src', ''); streamImg.attr('src', '');
// This is so that we don't waste bandwidth and let the browser do all the scaling. // This is so that we don't waste bandwidth and let the browser do all the scaling.
if (autoScale > 100) autoScale = 100; if (autoScale > 100) autoScale = 100;
if (scale > 100) scale = 100; if (scale > 100) scale = 100;
var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale)); const newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale));
streamImg.width(newWidth); streamImg.width(newWidth);
streamImg.height(newHeight); streamImg.height(newHeight);
streamImg.attr('src', newSrc); streamImg.attr('src', newSrc);
}
} else { } else {
console.error('No element found for liveStream'+monitorId); console.error('No element found for liveStream'+monitorId);
} }
@ -200,9 +242,15 @@ function getStreamCmdResponse(respObj, respText) {
// The get status command can get backed up, in which case we won't be able to get the semaphore and will exit. // The get status command can get backed up, in which case we won't be able to get the semaphore and will exit.
if (respObj.status) { if (respObj.status) {
streamStatus = respObj.status; streamStatus = respObj.status;
$j('#fpsValue').text(streamStatus.fps); if ($j('#viewingFPSValue').text() != streamStatus.fps) {
$j('#capturefpsValue').text(streamStatus.capturefps); $j('#viewingFPSValue').text(streamStatus.fps);
$j('#analysisfpsValue').text(streamStatus.analysisfps); }
if ($j('#captureFPSValue').text() != streamStatus.capturefps) {
$j('#captureFPSValue').text(streamStatus.capturefps);
}
if ($j('#analysisFPSValue').text() != streamStatus.analysisfps) {
$j('#analysisFPSValue').text(streamStatus.analysisfps);
}
setAlarmState(streamStatus.state); setAlarmState(streamStatus.state);
@ -287,31 +335,19 @@ function getStreamCmdResponse(respObj, respText) {
// Try to reload the image stream. // Try to reload the image stream.
var streamImg = $j('#liveStream'+monitorId); var streamImg = $j('#liveStream'+monitorId);
if (streamImg) { if (streamImg) {
var oldSrc = streamImg.attr('src'); const oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth); if (oldSrc) {
const newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
if (oldSrc != newSrc) { if (oldSrc != newSrc) {
streamImg.attr('src', newSrc); streamImg.attr('src', newSrc);
table.bootstrapTable('refresh'); table.bootstrapTable('refresh');
} }
} }
}
} // end if have a new auth hash } // end if have a new auth hash
} // end if respObj.status } // end if respObj.status
} else { } else {
checkStreamForErrors('getStreamCmdResponse', respObj);//log them checkStreamForErrors('getStreamCmdResponse', respObj);//log them
// Try to reload the image stream.
// If it's an auth error, we should reload the whole page.
console.log("have error");
//window.location.reload();
var streamImg = $j('#liveStream'+monitorId);
if (streamImg) {
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
streamImg.attr('src', newSrc);
console.log('Changing livestream src to ' + newSrc);
} else {
console.log('Unable to find streamImg liveStream');
}
} }
var streamCmdTimeout = statusRefreshTimeout; var streamCmdTimeout = statusRefreshTimeout;
@ -368,6 +404,7 @@ function streamCmdPlay(action) {
} }
function streamCmdReq(data) { function streamCmdReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=stream&connkey='+connKey, data) $j.getJSON(monitorUrl + '?view=request&request=stream&connkey='+connKey, data)
.done(getStreamCmdResponse) .done(getStreamCmdResponse)
.fail(getStreamCmdError); .fail(getStreamCmdError);
@ -386,10 +423,7 @@ function streamCmdStop(action) {
setButtonState('fastRevBtn', 'unavail'); setButtonState('fastRevBtn', 'unavail');
} }
if (action) { if (action) {
var data = {}; streamCmdReq({command: CMD_STOP});
if (auth_hash) data.auth = auth_hash;
data.command = CMD_STOP;
streamCmdReq(data);
} }
setButtonState('stopBtn', 'unavail'); setButtonState('stopBtn', 'unavail');
setButtonState('playBtn', 'active'); setButtonState('playBtn', 'active');
@ -407,7 +441,6 @@ function streamCmdFastFwd(action) {
} }
if (action) { if (action) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_FASTFWD; data.command = CMD_FASTFWD;
streamCmdReq(data); streamCmdReq(data);
} }
@ -425,7 +458,6 @@ function streamCmdSlowFwd(action) {
} }
if (action) { if (action) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SLOWFWD; data.command = CMD_SLOWFWD;
streamCmdReq(data); streamCmdReq(data);
} }
@ -447,7 +479,6 @@ function streamCmdSlowRev(action) {
} }
if (action) { if (action) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SLOWREV; data.command = CMD_SLOWREV;
streamCmdReq(data); streamCmdReq(data);
} }
@ -469,7 +500,6 @@ function streamCmdFastRev(action) {
} }
if (action) { if (action) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_FASTREV; data.command = CMD_FASTREV;
streamCmdReq(data); streamCmdReq(data);
} }
@ -477,7 +507,6 @@ function streamCmdFastRev(action) {
function streamCmdZoomIn(x, y) { function streamCmdZoomIn(x, y) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.x = x; data.x = x;
data.y = y; data.y = y;
data.command = CMD_ZOOMIN; data.command = CMD_ZOOMIN;
@ -486,14 +515,12 @@ function streamCmdZoomIn(x, y) {
function streamCmdZoomOut() { function streamCmdZoomOut() {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_ZOOMOUT; data.command = CMD_ZOOMOUT;
streamCmdReq(data); streamCmdReq(data);
} }
function streamCmdScale(scale) { function streamCmdScale(scale) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SCALE; data.command = CMD_SCALE;
data.scale = scale; data.scale = scale;
streamCmdReq(data); streamCmdReq(data);
@ -501,7 +528,6 @@ function streamCmdScale(scale) {
function streamCmdPan(x, y) { function streamCmdPan(x, y) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.x = x; data.x = x;
data.y = y; data.y = y;
data.command = CMD_PAN; data.command = CMD_PAN;
@ -510,11 +536,11 @@ function streamCmdPan(x, y) {
function streamCmdQuery() { function streamCmdQuery() {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_QUERY; data.command = CMD_QUERY;
streamCmdReq(data); streamCmdReq(data);
} }
/* getStatusCmd is used when not streaming, since there is no persistent zms */
function getStatusCmdResponse(respObj, respText) { function getStatusCmdResponse(respObj, respText) {
watchdogOk('status'); watchdogOk('status');
if (statusCmdTimer) { if (statusCmdTimer) {
@ -522,32 +548,33 @@ function getStatusCmdResponse(respObj, respText) {
} }
if (respObj.result == 'Ok') { if (respObj.result == 'Ok') {
$j('#fpsValue').text(respObj.monitor.FrameRate); $j('#captureFPSValue').text(respObj.monitor.FrameRate);
setAlarmState(respObj.monitor.Status); setAlarmState(respObj.monitor.Status);
} else { } else {
checkStreamForErrors('getStatusCmdResponse', respObj); checkStreamForErrors('getStatusCmdResponse', respObj);
} }
var statusCmdTimeout = statusRefreshTimeout; var statusCmdTimeout = statusRefreshTimeout;
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { if (alarmState == STATE_ALARM || alarmState == STATE_ALERT) {
statusCmdTimeout = statusCmdTimeout/5; statusCmdTimeout = statusCmdTimeout/5;
} }
statusCmdTimer = setTimeout(statusCmdQuery, statusCmdTimeout); statusCmdTimer = setTimeout(statusCmdQuery, statusCmdTimeout);
} }
function statusCmdQuery() { function statusCmdQuery() {
$j.getJSON(monitorUrl + '?view=request&request=status&entity=monitor&element[]=Status&element[]=FrameRate&id='+monitorId) $j.getJSON(monitorUrl + '?view=request&request=status&entity=monitor&element[]=Status&element[]=FrameRate&id='+monitorId+'&'+auth_relay)
.done(getStatusCmdResponse) .done(getStatusCmdResponse)
.fail(logAjaxFail); .fail(logAjaxFail);
streamCmdTimer = null; statusCmdTimer = null;
} }
function alarmCmdReq(data) { function alarmCmdReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=alarm&id='+monitorId, data) $j.getJSON(monitorUrl + '?view=request&request=alarm&id='+monitorId, data)
.done(getAlarmCmdResponse) .done(getAlarmCmdResponse)
.fail(function(jqxhr, textStatus, error) { .fail(function(jqxhr, textStatus, error) {
if ( textStatus === "timeout" ) { if (textStatus === 'timeout') {
streamCmdQuery(); streamCmdQuery();
} else { } else {
logAjaxFail(jqxhr, textStatus, error); logAjaxFail(jqxhr, textStatus, error);
@ -561,14 +588,12 @@ function getAlarmCmdResponse(respObj, respText) {
function cmdDisableAlarms() { function cmdDisableAlarms() {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'disableAlarms'; data.command = 'disableAlarms';
alarmCmdReq(data); alarmCmdReq(data);
} }
function cmdEnableAlarms() { function cmdEnableAlarms() {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'enableAlarms'; data.command = 'enableAlarms';
alarmCmdReq(data); alarmCmdReq(data);
} }
@ -583,7 +608,6 @@ function cmdAlarm() {
function cmdForceAlarm() { function cmdForceAlarm() {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'forceAlarm'; data.command = 'forceAlarm';
alarmCmdReq(data); alarmCmdReq(data);
if (window.event) window.event.preventDefault(); if (window.event) window.event.preventDefault();
@ -591,7 +615,6 @@ function cmdForceAlarm() {
function cmdCancelForcedAlarm() { function cmdCancelForcedAlarm() {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'cancelForcedAlarm'; data.command = 'cancelForcedAlarm';
alarmCmdReq(data); alarmCmdReq(data);
if (window.event) window.event.preventDefault(); if (window.event) window.event.preventDefault();
@ -607,6 +630,7 @@ function cmdForce() {
} }
function controlReq(data) { function controlReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=control&id='+monitorId, data) $j.getJSON(monitorUrl + '?view=request&request=control&id='+monitorId, data)
.done(getControlResponse) .done(getControlResponse)
.fail(logAjaxFail); .fail(logAjaxFail);
@ -639,16 +663,16 @@ function controlCmd(event) {
var data = {}; var data = {};
if (event && (xtell || ytell)) { if (event && (xtell || ytell)) {
var target = event.target; const target = event.target;
var offset = $j(target).offset(); const offset = $j(target).offset();
var width = $j(target).width(); const width = $j(target).width();
var height = $j(target).height(); const height = $j(target).height();
var x = event.pageX - offset.left; const x = event.pageX - offset.left;
var y = event.pageY - offset.top; const y = event.pageY - offset.top;
if (xtell) { if (xtell) {
var xge = parseInt((x*100)/width); let xge = parseInt((x*100)/width);
if (xtell == -1) { if (xtell == -1) {
xge = 100 - xge; xge = 100 - xge;
} else if (xtell == 2) { } else if (xtell == 2) {
@ -657,7 +681,7 @@ function controlCmd(event) {
data.xge = xge; data.xge = xge;
} }
if (ytell) { if (ytell) {
var yge = parseInt((y*100)/height); let yge = parseInt((y*100)/height);
if (ytell == -1) { if (ytell == -1) {
yge = 100 - yge; yge = 100 - yge;
} else if (ytell == 2) { } else if (ytell == 2) {
@ -667,7 +691,6 @@ function controlCmd(event) {
} }
} }
if (auth_hash) data.auth = auth_hash;
data.control = control; data.control = control;
controlReq(data); controlReq(data);
@ -678,7 +701,6 @@ function controlCmd(event) {
function controlCmdImage(x, y) { function controlCmdImage(x, y) {
var data = {}; var data = {};
if (auth_hash) data.auth = auth_hash;
data.scale = scale; data.scale = scale;
data.control = imageControlMode; data.control = imageControlMode;
data.x = x; data.x = x;
@ -768,7 +790,6 @@ function reloadWebSite() {
function updatePresetLabels() { function updatePresetLabels() {
var lblNdx = $j('#ctrlPresetForm option:selected').val(); var lblNdx = $j('#ctrlPresetForm option:selected').val();
$j('#newLabel').val(labels[lblNdx]); $j('#newLabel').val(labels[lblNdx]);
} }
@ -878,7 +899,7 @@ function initPage() {
// Load the PTZ Preset modal into the DOM // Load the PTZ Preset modal into the DOM
if (monitorControllable) getCtrlPresetModal(); if (monitorControllable) getCtrlPresetModal();
// Load the settings modal into the DOM // Load the settings modal into the DOM
if (monitorType == "Local") getSettingsModal(); if (monitorType == 'Local') getSettingsModal();
} }
if (monitorType != 'WebSite') { if (monitorType != 'WebSite') {
@ -886,15 +907,12 @@ function initPage() {
statusCmdTimer = setTimeout(statusCmdQuery, (Math.random()+0.1)*statusRefreshTimeout); statusCmdTimer = setTimeout(statusCmdQuery, (Math.random()+0.1)*statusRefreshTimeout);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status'); setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
} else { } else {
streamCmdTimer = setTimeout(streamCmdQuery, (Math.random()+0.1)*statusRefreshTimeout);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream'); setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
} }
if (canStreamNative || (streamMode == 'single')) { if (canStreamNative || (streamMode == 'single')) {
var streamImg = $j('#imageFeed img'); var streamImg = $j('#imageFeed img');
if (!streamImg) { if (!streamImg) streamImg = $j('#imageFeed object');
streamImg = $j('#imageFeed object');
}
if (!streamImg) { if (!streamImg) {
console.error('No streamImg found for imageFeed'); console.error('No streamImg found for imageFeed');
} else { } else {
@ -905,6 +923,10 @@ function initPage() {
streamImg.click(function(event) { streamImg.click(function(event) {
handleClick(event); handleClick(event);
}); });
streamImg.on("error", function(thing) {
console.log("Error loading image");
console.log(thing);
});
} }
} // end if have streamImg } // end if have streamImg
} // streamMode native or single } // streamMode native or single
@ -944,6 +966,18 @@ function initPage() {
$j('#settingsModal').modal('show'); $j('#settingsModal').modal('show');
}); });
bindButton('#cyclePlayBtn', 'click', null, cycleStart);
bindButton('#cyclePauseBtn', 'click', null, cyclePause);
bindButton('#cycleNextBtn', 'click', null, cycleNext);
bindButton('#cyclePrevBtn', 'click', null, cyclePrev);
bindButton('#cycleToggle', 'click', null, cycleToggle);
bindButton('#cyclePeriod', 'change', null, cyclePeriodChange);
if (cycle) {
cycleStart();
} else {
cyclePause();
}
// Only enable the settings button for local cameras // Only enable the settings button for local cameras
settingsBtn.prop('disabled', !(canView.Control && (monitorType == 'Local'))); settingsBtn.prop('disabled', !(canView.Control && (monitorType == 'Local')));
@ -972,7 +1006,6 @@ function initPage() {
function watchFullscreen() { function watchFullscreen() {
const btn = document.getElementById('fullscreenBtn'); const btn = document.getElementById('fullscreenBtn');
console.log(btn);
if (btn.firstElementChild.innerHTML=='fullscreen') { if (btn.firstElementChild.innerHTML=='fullscreen') {
const content = document.getElementById('content'); const content = document.getElementById('content');
openFullscreen(content); openFullscreen(content);
@ -985,5 +1018,70 @@ function watchFullscreen() {
} }
} }
var intervalId;
var secondsToCycle = 0;
function nextCycleView() {
secondsToCycle --;
if (secondsToCycle<=0) {
window.location.replace('?view=watch&mid='+nextMid+'&mode='+mode+'&cycle=true');
}
$j('#secondsToCycle').text(secondsToCycle);
}
function cyclePause() {
clearInterval(intervalId);
$j('#cyclePauseBtn').hide();
$j('#cyclePlayBtn').show();
}
function cycleStart() {
secondsToCycle = $j('#cyclePeriod').val();
intervalId = setInterval(nextCycleView, 1000);
$j('#cyclePauseBtn').show();
$j('#cyclePlayBtn').hide();
}
function cycleNext() {
monIdx ++;
if (monIdx >= monitorData.length) {
monIdx = 0;
}
if (!monitorData[monIdx]) {
console.log('No monitorData for ' + monIdx);
}
window.location.replace('?view=watch&cycle=true&mid='+monitorData[monIdx].id+'&mode='+mode);
}
function cyclePrev() {
monIdx --;
if (monIdx < 0) {
monIdx = monitorData.length - 1;
}
if (!monitorData[monIdx]) {
console.log('No monitorData for ' + monIdx);
}
window.location.replace('?view=watch&cycle=true&mid='+monitorData[monIdx].id+'&mode='+mode);
}
function cyclePeriodChange() {
const cyclePeriodSelect = $j('#cyclePeriod');
secondsToCycle = cyclePeriodSelect.val();
setCookie('zmCyclePeriod', secondsToCycle, 3600);
}
function cycleToggle(e) {
sidebar = $j('#sidebar');
button = $j('#cycleToggle');
if (sidebar.is(":visible")) {
sidebar.hide();
setCookie('zmCycleShow', false, 3600);
} else {
sidebar.show();
setCookie('zmCycleShow', true, 3600);
}
button.toggleClass('btn-secondary');
button.toggleClass('btn-primary');
}
// Kick everything off // Kick everything off
$j(document).ready(initPage); $j(document).ready(initPage);

View File

@ -1,33 +1,20 @@
<?php <?php
global $monIdx;
global $nextMid;
global $options;
global $monitors;
global $streamMode; global $streamMode;
global $showPtzControls; global $showPtzControls;
global $connkey; global $connkey;
global $monitor; global $monitor;
global $scale; global $scale;
global $labels; global $labels;
global $cycle;
?> ?>
// //
// Import constants // Import constants
// //
var STATE_IDLE = <?php echo STATE_IDLE ?>;
var STATE_PREALARM = <?php echo STATE_PREALARM ?>;
var STATE_ALARM = <?php echo STATE_ALARM ?>;
var STATE_ALERT = <?php echo STATE_ALERT ?>;
var STATE_TAPE = <?php echo STATE_TAPE ?>;
var stateStrings = new Array();
stateStrings[STATE_IDLE] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_PREALARM] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_ALARM] = "<?php echo translate('Alarm') ?>";
stateStrings[STATE_ALERT] = "<?php echo translate('Alert') ?>";
stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";
var deleteString = "<?php echo translate('Delete') ?>";
var enableAlarmsStr = "<?php echo translate('EnableAlarms') ?>";
var disableAlarmsStr = "<?php echo translate('DisableAlarms') ?>";
var forceAlarmStr = "<?php echo translate('ForceAlarm') ?>";
var cancelForcedAlarmStr = "<?php echo translate('CancelForcedAlarm') ?>";
var CMD_NONE = <?php echo CMD_NONE ?>; var CMD_NONE = <?php echo CMD_NONE ?>;
var CMD_PAUSE = <?php echo CMD_PAUSE ?>; var CMD_PAUSE = <?php echo CMD_PAUSE ?>;
@ -46,14 +33,13 @@ var CMD_NEXT = <?php echo CMD_NEXT ?>;
var CMD_SEEK = <?php echo CMD_SEEK ?>; var CMD_SEEK = <?php echo CMD_SEEK ?>;
var CMD_QUERY = <?php echo CMD_QUERY ?>; var CMD_QUERY = <?php echo CMD_QUERY ?>;
var SCALE_BASE = <?php echo SCALE_BASE ?>;
var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_ON_ALARM ?>; var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_ON_ALARM ?>;
var POPUP_ON_ALARM = <?php echo ZM_WEB_POPUP_ON_ALARM ?>; var POPUP_ON_ALARM = <?php echo ZM_WEB_POPUP_ON_ALARM ?>;
var LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>; var LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
var streamMode = "<?php echo $streamMode ?>"; var streamMode = "<?php echo $streamMode ?>";
var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>"; var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>";
var cycle = <?php echo $cycle ? 'true' : 'false' ?>;
var connKey = '<?php echo $connkey ?>'; var connKey = '<?php echo $connkey ?>';
var maxDisplayEvents = <?php echo 2 * MAX_EVENTS ?>; var maxDisplayEvents = <?php echo 2 * MAX_EVENTS ?>;
@ -67,6 +53,28 @@ var monitorRefresh = '<?php echo $monitor->Refresh() ?>';
var monitorStreamReplayBuffer = <?php echo $monitor->StreamReplayBuffer() ?>; var monitorStreamReplayBuffer = <?php echo $monitor->StreamReplayBuffer() ?>;
var monitorControllable = <?php echo $monitor->Controllable()?'true':'false' ?>; var monitorControllable = <?php echo $monitor->Controllable()?'true':'false' ?>;
var monIdx = '<?php echo $monIdx; ?>';
var nextMid = "<?php echo isset($nextMid)?$nextMid:'' ?>";
var mode = "<?php echo $options['mode'] ?>";
var monitorData = new Array();
<?php
foreach ($monitors as $monitor) {
?>
monitorData[monitorData.length] = {
'id': <?php echo $monitor->Id() ?>,
'width': <?php echo $monitor->ViewWidth() ?>,
'height':<?php echo $monitor->ViewHeight() ?>,
'url': '<?php echo $monitor->UrlToIndex() ?>',
'onclick': function(){window.location.assign( '?view=watch&mid=<?php echo $monitor->Id() ?>' );},
'type': '<?php echo $monitor->Type() ?>',
'refresh': '<?php echo $monitor->Refresh() ?>'
};
<?php
} // end foreach monitor
?>
var SCALE_BASE = <?php echo SCALE_BASE ?>;
var scale = '<?php echo $scale ?>'; var scale = '<?php echo $scale ?>';
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>; var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
@ -75,17 +83,16 @@ var imageRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_IMAGE ?>;
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>; var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
<?php var imageControlMode = '<?php
$control = $monitor->Control(); $control = $monitor->Control();
if ( $control->CanMoveMap() ) { ?> if ($control->CanMoveMap()) {
var imageControlMode = "moveMap"; echo 'moveMap';
<?php } elseif ( $control->CanMoveRel() ) { ?> } else if ($control->CanMoveRel()) {
var imageControlMode = "movePseudoMap"; echo 'movePseudoMap';
<?php } elseif ( $control->CanMoveCon() ) { ?> } else if ($control->CanMoveCon()) {
var imageControlMode = "moveConMap"; echo 'moveConMap';
<?php } else { ?> }
var imageControlMode = null; ?>';
<?php } ?>
var refreshApplet = <?php echo (canStreamApplet() && $streamMode == "jpeg")?'true':'false' ?>; var refreshApplet = <?php echo (canStreamApplet() && $streamMode == "jpeg")?'true':'false' ?>;
var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>; var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
@ -93,17 +100,18 @@ var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
var labels = new Array(); var labels = new Array();
<?php <?php
$labels = array(); $labels = array();
foreach( dbFetchAll( 'SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array( $monitor->Id() ) ) as $row ) { foreach (dbFetchAll('SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array($monitor->Id())) as $row) {
$labels[$row['Preset']] = $row['Label']; $label = $labels[$row['Preset']] = $row['Label'];
} echo 'labels['. validInt($index) .'] = '.validJsStr($label).'\'';
foreach ($labels as $index=>$label) {
?>
labels[<?php echo validInt($index) ?>] = '<?php echo validJsStr($label) ?>';
<?php
} }
?> ?>
var deleteString = "<?php echo translate('Delete') ?>";
var enableAlarmsStr = "<?php echo translate('EnableAlarms') ?>";
var disableAlarmsStr = "<?php echo translate('DisableAlarms') ?>";
var forceAlarmStr = "<?php echo translate('ForceAlarm') ?>";
var cancelForcedAlarmStr = "<?php echo translate('CancelForcedAlarm') ?>";
var translate = { var translate = {
"seconds": "<?php echo translate('seconds') ?>",
"Fullscreen": "<?php echo translate('Fullscreen') ?>", "Fullscreen": "<?php echo translate('Fullscreen') ?>",
"Exit Fullscreen": "<?php echo translate('Exit Fullscreen') ?>", "Exit Fullscreen": "<?php echo translate('Exit Fullscreen') ?>",
}; };

View File

@ -94,20 +94,6 @@ var deleteString = "<?php echo translate('Delete') ?>";
// Imported from watch.js.php and modified for new zone edit view // Imported from watch.js.php and modified for new zone edit view
// //
var STATE_IDLE = <?php echo STATE_IDLE ?>;
var STATE_PREALARM = <?php echo STATE_PREALARM ?>;
var STATE_ALARM = <?php echo STATE_ALARM ?>;
var STATE_ALERT = <?php echo STATE_ALERT ?>;
var STATE_TAPE = <?php echo STATE_TAPE ?>;
var stateStrings = new Array();
stateStrings[STATE_IDLE] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_PREALARM] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_ALARM] = "<?php echo translate('Alarm') ?>";
stateStrings[STATE_ALERT] = "<?php echo translate('Alert') ?>";
stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";
var CMD_PAUSE = <?php echo CMD_PAUSE ?>; var CMD_PAUSE = <?php echo CMD_PAUSE ?>;
var CMD_PLAY = <?php echo CMD_PLAY ?>; var CMD_PLAY = <?php echo CMD_PLAY ?>;
var CMD_STOP = <?php echo CMD_STOP ?>; var CMD_STOP = <?php echo CMD_STOP ?>;

View File

@ -17,25 +17,10 @@ monitorData[monitorData.length] = {
} }
?> ?>
var STATE_IDLE = <?php echo STATE_IDLE ?>;
var STATE_PREALARM = <?php echo STATE_PREALARM ?>;
var STATE_ALARM = <?php echo STATE_ALARM ?>;
var STATE_ALERT = <?php echo STATE_ALERT ?>;
var STATE_TAPE = <?php echo STATE_TAPE ?>;
var stateStrings = new Array();
stateStrings[STATE_IDLE] = "<?php echo translate('Idle') ?>";
stateStrings[STATE_PREALARM] = "<?php echo translate('PreAlarm') ?>";
stateStrings[STATE_ALARM] = "<?php echo translate('Alarm') ?>";
stateStrings[STATE_ALERT] = "<?php echo translate('Alert') ?>";
stateStrings[STATE_TAPE] = "<?php echo translate('Record') ?>";
var CMD_PAUSE = <?php echo CMD_PAUSE ?>; var CMD_PAUSE = <?php echo CMD_PAUSE ?>;
var CMD_PLAY = <?php echo CMD_PLAY ?>; var CMD_PLAY = <?php echo CMD_PLAY ?>;
var CMD_STOP = <?php echo CMD_STOP ?>; var CMD_STOP = <?php echo CMD_STOP ?>;
var CMD_QUERY = <?php echo CMD_QUERY ?>; var CMD_QUERY = <?php echo CMD_QUERY ?>;
var CMD_QUIT = <?php echo CMD_QUIT ?>; var CMD_QUIT = <?php echo CMD_QUIT ?>;
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>; var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;

View File

@ -453,7 +453,7 @@ foreach ( $tabs as $name=>$value ) {
switch ( $name ) { switch ( $name ) {
case 'general' : case 'general' :
{ {
if (!$monitor->Id()) { if (!$monitor->Id() and count($monitors)) {
$monitor_ids = array(); $monitor_ids = array();
foreach ($monitors as $m) { $monitor_ids[] = $m['Id']; } foreach ($monitors as $m) { $monitor_ids[] = $m['Id']; }
$available_monitor_ids = array_diff(range(min($monitor_ids),max($monitor_ids)), $monitor_ids); $available_monitor_ids = array_diff(range(min($monitor_ids),max($monitor_ids)), $monitor_ids);
@ -470,7 +470,7 @@ if (count($available_monitor_ids)) {
</tr> </tr>
<?php <?php
} # end if ! $monitor->Id() } # end if ! $monitor->Id() and count($monitors)
?> ?>
<tr class="Name"> <tr class="Name">
<td class="text-right pr-3"><?php echo translate('Name') ?></td> <td class="text-right pr-3"><?php echo translate('Name') ?></td>
@ -675,6 +675,14 @@ if (count($available_monitor_ids)) {
} }
?> ?>
</td></tr> </td></tr>
<tr>
<td class="text-right pr-3"><?php echo translate('Event Start Command') ?></td>
<td><input type="text" name="newMonitor[EventStartCommand]" value="<?php echo validHtmlStr($monitor->EventStartCommand()) ?>" /></td>
</tr>
<tr>
<td class="text-right pr-3"><?php echo translate('Event End Command') ?></td>
<td><input type="text" name="newMonitor[EventEndCommand]" value="<?php echo validHtmlStr($monitor->EventEndCommand()) ?>" /></td>
</tr>
<?php <?php
} }
break; break;

View File

@ -24,6 +24,7 @@ if ( !canView('Stream') ) {
} }
require_once('includes/MontageLayout.php'); require_once('includes/MontageLayout.php');
require_once('includes/Zone.php');
$showControl = false; $showControl = false;
$showZones = false; $showZones = false;
@ -49,7 +50,6 @@ $heights = array(
'1080' => '1080px', '1080' => '1080px',
); );
$layouts = ZM\MontageLayout::find(NULL, array('order'=>"lower('Name')")); $layouts = ZM\MontageLayout::find(NULL, array('order'=>"lower('Name')"));
$layoutsById = array(); $layoutsById = array();
foreach ( $layouts as $l ) { foreach ( $layouts as $l ) {
@ -278,27 +278,25 @@ foreach ( $monitors as $monitor ) {
} }
$zones = array(); $zones = array();
foreach( dbFetchAll('SELECT * FROM Zones WHERE MonitorId=? ORDER BY Area DESC', NULL, array($monitor->Id()) ) as $row ) { foreach ( ZM\Zone::find(array('MonitorId'=>$monitor->Id()), array('order'=>'Area DESC')) as $row ) {
$row['Points'] = coordsToPoints($row['Coords']); $points = $row->Points();
if ($scale) {
if ( $scale ) { limitPoints($points, 0, 0, $monitor->Width(), $monitor->Height());
limitPoints($row['Points'], 0, 0, $monitor->Width(), $monitor->Height());
} else { } else {
limitPoints($row['Points'], 0, 0, limitPoints($points, 0, 0,
( $width ? $width-1 : $monitor->ViewWidth()-1 ), ( $width ? $width-1 : $monitor->ViewWidth()-1 ),
( $height ? $height-1 : $monitor->ViewHeight()-1 ) ( $height ? $height-1 : $monitor->ViewHeight()-1 )
); );
} }
$row['Coords'] = pointsToCoords($row['Points']); $row->Coords(pointsToCoords($points));
$row['AreaCoords'] = preg_replace('/\s+/', ',', $row['Coords']);
$zones[] = $row; $zones[] = $row;
} // end foreach Zone } // end foreach Zone
?> ?>
<svg class="zones" id="zones<?php echo $monitor->Id() ?>" style="position:absolute; top: 0; left: 0; background: none; width: 100%; height: 100%;" viewBox="0 0 <?php echo $monitor->ViewWidth() ?> <?php echo $monitor->ViewHeight() ?>" preserveAspectRatio="none"> <svg class="zones" id="zones<?php echo $monitor->Id() ?>" style="position:absolute; top: 0; left: 0; background: none; width: 100%; height: 100%;" viewBox="0 0 <?php echo $monitor->ViewWidth().' '.$monitor->ViewHeight() ?>" preserveAspectRatio="none">
<?php <?php
foreach ( array_reverse($zones) as $zone ) { foreach (array_reverse($zones) as $zone) {
echo '<polygon points="'. $zone['AreaCoords'] .'" class="'. $zone['Type'].'" />'; echo $zone->svg_polygon();
} // end foreach zone } // end foreach zone
?> ?>
Sorry, your browser does not support inline SVG Sorry, your browser does not support inline SVG
@ -308,9 +306,14 @@ foreach ( array_reverse($zones) as $zone ) {
?> ?>
</div> </div>
<?php <?php
if ( (!ZM_WEB_COMPACT_MONTAGE) && ($monitor->Type() != 'WebSite') ) { if ((!ZM_WEB_COMPACT_MONTAGE) && ($monitor->Type() != 'WebSite')) {
?> ?>
<div id="monitorState<?php echo $monitor->Id() ?>" class="monitorState idle"><?php echo translate('State') ?>:&nbsp;<span id="stateValue<?php echo $monitor->Id() ?>"></span>&nbsp;-&nbsp;<span id="fpsValue<?php echo $monitor->Id() ?>"></span>&nbsp;fps</div> <div id="monitorState<?php echo $monitor->Id() ?>" class="monitorState idle">
<?php echo translate('State') ?>:
<span id="stateValue<?php echo $monitor->Id() ?>"></span>
&nbsp;-&nbsp;
<span id="fpsValue<?php echo $monitor->Id() ?>"></span>&nbsp;fps
</div>
<?php <?php
} }
?> ?>

View File

@ -18,99 +18,258 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( !canView('Stream') ) { if (!canView('Stream')) {
$view = 'error'; $view = 'error';
return; return;
} }
require_once('includes/Monitor.php');
ob_start();
include('_monitor_filters.php');
$filterbar = ob_get_contents();
ob_end_clean();
if ( !isset($_REQUEST['mid']) ) {
$view = 'error';
return;
}
// This is for input sanitation // This is for input sanitation
$mid = intval($_REQUEST['mid']); $mid = isset($_REQUEST['mid']) ? intval($_REQUEST['mid']) : 0;
if ( !visibleMonitor($mid) ) {
$widths = array(
'auto' => translate('auto'),
'100%' => '100%',
'160px' => '160px',
'320px' => '320px',
'352px' => '352px',
'640px' => '640px',
'1280px' => '1280px',
'1920px' => '1920px'
);
$heights = array(
'auto' => translate('auto'),
'240px' => '240px',
'480px' => '480px',
'720px' => '720px',
'1080px' => '1080px',
);
$monitors = array();
$monitor_index = 0;
foreach ($displayMonitors as &$row) {
if ($row['Function'] == 'None') continue;
if ($mid and ($row['Id'] == $mid)) $monitor_index = count($monitors);
$monitors[] = new ZM\Monitor($row);
if (!isset($widths[$row['Width'].'px'])) {
$widths[$row['Width'].'px'] = $row['Width'].'px';
}
if (!isset($heights[$row['Height'].'px'])) {
$heights[$row['Height'].'px'] = $row['Height'].'px';
}
unset($row);
} # end foreach Monitor
if (!$mid) {
$mid = $monitors[0]->Id();
$monitor_index = 0;
}
if (!visibleMonitor($mid)) {
$view = 'error'; $view = 'error';
return; return;
} }
require_once('includes/Monitor.php');
$monitor = new ZM\Monitor($mid); $monitor = new ZM\Monitor($mid);
$nextMid = ($monitor_index == count($monitors)-1) ? $monitors[0]->Id() : $monitors[$monitor_index+1]->Id();
$cycle = isset($_REQUEST['cycle']) and ($_REQUEST['cycle'] == 'true');
$showCycle = $cycle;
ZM\Error("Show cycle: $showCycle");
if (isset($_COOKIE['zmCycleShow'])) {
$showCycle = $_COOKIE['zmCycleShow'] == 'true';
ZM\Error("Show cycle: $showCycle");
} else {
ZM\Error("Show cycle: not set");
}
#Whether to show the controls button #Whether to show the controls button
$showPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' ); $showPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' );
if ( isset($_REQUEST['scale']) ) { $options = array();
if (empty($_REQUEST['mode'])) {
$options['mode'] = canStream() ? 'stream' : 'still';
} else {
$options['mode'] = validHtmlStr($_REQUEST['mode']);
}
zm_session_start();
$period = ZM_WEB_REFRESH_CYCLE;
if (isset($_REQUEST['period'])) {
$period = validInt($_REQUEST['period']);
} else if (isset($_COOKIE['zmCyclePeriod'])) {
$period = validInt($_COOKIE['zmCyclePeriod']);
}
if (isset($_REQUEST['scale'])) {
$scale = validInt($_REQUEST['scale']); $scale = validInt($_REQUEST['scale']);
} else if ( isset($_COOKIE['zmWatchScale'.$mid]) ) { } else if ( isset($_COOKIE['zmWatchScale'.$mid]) ) {
$scale = $_COOKIE['zmWatchScale'.$mid]; $scale = $_COOKIE['zmWatchScale'.$mid];
} else { } else {
$scale = $monitor->DefaultScale(); $scale = $monitor->DefaultScale();
} }
$options['scale'] = $scale;
if (isset($_REQUEST['width'])) {
$options['width'] = validInt($_REQUEST['width']);
} else if ( isset($_COOKIE['zmCycleWidth']) and $_COOKIE['zmCycleWidth'] ) {
$_SESSION['zmCycleWidth'] = $options['width'] = $_COOKIE['zmCycleWidth'];
#} elseif ( isset($_SESSION['zmCycleWidth']) and $_SESSION['zmCycleWidth'] ) {
#$options['width'] = $_SESSION['zmCycleWidth'];
} else {
$options['width'] = '';
}
if (isset($_REQUEST['height'])) {
$options['height'] =validInt($_REQUEST['height']);
} else if (isset($_COOKIE['zmCycleHeight']) and $_COOKIE['zmCycleHeight']) {
$_SESSION['zmCycleHeight'] = $options['height'] = $_COOKIE['zmCycleHeight'];
#else if ( isset($_SESSION['zmCycleHeight']) and $_SESSION['zmCycleHeight'] )
#$options['height'] = $_SESSION['zmCycleHeight'];
} else {
$options['height'] = '';
}
session_write_close();
$connkey = generateConnKey(); $connkey = generateConnKey();
$streamMode = getStreamMode(); $streamMode = getStreamMode();
$popup = ((isset($_REQUEST['popup'])) && ($_REQUEST['popup'] == 1));
noCacheHeaders(); noCacheHeaders();
xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed'));
?> ?>
<body> <body>
<?php echo getNavBarHTML() ?> <?php echo getNavBarHTML() ?>
<div id="header">
<div class="controlHeader">
<form method="get">
<input type="hidden" name="view" value="watch"/>
<?php echo $filterbar ?>
</form>
</div>
<div class="d-flex flex-row justify-content-between px-3 py-1"> <div class="d-flex flex-row justify-content-between px-3 py-1">
<div> <div id="navButtons">
<button type="button" id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button> <button type="button" id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<button type="button" id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button> <button type="button" id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
<button type="button" id="settingsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Settings') ?>" disabled><i class="fa fa-sliders"></i></button> <button type="button" id="settingsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Settings') ?>" disabled><i class="fa fa-sliders"></i></button>
<button type="button" id="enableAlmBtn" class="btn btn-normal" data-on-click="cmdAlarm" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DisableAlarms') ?>" disabled><i class="fa fa-bell"></i></button> <button type="button" id="enableAlmBtn" class="btn btn-normal" data-on-click="cmdAlarm" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DisableAlarms') ?>" disabled><i class="fa fa-bell"></i></button>
<button type="button" id="forceAlmBtn" class="btn btn-danger" data-on-click="cmdForce" data-toggle="tooltip" data-placement="top" title="<?php echo translate('ForceAlarm') ?>" disabled><i class="fa fa-exclamation-circle"></i></button> <button type="button" id="forceAlmBtn" class="btn btn-danger" data-on-click="cmdForce" data-toggle="tooltip" data-placement="top" title="<?php echo translate('ForceAlarm') ?>" disabled><i class="fa fa-exclamation-circle"></i></button>
</div> </div>
<div id="headerButtons">
<div> <!--
<h2><?php echo makeLink('?view=monitor&amp;mid='.$monitor->Id(), validHtmlStr($monitor->Name()), canEdit('Monitors')) ?></h2> <?php if ( $options['mode'] == 'stream' ) { ?>
</div> <a href="?view=<?php echo $view ?>&amp;mode=still&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stills') ?></a>
<?php } else { ?>
<div> <a href="?view=<?php echo $view ?>&amp;mode=stream&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stream') ?></a>
<?php echo translate('Scale').': '.htmlSelect('scale', $scales, $scale, array('id'=>'scale')); ?> <?php } ?>
</div> -->
<button type="button" id="cycleToggle" class="btn <?php echo $showCycle ? 'btn-primary':'btn-secondary'?>" title="<?php echo translate('Toggle cycle sidebar')?>">
<span class="material-icons md-18">view_carousel</span>
</button>
</div> </div>
<div id="sizeControl">
<span id="widthControl">
<label><?php echo translate('Width') ?>:</label>
<?php echo htmlSelect('width', $widths, $options['width'], array('id'=>'width', 'data-on-change-this'=>'changeSize') ); ?>
</span>
<span id="heightControl">
<label><?php echo translate('Height') ?>:</label>
<?php echo htmlSelect('height', $heights, $options['height'], array('id'=>'height', 'data-on-change-this'=>'changeSize') ); ?>
</span>
<span id="scaleControl">
<label><?php echo translate('Scale') ?>:</label>
<?php echo htmlSelect('scale', $scales, $options['scale'], array('id'=>'scale', 'data-on-change-this'=>'changeScale') ); ?>
</span>
</div><!--sizeControl-->
</div><!--control header-->
</div><!--header-->
<?php <?php
if ( $monitor->Status() != 'Connected' and $monitor->Type() != 'WebSite' ) { if ( $monitor->Status() != 'Connected' and $monitor->Type() != 'WebSite' ) {
echo '<div class="warning">Monitor is not capturing. We will be unable to provide an image</div>'; echo '<div class="warning">Monitor is not capturing. We will be unable to provide an image</div>';
} }
?> ?>
<div id="content"> <div class="container-fluid h-100">
<div class="row flex-nowrap h-100" id="content">
<nav id="sidebar" class="h-100"<?php echo $showCycle?'':' style="display:none;"'?>>
<div id="cycleButtons" class="buttons">
<?php
$seconds = translate('seconds');
$minute = translate('minute');
$minutes = translate('minutes');
$cyclePeriodOptions = array(
10 => '10 '.$seconds,
30 => '30 '.$seconds,
60 => '1 '.$minute,
120 => '2 '.$minutes,
300 => '5 '.$minutes,
);
if (!isset($cyclePeriodOptions[ZM_WEB_REFRESH_CYCLE])) {
$cyclePeriodOptions[ZM_WEB_REFRESH_CYCLE] = ZM_WEB_REFRESH_CYCLE.' '.$seconds;
}
echo htmlSelect('cyclePeriod', $cyclePeriodOptions, $period, array('id'=>'cyclePeriod'));
?>
<span id="secondsToCycle"></span><br/>
<button type="button" id="cyclePrevBtn" title="<?php echo translate('PreviousMonitor') ?>">
<i class="material-icons md-18">skip_previous</i>
</button>
<button type="button" id="cyclePauseBtn" title="<?php echo translate('PauseCycle') ?>">
<i class="material-icons md-18">pause</i>
</button>
<button type="button" id="cyclePlayBtn" title="<?php echo translate('PlayCycle') ?>">
<i class="material-icons md-18">play_arrow</i>
</button>
<button type="button" id="cycleNextBtn" title="<?php echo translate('NextMonitor') ?>">
<i class="material-icons md-18">skip_next</i>
</button>
</div>
<ul class="nav nav-pills flex-column h-100">
<?php
foreach ($monitors as $m) {
echo '<li class="nav-item"><a class="nav-link'.( $m->Id() == $monitor->Id() ? ' active' : '' ).'" href="?view=watch&amp;mid='.$m->Id().'">'.$m->Name().'</a></li>';
}
?>
</ul>
</nav>
<div class="container-fluid col-sm-offset-2 h-100 pr-0">
<div id="imageFeed" <div id="imageFeed"
<?php <?php
if ( $streamMode == 'jpeg' ) { if ($streamMode == 'jpeg') {
echo 'title="Click to zoom, shift click to pan, ctrl click to zoom out"'; echo 'title="Click to zoom, shift click to pan, ctrl click to zoom out"';
} }
?> ?>
><?php echo getStreamHTML($monitor, array('scale'=>$scale)); ?></div> ><?php echo getStreamHTML($monitor, array('scale'=>$scale)); ?>
</div>
<?php if ($monitor->Type() != 'WebSite') { ?>
<?php if ( $monitor->Type() != 'WebSite' ) { ?>
<div id="monitorStatus"> <div id="monitorStatus">
<div id="monitorState"> <div id="monitorState">
<?php echo translate('State') ?>: <span><?php echo translate('State') ?>:<span id="stateValue"></span></span>
<span id="stateValue"></span> - <span id="viewingFPS" title="<?php echo translate('Viewing FPS')?>"><span id="viewingFPSValue"></span> fps</span>
<span title="<?php echo translate('Viewing FPS')?>"><span id="fpsValue"></span> fps</span> <span id="captureFPS" title="<?php echo translate('Capturing FPS')?>"><span id="captureFPSValue"></span> fps</span>
<span title="<?php echo translate('Capturing FPS')?>"><span id="capturefpsValue"></span> fps</span>
<?php if ( $monitor->Function() == 'Modect' or $monitor->Function() == 'Mocord' ) { ?> <?php if ( $monitor->Function() == 'Modect' or $monitor->Function() == 'Mocord' ) { ?>
<span title="<?php echo translate('Analysis FPS')?>"><span id="analysisfpsValue"></span> fps</span> <span id="analysisFPS" title="<?php echo translate('Analysis FPS')?>"><span id="analysisFPSValue"></span> fps</span>
<?php } ?> <?php } ?>
</div> </div>
</div> </div>
<div id="replayStatus"<?php echo $streamMode=="single" ? ' class="hidden"' : '' ?>>
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue"></span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"></span>x</span>
<span id="delay"><?php echo translate('Delay') ?>: <span id="delayValue"></span>s</span>
<span id="level"><?php echo translate('Buffer') ?>: <span id="levelValue"></span>%</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
</div>
<div class="buttons">
<div id="dvrControls"> <div id="dvrControls">
<?php <?php
if ( $streamMode == 'jpeg' ) { if ($streamMode == 'jpeg') {
if ( $monitor->StreamReplayBuffer() != 0 ) { if ($monitor->StreamReplayBuffer() != 0) {
?> ?>
<button type="button" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdFastRev"> <button type="button" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdFastRev">
<i class="material-icons md-18">fast_rewind</i> <i class="material-icons md-18">fast_rewind</i>
</button> </button>
<button type="button" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdSlowRev"> <button type="button" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdSlowRev">
<i class="material-icons md-18">chevron_right</i> <i class="material-icons md-18">chevron_right</i>
@ -119,22 +278,22 @@ if ( $streamMode == 'jpeg' ) {
} }
?> ?>
<button type="button" id="pauseBtn" title="<?php echo translate('Pause') ?>" class="inactive" data-on-click-true="streamCmdPause"> <button type="button" id="pauseBtn" title="<?php echo translate('Pause') ?>" class="inactive" data-on-click-true="streamCmdPause">
<i class="material-icons md-18">pause</i> <i class="material-icons md-18">pause</i>
</button> </button>
<button type="button" id="stopBtn" title="<?php echo translate('Stop') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdStop" style="display:none;"> <button type="button" id="stopBtn" title="<?php echo translate('Stop') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdStop" style="display:none;">
<i class="material-icons md-18">stop</i> <i class="material-icons md-18">stop</i>
</button> </button>
<button type="button" id="playBtn" title="<?php echo translate('Play') ?>" class="active" disabled="disabled" data-on-click-true="streamCmdPlay"> <button type="button" id="playBtn" title="<?php echo translate('Play') ?>" class="active" disabled="disabled" data-on-click-true="streamCmdPlay">
<i class="material-icons md-18">play_arrow</i> <i class="material-icons md-18">play_arrow</i>
</button> </button>
<?php <?php
if ( $monitor->StreamReplayBuffer() != 0 ) { if ($monitor->StreamReplayBuffer() != 0) {
?> ?>
<button type="button" id="slowFwdBtn" title="<?php echo translate('StepForward') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdSlowFwd"> <button type="button" id="slowFwdBtn" title="<?php echo translate('StepForward') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdSlowFwd">
<i class="material-icons md-18">chevron_right</i> <i class="material-icons md-18">chevron_right</i>
</button> </button>
<button type="button" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdFastFwd"> <button type="button" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="unavail" disabled="disabled" data-on-click-true="streamCmdFastFwd">
<i class="material-icons md-18">fast_forward</i> <i class="material-icons md-18">fast_forward</i>
</button> </button>
<?php <?php
} }
@ -148,13 +307,7 @@ if ( $streamMode == 'jpeg' ) {
<?php <?php
} // end if streamMode==jpeg } // end if streamMode==jpeg
?> ?>
</div> </div><!--dvrButtons-->
<div id="replayStatus"<?php echo $streamMode=="single" ? ' class="hidden"' : '' ?>>
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue"></span></span>
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"></span>x</span>
<span id="delay"><?php echo translate('Delay') ?>: <span id="delayValue"></span>s</span>
<span id="level"><?php echo translate('Buffer') ?>: <span id="levelValue"></span>%</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
</div> </div>
<?php } // end if $monitor->Type() != 'WebSite' ?> <?php } // end if $monitor->Type() != 'WebSite' ?>
<?php <?php
@ -208,6 +361,7 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
</table> </table>
</div> </div>
</div>
<?php <?php
} }
if ( ZM_WEB_SOUND_ON_ALARM ) { if ( ZM_WEB_SOUND_ON_ALARM ) {

View File

@ -37,7 +37,8 @@ ZM_WEB_GROUP=@WEB_GROUP@
ZM_DB_TYPE=@ZM_DB_TYPE@ ZM_DB_TYPE=@ZM_DB_TYPE@
# ZoneMinder database hostname or ip address and optionally port or unix socket # ZoneMinder database hostname or ip address and optionally port or unix socket
# Acceptable formats include hostname[:port], ip_address[:port], or localhost:unix_socket # Acceptable formats include hostname[:port], ip_address[:port], or
# localhost:/path/to/unix_socket
ZM_DB_HOST=@ZM_DB_HOST@ ZM_DB_HOST=@ZM_DB_HOST@
# ZoneMinder database name # ZoneMinder database name