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 to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
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 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
@ -407,21 +409,24 @@ else()
message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system")
endif()
# pcre (using find_library and find_path)
find_library(PCRE_LIBRARIES pcre)
if(PCRE_LIBRARIES)
set(HAVE_LIBPCRE 1)
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
find_path(PCRE_INCLUDE_DIR pcre.h)
if(PCRE_INCLUDE_DIR)
include_directories("${PCRE_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
# Do not check for cURL if ZM_NO_CURL is on
if(NOT ZM_NO_PRCE)
# pcre (using find_library and find_path)
find_library(PCRE_LIBRARIES pcre)
if(PCRE_LIBRARIES)
set(HAVE_LIBPCRE 1)
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
find_path(PCRE_INCLUDE_DIR pcre.h)
if(PCRE_INCLUDE_DIR)
include_directories("${PCRE_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
endif()
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
check_include_file("pcre.h" HAVE_PCRE_H)
set(optlibsfound "${optlibsfound} PCRE")
else()
set(optlibsnotfound "${optlibsnotfound} PCRE")
endif()
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
check_include_file("pcre.h" HAVE_PCRE_H)
set(optlibsfound "${optlibsfound} PCRE")
else()
set(optlibsnotfound "${optlibsnotfound} PCRE")
endif()
# mysqlclient (using find_library and find_path)
@ -540,6 +545,7 @@ set(ZM_PCRE 0)
if(HAVE_LIBPCRE AND HAVE_PCRE_H)
set(ZM_PCRE 1)
endif()
# Check for mmap and enable in all components
set(ZM_MEM_MAPPED 0)
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',
`LinkedMonitors` varchar(255),
`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_Username` 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
Name: zoneminder
Version: 1.37.3
Version: 1.37.5
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
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
,libturbojpeg0-dev
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
,libpcre3-dev
,libpolkit-gobject-1-dev
,libv4l-dev [!hurd-any]
,libvlc-dev
@ -70,7 +69,6 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,policykit-1
,rsyslog | system-log-daemon
,zip
,libpcre3
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
,libvncclient1|libvncclient0

View File

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

View File

@ -4,7 +4,7 @@ Debian
.. contents::
Easy Way: 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:
.. code-block::
::
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;
use Date::Format qw(time2str);
use Time::HiRes qw(gettimeofday tv_interval stat);
use Scalar::Util qw(looks_like_number);
#our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object);
@ -601,7 +602,7 @@ sub CopyTo {
# First determine if we can move it to the dest.
# We do this before bothering to lock the event
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.';
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
return 'Event is already located at ' . $NewPath;
@ -733,19 +734,22 @@ sub MoveTo {
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
$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 $error = $self->CopyTo($NewStorage);
return $error if $error;
if (!$error) {
# Succeeded in copying all files, so we may now update the Event.
$$self{StorageId} = $$NewStorage{Id};
$self->Storage($NewStorage);
$error .= $self->save();
# Succeeded in copying all files, so we may now update the Event.
$$self{StorageId} = $$NewStorage{Id};
$self->Storage($NewStorage);
$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;
return $error if $error;

View File

@ -230,8 +230,8 @@ sub Sql {
# PostCondition, so no further SQL
} else {
( 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' ) {
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
@ -250,7 +250,8 @@ sub Sql {
$$self{Server} = new ZoneMinder::Server($temp_value);
}
} 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);
} elsif ( $term->{attr} eq 'Name'
|| $term->{attr} eq 'Cause'

View File

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

View File

@ -326,17 +326,23 @@ sub resumeMotionDetection {
sub Control {
my $self = shift;
if ( ! exists $$self{Control}) {
if (!exists $$self{Control}) {
if ($$self{ControlId}) {
require ZoneMinder::Control;
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
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;
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$$Control{Protocol} => undef})) {
Error("Can't load ZoneMinder::Control::$$Control{Protocol}\n$Module::Load::Conditional::ERROR");
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$Protocol => undef})) {
Error("Can't load ZoneMinder::Control::$Protocol\n$Module::Load::Conditional::ERROR");
return undef;
}
bless $Control, 'ZoneMinder::Control::'.$$Control{Protocol};
bless $Control, 'ZoneMinder::Control::'.$Protocol;
$$Control{MonitorId} = $$self{Id};
$$self{Control} = $Control;
} else {

View File

@ -429,10 +429,20 @@ sub start {
# It's not running, or at least it's not been started by us
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
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 "
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}\n"
);
);
}
return;
}
@ -523,7 +533,7 @@ sub send_stop {
."\n"
);
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
return();
return ();
}
my $pid = $process->{pid};
@ -586,7 +596,7 @@ sub check_for_processes_to_kill {
sub stop {
my ( $daemon, @args ) = @_;
my $command = join(' ', $daemon, @args );
my $command = join(' ', $daemon, @args);
my $process = $cmd_hash{$command};
if ( !$process ) {
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 bytes;
@ -163,7 +141,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $event_id = 0;
if ( !EVENT_PATH ) {
if (!EVENT_PATH) {
Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS});
die;
}
@ -195,22 +173,22 @@ if ( ! ( $filter_name or $filter_id ) ) {
my @filters;
my $last_action = 0;
while( !$zm_terminate ) {
while (!$zm_terminate) {
my $now = time;
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
if (($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY}) {
Debug('Reloading filters');
$last_action = $now;
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
}
foreach my $filter ( @filters ) {
foreach my $filter (@filters) {
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 ( $id ) = $$filter{Id} =~ /(\d+)/;
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 {
checkFilter($filter);
}
@ -1051,9 +1029,7 @@ sub executeCommand {
my $filter = 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);
Info("Executing '$command'");
@ -1063,15 +1039,37 @@ sub executeCommand {
chomp($output);
Debug("Output: $output");
}
if ( $status ) {
if ($status) {
Error("Command '$command' exited with status: $status");
return 0;
} else {
my $sql = 'UPDATE `Events` SET `Executed` = 1 WHERE `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());
zmSQLExecute('UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?', $Event->{Id});
}
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) ) {
if ( vec($rout, $connection->fileno(), 1) ) {
Debug('Got input from spawned connection '
.$connection->name()
.' ('
.$connection->fileno()
.')'
);
.$connection->name().' ('.$connection->fileno().')');
my $messages = $connection->getMessages();
if ( defined($messages) ) {
if (defined($messages)) {
foreach my $message ( @$messages ) {
handleMessage($connection, $message);
}
@ -199,34 +195,32 @@ while (!$zm_terminate) {
# Check polled connections
foreach my $connection ( @in_poll_connections ) {
my $messages = $connection->getMessages();
if ( defined($messages) ) {
foreach my $message ( @$messages ) {
handleMessage($connection, $message);
}
if (defined($messages)) {
foreach my $message (@$messages) handleMessage($connection, $message);
}
}
# Check for alarms that might have happened
my @out_messages;
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.
# Don't need to zmMemInvalidate because the monitor reload will do it.
push @needsReload, $monitor;
next;
}
my ( $state, $last_event ) = zmMemRead( $monitor,
[
my ($state, $last_event) = zmMemRead($monitor, [
'shared_data:state',
'shared_data:last_event'
]
);
]);
#print( "$monitor->{Id}: S:$state, LE:$last_event" );
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
if ($state == STATE_ALARM or $state == STATE_ALERT) {
# In alarm state
if ( !defined($monitor->{LastEvent})
or ($last_event != $monitor->{LastEvent})

View File

@ -251,11 +251,11 @@ void zmDbQueue::process() {
mCondition.wait(lock);
}
while (!mQueue.empty()) {
if (mQueue.size() > 10) {
if (mQueue.size() > 20) {
Logger *log = Logger::fetch();
Logger::Level db_level = log->databaseLevel();
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);
}
std::string sql = mQueue.front();
@ -271,8 +271,10 @@ void zmDbQueue::process() {
void zmDbQueue::push(std::string &&sql) {
if (mTerminate) return;
std::unique_lock<std::mutex> lock(mMutex);
mQueue.push(std::move(sql));
{
std::unique_lock<std::mutex> lock(mMutex);
mQueue.push(std::move(sql));
}
mCondition.notify_all();
}

View File

@ -546,7 +546,7 @@ void Event::AddFrame(Image *image,
or
(frame_type == BULK)
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)",
frame_data.size(), write_to_db, fps, (frame_type == BULK));
WriteDbFrames();

View File

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

View File

@ -70,7 +70,7 @@
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
std::string load_monitor_sql =
"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
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
@ -435,7 +435,7 @@ Monitor::Monitor()
/*
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,"
"Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
"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
ReloadLinkedMonitors(dbrow[col]); col++;
event_start_command = dbrow[col] ? dbrow[col] : ""; col++;
event_end_command = dbrow[col] ? dbrow[col] : ""; col++;
/* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */
analysis_fps_limit = dbrow[col] ? strtod(dbrow[col], nullptr) : 0.0; col++;
@ -1873,19 +1875,27 @@ bool Monitor::Analyse() {
if (snap->image) {
// decoder may not have been able to provide an image
if (!ref_image.Buffer()) {
Debug(1, "Assigning instead of Dectecting");
Debug(1, "Assigning instead of Detecting");
ref_image.Assign(*(snap->image));
} else {
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
// Get new score.
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());
for (const Zone &zone : zones) {
const ZoneStats &stats = zone.GetStats();
stats.DumpToLog("After detect motion");
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)",
score, last_motion_score, motion_score);
@ -1911,6 +1921,7 @@ bool Monitor::Analyse() {
);
} // end if active and doing motion detection
if (function == RECORD or function == MOCORD) {
// If doing record, check to see if we need to close the event or not.
if (event) {
@ -1933,69 +1944,9 @@ bool Monitor::Analyse() {
} // end if event
if (!event) {
Debug(2, "Creating continuous event");
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 */
);
event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
// 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");
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",
Info("%s: %03d - Opened new event %" PRIu64 ", continuous section start",
name.c_str(), analysis_image_count, event->Id());
/* To prevent cancelling out an existing alert\prealarm\alarm state */
if (state == IDLE) {
@ -2027,63 +1978,12 @@ bool Monitor::Analyse() {
(event_close_mode == CLOSE_ALARM));
}
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",
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) {
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
*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());
event = openEvent(snap, cause, noteSetMap);
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());
} else {
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>(Seconds(section_length).count()));
closeEvent();
event = new Event(this, timestamp, 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());
event = openEvent(snap, cause, noteSetMap);
}
} else {
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);
} // 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() {
if (!event) return;
@ -2786,7 +2743,18 @@ void Monitor::closeEvent() {
Debug(1, "close event thread is not joinable");
}
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");
event = nullptr;
if (shared_data) video_store_data->recording = {};
@ -3079,9 +3047,6 @@ int Monitor::PrimeCapture() {
int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture(); }
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
if (decoder) {
decoder->Stop();
@ -3099,10 +3064,14 @@ int Monitor::Close() {
video_fifo = nullptr;
}
if (close_event_thread.joinable()) {
close_event_thread.join();
}
std::lock_guard<std::mutex> lck(event_mutex);
if (event) {
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id());
closeEvent();
close_event_thread.join();
}
if (camera) camera->Close();
return 1;

View File

@ -404,6 +404,8 @@ protected:
int n_linked_monitors;
MonitorLink **linked_monitors;
std::string event_start_command;
std::string event_end_command;
std::vector<Group *> groups;
@ -591,6 +593,10 @@ public:
bool Decode();
void DumpImage( Image *dump_image ) 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 Reload();

View File

@ -116,14 +116,15 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
, max_video_packet_count);
for (
auto it = ++pktQueue.begin();
it != pktQueue.end() and *it != add_packet;
auto it = ++pktQueue.begin();
it != pktQueue.end() and *it != add_packet;
// iterator is incremented by erase
) {
std::shared_ptr <ZMPacket>zm_packet = *it;
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
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;
++it;
continue;
@ -312,7 +313,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
pktQueue.size());
pktQueue.pop_front();
packet_counts[zm_packet->packet.stream_index] -= 1;
//delete zm_packet;
}
} // end if have at least max_video_packet_count video packets remaining
// We signal on every packet because someday we may analyze sound

View File

@ -252,8 +252,15 @@ void HwCapsDetect() {
#elif defined(__arm__)
// ARM processor in 32bit mode
// To see if it supports NEON, we need to get that information from the kernel
#ifdef __linux__
unsigned long auxval = getauxval(AT_HWCAP);
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");
neonversion = 1;
} else {

View File

@ -878,16 +878,23 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
continue;
}
if (polygon.Extent().Lo().x_ < 0 || 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), fixing",
if (polygon.Extent().Lo().x_ < 0
||
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,
Name,
monitor->Name(),
polygon.Extent().Lo().x_,
polygon.Extent().Lo().y_,
polygon.Extent().Hi().x_,
polygon.Extent().Hi().y_);
polygon.Extent().Hi().y_,
monitor->Width(),
monitor->Height());
polygon.Clip(Box(
{0, 0},

View File

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

View File

@ -116,52 +116,6 @@ else
echo "Defaulting to ZoneMinder upstream git"
GITHUB_FORK="ZoneMinder"
fi;
if [ "$SNAPSHOT" == "stable" ]; then
if [ "$BRANCH" == "" ]; then
#REV=$(git rev-list --tags --max-count=1)
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
if [ -z "$BRANCH" ]; then
# This should only happen in CI environments where tag info isn't available
BRANCH=`cat version`
echo "Building branch $BRANCH"
fi
if [ "$BRANCH" == "" ]; then
echo "Unable to determine latest stable branch!"
exit 0;
fi
echo "Latest stable branch is $BRANCH";
fi;
else
if [ "$BRANCH" == "" ]; then
echo "Defaulting to master branch";
BRANCH="master";
fi;
if [ "$SNAPSHOT" == "NOW" ]; then
SNAPSHOT=`date +%Y%m%d%H%M%S`;
else
if [ "$SNAPSHOT" == "CURRENT" ]; then
SNAPSHOT="`date +%Y%m%d.`$(git rev-list ${versionhash}..HEAD --count)"
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;
# Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead.
@ -171,15 +125,8 @@ if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; 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."
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
@ -192,14 +139,59 @@ else
fi;
cd "${GITHUB_FORK}_zoneminder_release"
git checkout $BRANCH
cd ../
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
if [ "$SNAPSHOT" == "stable" ]; then
if [ "$BRANCH" == "" ]; then
#REV=$(git rev-list --tags --max-count=1)
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
if [ -z "$BRANCH" ]; then
# This should only happen in CI environments where tag info isn't available
BRANCH=`cat version`
echo "Building branch $BRANCH"
fi
if [ "$BRANCH" == "" ]; then
echo "Unable to determine latest stable branch!"
exit 0;
fi
echo "Latest stable branch is $BRANCH";
fi;
else
if [ "$BRANCH" == "" ]; then
echo "Defaulting to master branch";
BRANCH="master";
fi;
if [ "$SNAPSHOT" == "NOW" ]; then
SNAPSHOT=`date +%Y%m%d%H%M%S`;
else
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)"
fi;
fi;
fi;
echo "git checkout $BRANCH"
git checkout $BRANCH
if [ $? -ne 0 ]; then
echo "Failed to switch to branch."
exit 1;
fi;
echo "git pull..."
git pull
# Grab the ZoneMinder version from the contents of the version file
VERSION=$(cat version)
if [ -z "$VERSION" ]; then
exit 1;
fi;
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
cd ../
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
VERSION="$VERSION~$SNAPSHOT";
fi;
@ -230,8 +222,8 @@ rm .gitignore
cd ../
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then
read -p "$DIRECTORY.orig.tar.gz does not exist, create it? [Y/n]"
if [ -e "$DIRECTORY.orig.tar.gz" ]; then
read -p "$DIRECTORY.orig.tar.gz exists, overwrite it? [Y/n]"
if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
fi;
@ -357,6 +349,22 @@ EOF
fi;
else
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";
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(
'fields' => array('Id', 'Type', 'Device'),
'fields' => array('Id', 'Type', 'Device', 'Function'),
'conditions' => array('Id' => $id)
));
// Clean up the returned array
$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
if ( $monitor[0]['Type'] == 'Local' ) {

View File

@ -167,9 +167,15 @@ class Group extends ZM_Object {
public function Parents() {
$Parents = array();
$Parent = $this->Parent();
while( $Parent ) {
$seen_parents = array();
while ($Parent) {
$seen_parents[$Parent->Id()] = $Parent;
array_unshift($Parents, $Parent);
$Parent = $Parent->Parent();
if ($Parent and isset($seen_parents[$Parent->Id()])) {
Warning("Detected hierarchy loop in group {$Parent->Name()}");
break;
}
}
return $Parents;
}
@ -189,6 +195,9 @@ class Group extends ZM_Object {
public function canView($u=null) {
global $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.
foreach ($this->Monitors() as $monitor) {
if ($monitor->canView($u)) return true;

View File

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

View File

@ -13,6 +13,10 @@ class Zone extends ZM_Object {
'Name' => '',
'Type' => 'Active',
'Units' => 'Pixels',
'NumCoords' => '0',
'Coords' => 0,
'Area' => '0',
'AlarmRGB' => '0',
'CheckMethod' => 'Blobs',
'MinPixelThreshold' => null,
'MaxPixelThreshold' => null,
@ -46,5 +50,17 @@ class Zone extends ZM_Object {
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
?>

View File

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

View File

@ -77,7 +77,7 @@ height: 100%;
position: relative;
}
#imageFeed {
#videoFeed {
display: inline-block;
position: relative;
text-align: center;
@ -263,3 +263,17 @@ height: 100%;
height: 100%;
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 {
display: flex;
justify-content: space-between;
}
#sidebar {
min-width: 140px;
}
#menuControls {
@ -19,19 +21,22 @@
#monitorStatus {
margin: 4px auto;
text-align: center;
}
#monitorStatus #enableDisableAlarms {
float: left;
}
#monitorStatus #forceCancelAlarm {
float: right;
display: inline-block;
}
#monitorStatus #monitorState {
}
#replayStatus {
margin: 3px 0 2px;
text-align: center;
display: inline-block;
}
#replayStatus > span {
padding: 0 4px;
}
#dvrControls {
margin-top: 3px;
margin-bottom: 2px;
@ -67,15 +72,6 @@
cursor: default;
}
#replayStatus {
margin: 3px 0 2px;
text-align: center;
clear: both;
}
#replayStatus > span {
padding: 0 4px;
}
#events {
margin: 0 auto;
@ -116,3 +112,22 @@ span.alert {
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);
?>;
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'] ?>">
<?php
$source_class = 'infoText';
$source_class_reason = '';
if ( (!$monitor['Status'] || $monitor['Status'] == 'NotRunning') && $monitor['Type']!='WebSite' ) {
$source_class = 'errorText';
$source_class_reason = translate('Not Running');
} else {
if ( $monitor['CaptureFPS'] == '0.00' ) {
$source_class = 'errorText';
$source_class_reason = translate('No capture FPS');
} else if ( (!$monitor['AnalysisFPS']) && ($monitor['Function']!='Monitor') && ($monitor['Function'] != 'Nodect') ) {
$source_class = 'warnText';
} else {
$source_class = 'infoText';
$source_class_reason = translate('No analysis FPS');
}
}
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);
$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 ) {
?>
@ -317,7 +323,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
}
?>
<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/>
<?php echo $imgHTML ?>
<div class="small text-nowrap text-muted">

View File

@ -25,47 +25,57 @@ if ( !canView('Events') ) {
require_once('includes/Event.php');
require_once('includes/Filter.php');
require_once('includes/Zone.php');
$eid = validInt($_REQUEST['eid']);
$fid = !empty($_REQUEST['fid']) ? validInt($_REQUEST['fid']) : 1;
$Event = new ZM\Event($eid);
if ( $user['MonitorIds'] ) {
$monitor_ids = explode(',', $user['MonitorIds']);
if ( count($monitor_ids) and ! in_array($Event->MonitorId(), $monitor_ids) ) {
$view = 'error';
return;
}
}
$Monitor = $Event->Monitor();
$monitor = $Event->Monitor();
if ( isset($_REQUEST['rate']) ) {
if (!$monitor->canView()) {
$view = 'error';
return;
}
zm_session_start();
if (isset($_REQUEST['rate']) ) {
$rate = validInt($_REQUEST['rate']);
} else if ( isset($_COOKIE['zmEventRate']) ) {
} else if (isset($_COOKIE['zmEventRate'])) {
$rate = $_COOKIE['zmEventRate'];
} 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']);
} else if ( isset($_COOKIE['zmEventScale'.$Event->MonitorId()]) ) {
} else if (isset($_COOKIE['zmEventScale'.$Event->MonitorId()])) {
$scale = $_COOKIE['zmEventScale'.$Event->MonitorId()];
} 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';
if ( isset($_REQUEST['codec']) ) {
if (isset($_REQUEST['codec'])) {
$codec = $_REQUEST['codec'];
zm_session_start();
$_SESSION['zmEventCodec'.$Event->MonitorId()] = $codec;
session_write_close();
} else if ( isset($_SESSION['zmEventCodec'.$Event->MonitorId()]) ) {
$codec = $_SESSION['zmEventCodec'.$Event->MonitorId()];
} else {
$codec = $Monitor->DefaultCodec();
$codec = $monitor->DefaultCodec();
}
session_write_close();
$codecs = array(
'auto' => translate('Auto'),
'MP4' => translate('MP4'),
@ -79,32 +89,30 @@ $replayModes = array(
'gapless' => translate('ReplayGapless'),
);
if ( isset($_REQUEST['streamMode']) )
if (isset($_REQUEST['streamMode']))
$streamMode = validHtmlStr($_REQUEST['streamMode']);
else
$streamMode = 'video';
$replayMode = '';
if ( isset($_REQUEST['replayMode']) )
if (isset($_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']);
if ( ( !$replayMode ) or ( !$replayModes[$replayMode] ) ) {
if ((!$replayMode) or !$replayModes[$replayMode]) {
$replayMode = 'none';
}
$video_tag = false;
if ( $Event->DefaultVideo() and ( $codec == 'MP4' or $codec == 'auto' ) ) {
$video_tag = true;
}
$video_tag = ($Event->DefaultVideo() and ($codec == 'MP4' or $codec == 'auto'));
// videojs zoomrotate only when direct recording
$Zoom = 1;
$Rotation = 0;
if ( $Monitor->VideoWriter() == '2' ) {
if ($monitor->VideoWriter() == '2') {
# Passthrough
$Rotation = $Event->Orientation();
if ( in_array($Event->Orientation(),array('90','270')) )
if (in_array($Event->Orientation(),array('90','270')))
$Zoom = $Event->Height()/$Event->Width();
}
@ -143,7 +151,7 @@ if ( $Event->Id() and !file_exists($Event->Path()) )
<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="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="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>
@ -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="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>
<?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>
<h2><?php echo translate('Event').' '.$Event->Id() ?></h2>
@ -190,10 +204,10 @@ if ( $Event->Id() and !file_exists($Event->Path()) )
<div class="">
<div id="eventVideo">
<!-- VIDEO CONTENT -->
<?php
if ( $video_tag ) {
?>
<div id="videoFeed">
<?php
if ($video_tag) {
?>
<video autoplay id="videoobj" class="video-js vjs-default-skin"
style="transform: matrix(1, 0, 0, 1, 0, 0);"
<?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/>
Your browser does not support the video tag.
</video>
</div><!--videoFeed-->
<?php
} else {
?>
<div id="imageFeed">
<?php
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;');
outputVideoStream('evtStream', $streamSrc, reScale( $Event->Width(), $scale ).'px', reScale( $Event->Height(), $scale ).'px', ZM_MPEG_LIVE_FORMAT );
} else {
$streamSrc = $Event->getStreamSrc(array('mode'=>'jpeg', 'frame'=>$fid, 'scale'=>$scale, 'rate'=>$rate, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>$replayMode),'&amp;');
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 {
outputHelperStream('evtStream', $streamSrc, reScale($Event->Width(), $scale).'px', reScale($Event->Height(), $scale).'px' );
outputHelperStream('evtStream', $streamSrc, '100%', '100%');
}
} // 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 class="progressBox" id="progressBox" title="" style="width: 0%;"></div>
</div><!--progressBar-->
</div><!--imageFeed-->
<?php
} /*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">
<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>
@ -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="rate"><?php echo translate('Rate') ?>:
<?php
#rates are defined in skins/classic/includes/config.php
echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue'));
#rates are defined in skins/classic/includes/config.php
echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue'));
?>
<!--<span id="rateValue"><?php echo $rate/100 ?></span>x</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><!--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;
$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();
for ( $i = 0; $i < 7; $i++ ) {

View File

@ -177,7 +177,7 @@ function changeScale() {
var newWidth;
var newHeight;
var autoScale;
var eventViewer= $j(vid ? '#videoobj' : '#evtStream');
var eventViewer= $j(vid ? '#videoobj' : '#videoFeed');
var alarmCue = $j('div.alarmCue');
var bottomEl = $j('#replayStatus');
@ -773,8 +773,9 @@ function manageDelConfirmModalBtns() {
return;
}
pauseClicked();
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) {
$j('#deleteConfirm').modal('hide');
streamNext(true);
@ -909,12 +910,12 @@ function initPage() {
progressBarNav();
streamCmdTimer = setTimeout(streamQuery, 500);
if (canStreamNative) {
if (!$j('#imageFeed')) {
console.log('No element with id tag imageFeed found.');
if (!$j('#videoFeed')) {
console.log('No element with id tag videoFeed found.');
} else {
var streamImg = $j('#imageFeed img');
var streamImg = $j('#videoFeed img');
if (!streamImg) {
streamImg = $j('#imageFeed object');
streamImg = $j('#videoFeed object');
}
$j(streamImg).click(function(event) {
handleClick(event);
@ -1070,5 +1071,27 @@ function 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
$j(document).ready(initPage);

View File

@ -1,7 +1,7 @@
<?php
global $connkey;
global $Event;
global $Monitor;
global $monitor;
global $filterQuery;
global $sortQuery;
global $rates;
@ -43,8 +43,9 @@ var eventData = {
Id: '<?php echo $Event->Id() ?>',
Name: '<?php echo $Event->Name() ?>',
MonitorId: '<?php echo $Event->MonitorId() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
MonitorName: '<?php echo validJsStr($monitor->Name()) ?>',
Cause: '<?php echo validHtmlStr($Event->Cause()) ?>',
Notes: '<?php echo $Event->Notes()?>',
Width: '<?php echo $Event->Width() ?>',
Height: '<?php echo $Event->Height() ?>',
Length: '<?php echo $Event->Length() ?>',
@ -72,6 +73,7 @@ var eventDataStrings = {
MonitorId: '<?php echo translate('AttrMonitorId') ?>',
MonitorName: '<?php echo translate('AttrMonitorName') ?>',
Cause: '<?php echo translate('Cause') ?>',
Notes: '<?php echo translate('Notes') ?>',
StartDateTimeShort: '<?php echo translate('AttrStartTime') ?>',
Length: '<?php echo translate('Duration') ?>',
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 rate = '<?php echo $rate ?>'; // really only used when setting up initial playback rate.
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 ?>;
@ -105,6 +107,8 @@ var streamMode = '<?php echo $streamMode ?>';
//
var deleteString = "<?php echo validJsStr(translate('Delete')) ?>";
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_HEIGHT = '<?php echo ZM_WEB_LIST_THUMB_HEIGHT ?>';
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.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.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
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: ' ) {
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.AlarmFrames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.AlarmFrames + '</a>';

View File

@ -1,18 +1,6 @@
//
// 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 ?>;

View File

@ -57,7 +57,7 @@ function getFrame(monId, time, last_Frame) {
var events_for_monitor = events_by_monitor_id[monId];
if ( !events_for_monitor ) {
console.log("No events for monitor " + monId);
//console.log("No events for monitor " + monId);
return;
}
@ -648,8 +648,11 @@ function setSpeed(speed_index) {
}
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;
var form = $j('#montagereview_form')[0];
var form = document.getElementById('montagereview_form');
form.elements['live'].value = value;
form.submit();
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 ) {
var monId = monitorPtr[i];
if ( !monId ) continue;
@ -1006,18 +1022,6 @@ function initPage() {
}
} // 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);
//setFit(fitMode); // will redraw
//setLive(liveMode); // will redraw

View File

@ -239,6 +239,6 @@ echo "];\n";
var cWidth; // save canvas width
var cHeight; // save canvas height
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 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.request = 'watch';
data.mid = monitorId;
if ( auth_hash ) data.auth = auth_hash;
if (auth_hash) data.auth = auth_hash;
$j.getJSON(thisUrl, data)
.done(function(data) {
@ -94,8 +94,48 @@ function showPtzControls() {
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() {
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 newHeight;
var autoScale;
@ -104,7 +144,7 @@ function changeScale() {
// times and what the consequences would be
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
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;
newHeight = newSize.height;
autoScale = newSize.autoScale;
@ -118,16 +158,18 @@ function changeScale() {
var streamImg = $j('#liveStream'+monitorId);
if (streamImg) {
var oldSrc = streamImg.attr('src');
streamImg.attr('src', '');
// This is so that we don't waste bandwidth and let the browser do all the scaling.
if (autoScale > 100) autoScale = 100;
if (scale > 100) scale = 100;
var newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale));
if (streamImg.nodeName == 'IMG') {
const oldSrc = streamImg.attr('src');
streamImg.attr('src', '');
// This is so that we don't waste bandwidth and let the browser do all the scaling.
if (autoScale > 100) autoScale = 100;
if (scale > 100) scale = 100;
const newSrc = oldSrc.replace(/scale=\d+/i, 'scale='+((scale == 'auto' || scale == '0') ? autoScale : scale));
streamImg.width(newWidth);
streamImg.height(newHeight);
streamImg.attr('src', newSrc);
streamImg.width(newWidth);
streamImg.height(newHeight);
streamImg.attr('src', newSrc);
}
} else {
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.
if (respObj.status) {
streamStatus = respObj.status;
$j('#fpsValue').text(streamStatus.fps);
$j('#capturefpsValue').text(streamStatus.capturefps);
$j('#analysisfpsValue').text(streamStatus.analysisfps);
if ($j('#viewingFPSValue').text() != streamStatus.fps) {
$j('#viewingFPSValue').text(streamStatus.fps);
}
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);
@ -287,31 +335,19 @@ function getStreamCmdResponse(respObj, respText) {
// Try to reload the image stream.
var streamImg = $j('#liveStream'+monitorId);
if (streamImg) {
var oldSrc = streamImg.attr('src');
var newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
if (oldSrc != newSrc) {
streamImg.attr('src', newSrc);
table.bootstrapTable('refresh');
const oldSrc = streamImg.attr('src');
if (oldSrc) {
const newSrc = oldSrc.replace(/auth=\w+/i, 'auth='+streamStatus.auth);
if (oldSrc != newSrc) {
streamImg.attr('src', newSrc);
table.bootstrapTable('refresh');
}
}
}
} // end if have a new auth hash
} // end if respObj.status
} else {
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;
@ -368,6 +404,7 @@ function streamCmdPlay(action) {
}
function streamCmdReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=stream&connkey='+connKey, data)
.done(getStreamCmdResponse)
.fail(getStreamCmdError);
@ -386,10 +423,7 @@ function streamCmdStop(action) {
setButtonState('fastRevBtn', 'unavail');
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_STOP;
streamCmdReq(data);
streamCmdReq({command: CMD_STOP});
}
setButtonState('stopBtn', 'unavail');
setButtonState('playBtn', 'active');
@ -407,7 +441,6 @@ function streamCmdFastFwd(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_FASTFWD;
streamCmdReq(data);
}
@ -425,7 +458,6 @@ function streamCmdSlowFwd(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SLOWFWD;
streamCmdReq(data);
}
@ -447,7 +479,6 @@ function streamCmdSlowRev(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SLOWREV;
streamCmdReq(data);
}
@ -469,7 +500,6 @@ function streamCmdFastRev(action) {
}
if (action) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_FASTREV;
streamCmdReq(data);
}
@ -477,7 +507,6 @@ function streamCmdFastRev(action) {
function streamCmdZoomIn(x, y) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_ZOOMIN;
@ -486,14 +515,12 @@ function streamCmdZoomIn(x, y) {
function streamCmdZoomOut() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_ZOOMOUT;
streamCmdReq(data);
}
function streamCmdScale(scale) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_SCALE;
data.scale = scale;
streamCmdReq(data);
@ -501,7 +528,6 @@ function streamCmdScale(scale) {
function streamCmdPan(x, y) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.x = x;
data.y = y;
data.command = CMD_PAN;
@ -510,11 +536,11 @@ function streamCmdPan(x, y) {
function streamCmdQuery() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = CMD_QUERY;
streamCmdReq(data);
}
/* getStatusCmd is used when not streaming, since there is no persistent zms */
function getStatusCmdResponse(respObj, respText) {
watchdogOk('status');
if (statusCmdTimer) {
@ -522,32 +548,33 @@ function getStatusCmdResponse(respObj, respText) {
}
if (respObj.result == 'Ok') {
$j('#fpsValue').text(respObj.monitor.FrameRate);
$j('#captureFPSValue').text(respObj.monitor.FrameRate);
setAlarmState(respObj.monitor.Status);
} else {
checkStreamForErrors('getStatusCmdResponse', respObj);
}
var statusCmdTimeout = statusRefreshTimeout;
if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) {
if (alarmState == STATE_ALARM || alarmState == STATE_ALERT) {
statusCmdTimeout = statusCmdTimeout/5;
}
statusCmdTimer = setTimeout(statusCmdQuery, statusCmdTimeout);
}
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)
.fail(logAjaxFail);
streamCmdTimer = null;
statusCmdTimer = null;
}
function alarmCmdReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=alarm&id='+monitorId, data)
.done(getAlarmCmdResponse)
.fail(function(jqxhr, textStatus, error) {
if ( textStatus === "timeout" ) {
if (textStatus === 'timeout') {
streamCmdQuery();
} else {
logAjaxFail(jqxhr, textStatus, error);
@ -561,14 +588,12 @@ function getAlarmCmdResponse(respObj, respText) {
function cmdDisableAlarms() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'disableAlarms';
alarmCmdReq(data);
}
function cmdEnableAlarms() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'enableAlarms';
alarmCmdReq(data);
}
@ -583,7 +608,6 @@ function cmdAlarm() {
function cmdForceAlarm() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'forceAlarm';
alarmCmdReq(data);
if (window.event) window.event.preventDefault();
@ -591,7 +615,6 @@ function cmdForceAlarm() {
function cmdCancelForcedAlarm() {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.command = 'cancelForcedAlarm';
alarmCmdReq(data);
if (window.event) window.event.preventDefault();
@ -607,6 +630,7 @@ function cmdForce() {
}
function controlReq(data) {
if (auth_hash) data.auth = auth_hash;
$j.getJSON(monitorUrl + '?view=request&request=control&id='+monitorId, data)
.done(getControlResponse)
.fail(logAjaxFail);
@ -639,16 +663,16 @@ function controlCmd(event) {
var data = {};
if (event && (xtell || ytell)) {
var target = event.target;
var offset = $j(target).offset();
var width = $j(target).width();
var height = $j(target).height();
const target = event.target;
const offset = $j(target).offset();
const width = $j(target).width();
const height = $j(target).height();
var x = event.pageX - offset.left;
var y = event.pageY - offset.top;
const x = event.pageX - offset.left;
const y = event.pageY - offset.top;
if (xtell) {
var xge = parseInt((x*100)/width);
let xge = parseInt((x*100)/width);
if (xtell == -1) {
xge = 100 - xge;
} else if (xtell == 2) {
@ -657,7 +681,7 @@ function controlCmd(event) {
data.xge = xge;
}
if (ytell) {
var yge = parseInt((y*100)/height);
let yge = parseInt((y*100)/height);
if (ytell == -1) {
yge = 100 - yge;
} else if (ytell == 2) {
@ -667,7 +691,6 @@ function controlCmd(event) {
}
}
if (auth_hash) data.auth = auth_hash;
data.control = control;
controlReq(data);
@ -678,7 +701,6 @@ function controlCmd(event) {
function controlCmdImage(x, y) {
var data = {};
if (auth_hash) data.auth = auth_hash;
data.scale = scale;
data.control = imageControlMode;
data.x = x;
@ -768,7 +790,6 @@ function reloadWebSite() {
function updatePresetLabels() {
var lblNdx = $j('#ctrlPresetForm option:selected').val();
$j('#newLabel').val(labels[lblNdx]);
}
@ -878,7 +899,7 @@ function initPage() {
// Load the PTZ Preset modal into the DOM
if (monitorControllable) getCtrlPresetModal();
// Load the settings modal into the DOM
if (monitorType == "Local") getSettingsModal();
if (monitorType == 'Local') getSettingsModal();
}
if (monitorType != 'WebSite') {
@ -886,15 +907,12 @@ function initPage() {
statusCmdTimer = setTimeout(statusCmdQuery, (Math.random()+0.1)*statusRefreshTimeout);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'status');
} else {
streamCmdTimer = setTimeout(streamCmdQuery, (Math.random()+0.1)*statusRefreshTimeout);
setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream');
}
if (canStreamNative || (streamMode == 'single')) {
var streamImg = $j('#imageFeed img');
if (!streamImg) {
streamImg = $j('#imageFeed object');
}
if (!streamImg) streamImg = $j('#imageFeed object');
if (!streamImg) {
console.error('No streamImg found for imageFeed');
} else {
@ -905,6 +923,10 @@ function initPage() {
streamImg.click(function(event) {
handleClick(event);
});
streamImg.on("error", function(thing) {
console.log("Error loading image");
console.log(thing);
});
}
} // end if have streamImg
} // streamMode native or single
@ -944,6 +966,18 @@ function initPage() {
$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
settingsBtn.prop('disabled', !(canView.Control && (monitorType == 'Local')));
@ -972,7 +1006,6 @@ function initPage() {
function watchFullscreen() {
const btn = document.getElementById('fullscreenBtn');
console.log(btn);
if (btn.firstElementChild.innerHTML=='fullscreen') {
const content = document.getElementById('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
$j(document).ready(initPage);

View File

@ -1,33 +1,20 @@
<?php
global $monIdx;
global $nextMid;
global $options;
global $monitors;
global $streamMode;
global $showPtzControls;
global $connkey;
global $monitor;
global $scale;
global $labels;
global $cycle;
?>
//
// 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_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_QUERY = <?php echo CMD_QUERY ?>;
var SCALE_BASE = <?php echo SCALE_BASE ?>;
var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_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 streamMode = "<?php echo $streamMode ?>";
var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>";
var cycle = <?php echo $cycle ? 'true' : 'false' ?>;
var connKey = '<?php echo $connkey ?>';
var maxDisplayEvents = <?php echo 2 * MAX_EVENTS ?>;
@ -67,6 +53,28 @@ var monitorRefresh = '<?php echo $monitor->Refresh() ?>';
var monitorStreamReplayBuffer = <?php echo $monitor->StreamReplayBuffer() ?>;
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 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' ?>;
<?php
$control = $monitor->Control();
if ( $control->CanMoveMap() ) { ?>
var imageControlMode = "moveMap";
<?php } elseif ( $control->CanMoveRel() ) { ?>
var imageControlMode = "movePseudoMap";
<?php } elseif ( $control->CanMoveCon() ) { ?>
var imageControlMode = "moveConMap";
<?php } else { ?>
var imageControlMode = null;
<?php } ?>
var imageControlMode = '<?php
$control = $monitor->Control();
if ($control->CanMoveMap()) {
echo 'moveMap';
} else if ($control->CanMoveRel()) {
echo 'movePseudoMap';
} else if ($control->CanMoveCon()) {
echo 'moveConMap';
}
?>';
var refreshApplet = <?php echo (canStreamApplet() && $streamMode == "jpeg")?'true':'false' ?>;
var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
@ -93,17 +100,18 @@ var appletRefreshTime = <?php echo ZM_RELOAD_CAMBOZOLA ?>;
var labels = new Array();
<?php
$labels = array();
foreach( dbFetchAll( 'SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array( $monitor->Id() ) ) as $row ) {
$labels[$row['Preset']] = $row['Label'];
}
foreach ($labels as $index=>$label) {
?>
labels[<?php echo validInt($index) ?>] = '<?php echo validJsStr($label) ?>';
<?php
foreach (dbFetchAll('SELECT * FROM ControlPresets WHERE MonitorId = ?', NULL, array($monitor->Id())) as $row) {
$label = $labels[$row['Preset']] = $row['Label'];
echo 'labels['. validInt($index) .'] = '.validJsStr($label).'\'';
}
?>
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 = {
"seconds": "<?php echo translate('seconds') ?>",
"Fullscreen": "<?php echo translate('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
//
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_PLAY = <?php echo CMD_PLAY ?>;
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_PLAY = <?php echo CMD_PLAY ?>;
var CMD_STOP = <?php echo CMD_STOP ?>;
var CMD_QUERY = <?php echo CMD_QUERY ?>;
var CMD_QUIT = <?php echo CMD_QUIT ?>;
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;

View File

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

View File

@ -24,6 +24,7 @@ if ( !canView('Stream') ) {
}
require_once('includes/MontageLayout.php');
require_once('includes/Zone.php');
$showControl = false;
$showZones = false;
@ -49,7 +50,6 @@ $heights = array(
'1080' => '1080px',
);
$layouts = ZM\MontageLayout::find(NULL, array('order'=>"lower('Name')"));
$layoutsById = array();
foreach ( $layouts as $l ) {
@ -149,7 +149,7 @@ echo getNavBarHTML();
$html .= '<div class="container-fluid" id="mfbpanel"'.( ( $flip == 'down' ) ? ' style="display:none;"' : '' ) .'>'.PHP_EOL;
echo $html;
?>
<div id="headerButtons">
<div id="headerButtons">
<?php
if ( $showControl ) {
echo makeLink('?view=control', translate('Control'));
@ -237,10 +237,10 @@ foreach ( $monitors as $monitor ) {
$monitor_options['width'] = '100%';
if ( 0 ) {
if ($monitor_options['width'] > 0)
$monitor_options['width'] = $monitor_options['width'].'px';
if ($monitor_options['height'] > 0)
$monitor_options['height'] = $monitor_options['height']?$monitor_options['height'].'px' : null;
if ($monitor_options['width'] > 0)
$monitor_options['width'] = $monitor_options['width'].'px';
if ($monitor_options['height'] > 0)
$monitor_options['height'] = $monitor_options['height']?$monitor_options['height'].'px' : null;
} # end if
$monitor_options['connkey'] = $monitor->connKey();
@ -275,30 +275,28 @@ foreach ( $monitors as $monitor ) {
} else if ( $scale ) {
$width = reScale($monitor->Width(), $scale);
$height = reScale($monitor->Height(), $scale);
}
}
$zones = array();
foreach( dbFetchAll('SELECT * FROM Zones WHERE MonitorId=? ORDER BY Area DESC', NULL, array($monitor->Id()) ) as $row ) {
$row['Points'] = coordsToPoints($row['Coords']);
if ( $scale ) {
limitPoints($row['Points'], 0, 0, $monitor->Width(), $monitor->Height());
foreach ( ZM\Zone::find(array('MonitorId'=>$monitor->Id()), array('order'=>'Area DESC')) as $row ) {
$points = $row->Points();
if ($scale) {
limitPoints($points, 0, 0, $monitor->Width(), $monitor->Height());
} else {
limitPoints($row['Points'], 0, 0,
limitPoints($points, 0, 0,
( $width ? $width-1 : $monitor->ViewWidth()-1 ),
( $height ? $height-1 : $monitor->ViewHeight()-1 )
);
}
$row['Coords'] = pointsToCoords($row['Points']);
$row['AreaCoords'] = preg_replace('/\s+/', ',', $row['Coords']);
$row->Coords(pointsToCoords($points));
$zones[] = $row;
} // 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
foreach ( array_reverse($zones) as $zone ) {
echo '<polygon points="'. $zone['AreaCoords'] .'" class="'. $zone['Type'].'" />';
foreach (array_reverse($zones) as $zone) {
echo $zone->svg_polygon();
} // end foreach zone
?>
Sorry, your browser does not support inline SVG
@ -308,9 +306,14 @@ foreach ( array_reverse($zones) as $zone ) {
?>
</div>
<?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
}
?>

View File

@ -18,143 +18,296 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canView('Stream') ) {
if (!canView('Stream')) {
$view = 'error';
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
$mid = intval($_REQUEST['mid']);
if ( !visibleMonitor($mid) ) {
$mid = isset($_REQUEST['mid']) ? intval($_REQUEST['mid']) : 0;
$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';
return;
}
require_once('includes/Monitor.php');
$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
$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']);
} else if ( isset($_COOKIE['zmWatchScale'.$mid]) ) {
$scale = $_COOKIE['zmWatchScale'.$mid];
} else {
$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();
$streamMode = getStreamMode();
$popup = ((isset($_REQUEST['popup'])) && ($_REQUEST['popup'] == 1));
noCacheHeaders();
xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed'));
?>
<body>
<?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>
<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="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="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>
</div>
<div>
<h2><?php echo makeLink('?view=monitor&amp;mid='.$monitor->Id(), validHtmlStr($monitor->Name()), canEdit('Monitors')) ?></h2>
<div id="headerButtons">
<!--
<?php if ( $options['mode'] == 'stream' ) { ?>
<a href="?view=<?php echo $view ?>&amp;mode=still&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stills') ?></a>
<?php } else { ?>
<a href="?view=<?php echo $view ?>&amp;mode=stream&amp;mid=<?php echo $monitor ? $monitor->Id() : '' ?>"><?php echo translate('Stream') ?></a>
<?php } ?>
-->
<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>
<?php echo translate('Scale').': '.htmlSelect('scale', $scales, $scale, array('id'=>'scale')); ?>
</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
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>';
}
?>
<div id="content">
<div id="imageFeed"
<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
if ( $streamMode == 'jpeg' ) {
$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"
<?php
if ($streamMode == 'jpeg') {
echo 'title="Click to zoom, shift click to pan, ctrl click to zoom out"';
}
?>
><?php echo getStreamHTML($monitor, array('scale'=>$scale)); ?></div>
<?php if ( $monitor->Type() != 'WebSite' ) { ?>
<div id="monitorStatus">
<div id="monitorState">
<?php echo translate('State') ?>:
<span id="stateValue"></span> -
<span title="<?php echo translate('Viewing FPS')?>"><span id="fpsValue"></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' ) { ?>
<span title="<?php echo translate('Analysis FPS')?>"><span id="analysisfpsValue"></span> fps</span>
<?php } ?>
><?php echo getStreamHTML($monitor, array('scale'=>$scale)); ?>
</div>
</div>
<div id="dvrControls">
<?php if ($monitor->Type() != 'WebSite') { ?>
<div id="monitorStatus">
<div id="monitorState">
<span><?php echo translate('State') ?>:<span id="stateValue"></span></span>
<span id="viewingFPS" title="<?php echo translate('Viewing FPS')?>"><span id="viewingFPSValue"></span> fps</span>
<span id="captureFPS" title="<?php echo translate('Capturing FPS')?>"><span id="captureFPSValue"></span> fps</span>
<?php if ( $monitor->Function() == 'Modect' or $monitor->Function() == 'Mocord' ) { ?>
<span id="analysisFPS" title="<?php echo translate('Analysis FPS')?>"><span id="analysisFPSValue"></span> fps</span>
<?php } ?>
</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">
<?php
if ( $streamMode == 'jpeg' ) {
if ( $monitor->StreamReplayBuffer() != 0 ) {
if ($streamMode == 'jpeg') {
if ($monitor->StreamReplayBuffer() != 0) {
?>
<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>
</button>
<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>
</button>
<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>
</button>
<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>
</button>
<?php
}
?>
<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>
</button>
<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>
</button>
<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>
</button>
<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>
</button>
<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>
</button>
<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>
</button>
<?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">
<i class="material-icons md-18">chevron_right</i>
</button>
<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>
</button>
<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>
</button>
<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>
</button>
<?php
}
?>
<button type="button" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="avail" data-on-click="streamCmdZoomOut">
<i class="material-icons md-18">zoom_out</i>
</button>
<button type="button" id="fullscreenBtn" title="<?php echo translate('Fullscreen') ?>" class="avail" data-on-click="watchFullscreen">
<i class="material-icons md-18">fullscreen</i>
</button>
<button type="button" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="avail" data-on-click="streamCmdZoomOut">
<i class="material-icons md-18">zoom_out</i>
</button>
<button type="button" id="fullscreenBtn" title="<?php echo translate('Fullscreen') ?>" class="avail" data-on-click="watchFullscreen">
<i class="material-icons md-18">fullscreen</i>
</button>
<?php
} // end if streamMode==jpeg
?>
</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><!--dvrButtons-->
</div>
<?php } // end if $monitor->Type() != 'WebSite' ?>
<?php
@ -208,6 +361,7 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
</table>
</div>
</div>
<?php
}
if ( ZM_WEB_SOUND_ON_ALARM ) {

View File

@ -37,7 +37,8 @@ ZM_WEB_GROUP=@WEB_GROUP@
ZM_DB_TYPE=@ZM_DB_TYPE@
# 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@
# ZoneMinder database name