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

This commit is contained in:
Isaac Connor 2020-06-25 16:07:22 -04:00
commit b060e0835c
29 changed files with 753 additions and 391 deletions

View File

@ -282,6 +282,7 @@ DROP TABLE IF EXISTS `Filters`;
CREATE TABLE `Filters` ( CREATE TABLE `Filters` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`UserId` int(10) unsigned,
`Query_json` text NOT NULL, `Query_json` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0', `AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoVideo` tinyint(3) unsigned NOT NULL default '0', `AutoVideo` tinyint(3) unsigned NOT NULL default '0',
@ -787,6 +788,7 @@ INSERT INTO `Filters`
`AutoCopy`, `AutoCopy`,
`AutoCopyTo`, `AutoCopyTo`,
`UpdateDiskSpace`, `UpdateDiskSpace`,
`UserId`,
`Background`, `Background`,
`Concurrent` `Concurrent`
) )
@ -807,6 +809,7 @@ INSERT INTO `Filters`
0/*AutoMove*/,0/*MoveTo*/, 0/*AutoMove*/,0/*MoveTo*/,
0/*AutoCopy*/,0/*CopyTo*/, 0/*AutoCopy*/,0/*CopyTo*/,
0/*UpdateDiskSpace*/, 0/*UpdateDiskSpace*/,
1/*UserId = admin*/,
1/*Background*/, 1/*Background*/,
0/*Concurrent*/ 0/*Concurrent*/
); );
@ -830,6 +833,7 @@ INSERT INTO `Filters`
`AutoCopy`, `AutoCopy`,
`AutoCopyTo`, `AutoCopyTo`,
`UpdateDiskSpace`, `UpdateDiskSpace`,
`UserId`,
`Background`, `Background`,
`Concurrent` `Concurrent`
) )
@ -849,6 +853,7 @@ VALUES (
0/*AutoMove*/,0/*MoveTo*/, 0/*AutoMove*/,0/*MoveTo*/,
0/*AutoCopy*/,0/*CopyTo*/, 0/*AutoCopy*/,0/*CopyTo*/,
1/*UpdateDiskSpace*/, 1/*UpdateDiskSpace*/,
1/*UserId=admin*/,
1/*Background*/, 1/*Background*/,
0/*Concurrent*/ 0/*Concurrent*/
); );

11
db/zm_update-1.35.5.sql Normal file
View File

@ -0,0 +1,11 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Filters'
AND column_name = 'UserId'
) > 0,
"SELECT 'Column UserId already exists in Filters'",
"ALTER TABLE `Filters` ADD `UserId` int(10) unsigned AFTER `Name`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -28,7 +28,7 @@
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.35.4 Version: 1.35.5
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons

View File

@ -83,7 +83,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libssl | libssl1.0.0 | libssl1.1 ,libssl | libssl1.0.0 | libssl1.1
,libcrypt-eksblowfish-perl ,libcrypt-eksblowfish-perl
,libdata-entropy-perl ,libdata-entropy-perl
,libvncclient1 ,libvncclient1|libvncclient0
Recommends: ${misc:Recommends} Recommends: ${misc:Recommends}
,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
,mysql-server | mariadb-server | virtual-mysql-server ,mysql-server | mariadb-server | virtual-mysql-server

View File

@ -352,6 +352,11 @@ sub GenerateVideo {
sub delete { sub delete {
my $event = $_[0]; my $event = $_[0];
if ( !$event->canEdit() ) {
Warning('No permission to delete event.');
return 'No permission to delete event.';
}
my $in_zmaudit = ( $0 =~ 'zmaudit.pl$'); my $in_zmaudit = ( $0 =~ 'zmaudit.pl$');
if ( ! $in_zmaudit ) { if ( ! $in_zmaudit ) {
@ -402,6 +407,11 @@ sub delete {
sub delete_files { sub delete_files {
my $event = shift; my $event = shift;
if ( !$event->canEdit() ) {
Warning('No permission to delete event.');
return 'No permission to delete event.';
}
foreach my $Storage ( foreach my $Storage (
@_ ? ($_[0]) : ( @_ ? ($_[0]) : (
new ZoneMinder::Storage($$event{StorageId}), new ZoneMinder::Storage($$event{StorageId}),
@ -570,6 +580,11 @@ sub DiskSpace {
sub CopyTo { sub CopyTo {
my ( $self, $NewStorage ) = @_; my ( $self, $NewStorage ) = @_;
if ( !$self->canEdit() ) {
Warning('No permission to copy event.');
return 'No permission to copy event.';
}
my $OldStorage = $self->Storage(undef); my $OldStorage = $self->Storage(undef);
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint
if ( ! -e $OldPath ) { if ( ! -e $OldPath ) {
@ -734,8 +749,13 @@ sub CopyTo {
} # end sub CopyTo } # end sub CopyTo
sub MoveTo { sub MoveTo {
my ( $self, $NewStorage ) = @_; my ( $self, $NewStorage ) = @_;
if ( !$self->canEdit() ) {
Warning('No permission to move event.');
return 'No permission to move event.';
}
my $OldStorage = $self->Storage(undef); my $OldStorage = $self->Storage(undef);
my $error = $self->CopyTo($NewStorage); my $error = $self->CopyTo($NewStorage);
@ -857,16 +877,32 @@ sub files {
sub has_capture_jpegs { sub has_capture_jpegs {
@{$_[0]{capture_jpegs}} = grep(/^\d+\-capture\.jpg$/, $_[0]->files()); @{$_[0]{capture_jpegs}} = grep(/^\d+\-capture\.jpg$/, $_[0]->files());
Debug("have " . @{$_[0]{capture_jpegs}} . " capture jpegs"); Debug('have ' . @{$_[0]{capture_jpegs}} . ' capture jpegs');
return @{$_[0]{capture_jpegs}} ? 1 : 0; return @{$_[0]{capture_jpegs}} ? 1 : 0;
} }
sub has_analyse_jpegs { sub has_analyse_jpegs {
@{$_[0]{analyse_jpegs}} = grep(/^\d+\-analyse\.jpg$/, $_[0]->files()); @{$_[0]{analyse_jpegs}} = grep(/^\d+\-analyse\.jpg$/, $_[0]->files());
Debug("have " . @{$_[0]{analyse_jpegs}} . " analyse jpegs"); Debug('have ' . @{$_[0]{analyse_jpegs}} . ' analyse jpegs');
return @{$_[0]{analyse_jpegs}} ? 1 : 0; return @{$_[0]{analyse_jpegs}} ? 1 : 0;
} }
sub canEdit {
my $self = shift;
if ( !$ZoneMinder::user ) {
# No user loaded... assume running as system
return 1;
}
if ( !$$ZoneMinder::user{MonitorIds} ) {
# User has no monitor limitations
return 1;
}
if ( $$ZoneMinder::user{Events} eq 'Edit' ) {
return 1;
}
return 0;
} # end sub canEdit
1; 1;
__END__ __END__

View File

@ -49,49 +49,34 @@ use ZoneMinder::Database qw(:all);
require ZoneMinder::Storage; require ZoneMinder::Storage;
require ZoneMinder::Server; require ZoneMinder::Server;
sub Name { use vars qw/ $table $primary_key %fields /;
if ( @_ > 1 ) { $table = 'Users';
$_[0]{Name} = $_[1]; $primary_key = 'Id';
}
return $_[0]{Name};
} # end sub Path
sub find { %fields = map { $_ => $_ } qw(
shift if $_[0] eq 'ZoneMinder::Filter'; Id
my %sql_filters = @_; Name
Query_json
my $sql = 'SELECT * FROM Filters'; AutoArchive
my @sql_filters; AutoVideo
my @sql_values; AutoUpload
AutoEmail
if ( exists $sql_filters{Name} ) { EmailTo
push @sql_filters , ' Name = ? '; EmailSubject
push @sql_values, $sql_filters{Name}; EmailBody
} AutoMessage
AutoExecute
$sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; AutoExecuteCmd
$sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; AutoDelete
AutoMove
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) AutoMoveTo
or Fatal("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr()); AutoCopy
my $res = $sth->execute(@sql_values) AutoCopyTo
or Fatal("Can't execute '$sql': ".$sth->errstr()); UpdateDiskSpace
UserId
my @results; Background
Concurrent
while( my $db_filter = $sth->fetchrow_hashref() ) { );
my $filter = new ZoneMinder::Filter($$db_filter{Id}, $db_filter);
push @results, $filter;
} # end while
$sth->finish();
return @results;
}
sub find_one {
my @results = find(@_);
return $results[0] if @results;
}
sub Execute { sub Execute {
my $self = $_[0]; my $self = $_[0];
@ -324,14 +309,9 @@ sub Sql {
} # end if terms } # end if terms
if ( $self->{Sql} ) { if ( $self->{Sql} ) {
if ( $self->{AutoMessage} ) {
# Include all events, including events that are still ongoing # Include all events, including events that are still ongoing
# and have no EndTime yet # and have no EndTime yet
$sql .= ' WHERE ( '.$self->{Sql}.' )'; $sql .= ' WHERE ( '.$self->{Sql}.' )';
} else {
# Only include closed events (events with valid EndTime)
$sql .= ' WHERE (E.EndTime IS NOT NULL) AND ( '.$self->{Sql}.' )';
}
} }
my @auto_terms; my @auto_terms;
if ( $self->{AutoArchive} ) { if ( $self->{AutoArchive} ) {
@ -458,6 +438,15 @@ sub DateTimeToSQL {
return POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($dt_val)); return POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($dt_val));
} }
sub User {
my $self = shift;
$$self{User} = shift if @_;
if ( ! $$self{User} and $$self{UserId} ) {
$$self{User} = ZoneMinder::User->find_one(Id=>$$self{UserId});
}
return $$self{User};
}
1; 1;
__END__ __END__
# Below is stub documentation for your module. You'd better edit it! # Below is stub documentation for your module. You'd better edit it!

View File

@ -28,6 +28,9 @@ our %EXPORT_TAGS = (
makePath makePath
jsonEncode jsonEncode
jsonDecode jsonDecode
systemStatus
packageControl
daemonControl
) ] ) ]
); );
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -531,6 +534,38 @@ sub jsonDecode {
return $result; return $result;
} }
sub packageControl {
my $command = shift;
my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command;
$string .= ' 2>/dev/null >&- <&- >/dev/null';
executeShellCommand($string);
}
sub daemonControl {
my ($command, $daemon, $args) = @_;
my $string = $Config{ZM_PATH_BIN}.'/zmdc.pl '.$command;
if ( $daemon ) {
$string .= ' ' . $daemon;
if ( $args ) {
$string .= ' ' . $args;
}
}
#$string .= ' 2>/dev/null >&- <&- >/dev/null';
executeShellCommand($string);
}
sub systemStatus {
my $command = $Config{ZM_PATH_BIN}.'/zmdc.pl check';
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
$output = '' if !defined($output);
chomp($output);
Debug("Command: $command Output: $output");
}
return $output;
}
1; 1;
__END__ __END__
# Below is stub documentation for your module. You'd better edit it! # Below is stub documentation for your module. You'd better edit it!
@ -542,7 +577,6 @@ ZoneMinder::General - Utility Functions for ZoneMinder
=head1 SYNOPSIS =head1 SYNOPSIS
use ZoneMinder::General; use ZoneMinder::General;
blah blah blah
=head1 DESCRIPTION =head1 DESCRIPTION
@ -561,6 +595,9 @@ of the ZoneMinder scripts
makePath makePath
jsonEncode jsonEncode
jsonDecode jsonDecode
packageControl
daemonControl
systemStatus
) ] ) ]

View File

@ -0,0 +1,80 @@
# ==========================================================================
#
# ZoneMinder User Module
# Copyright (C) 2020 ZoneMinder LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ==========================================================================
package ZoneMinder::Frame;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Object;
use parent qw(ZoneMinder::Object);
use vars qw/ $table $primary_key %fields /;
$table = 'Users';
$primary_key = 'Id';
%fields = map { $_ => $_ } qw(
Id
Username
Password
Language
Enabled
Stream
Events
Control
Monitors
Groups
Devices
System
MaxBandwidth
MonitorIds
TokenMinExpiry
APIEnabled
);
1;
__END__
=head1 NAME
ZoneMinder::User - Perl Class for Users
=head1 SYNOPSIS
use ZoneMinder::User;
=head1 AUTHOR
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2017 ZoneMinder LLC
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -119,8 +119,13 @@ GetOptions(
) or pod2usage(-exitstatus => -1); ) or pod2usage(-exitstatus => -1);
my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } ); my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } );
if ( !$dbh ) {
die "Unable to connect to db\n";
}
$Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_USER} = $dbUser;
$Config{ZM_DB_PASS} = $dbPass; $Config{ZM_DB_PASS} = $dbPass;
# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it.
$dbPass =~ s/'/\\'/g;
if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) ) { if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) ) {
if ( $Config{ZM_DYN_DB_VERSION} ) { if ( $Config{ZM_DYN_DB_VERSION} ) {
@ -137,7 +142,7 @@ if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0))
} }
if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); print('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
my $currVersion = $Config{ZM_DYN_CURR_VERSION}; my $currVersion = $Config{ZM_DYN_CURR_VERSION};
my $lastVersion = $Config{ZM_DYN_LAST_VERSION}; my $lastVersion = $Config{ZM_DYN_LAST_VERSION};
@ -148,18 +153,18 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'"; my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'";
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute($currVersion) or die("Can't execute: ".$sth->errstr());
$sth->finish(); $sth->finish();
} }
while ( 1 ) { while ( 1 ) {
my $now = time(); my $now = time();
if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) { if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) {
Info( "Checking for updates\n" ); Info('Checking for updates');
use LWP::UserAgent; use LWP::UserAgent;
my $ua = LWP::UserAgent->new; my $ua = LWP::UserAgent->new;
$ua->agent( "ZoneMinder Update Agent/".ZM_VERSION ); $ua->agent('ZoneMinder Update Agent/'.ZM_VERSION);
if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { if ( $Config{ZM_UPDATE_CHECK_PROXY} ) {
$ua->proxy('http', $Config{ZM_UPDATE_CHECK_PROXY}); $ua->proxy('http', $Config{ZM_UPDATE_CHECK_PROXY});
} }
@ -171,24 +176,24 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
chomp($lastVersion); chomp($lastVersion);
$lastCheck = $now; $lastCheck = $now;
Info( "Got version: '".$lastVersion."'\n" ); Info('Got version: '.$lastVersion);
my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'"; my $lv_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'';
my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr()); my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr());
my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr()); my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr());
$lv_sth->finish(); $lv_sth->finish();
my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'"; my $lc_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\'';
my $lc_sth = $dbh->prepare_cached($lc_sql) or die("Can't prepare '$lc_sql': ".$dbh->errstr()); my $lc_sth = $dbh->prepare_cached($lc_sql) or die("Can't prepare '$lc_sql': ".$dbh->errstr());
my $lc_res = $lc_sth->execute($lastCheck) or die("Can't execute: ".$lc_sth->errstr()); my $lc_res = $lc_sth->execute($lastCheck) or die("Can't execute: ".$lc_sth->errstr());
$lc_sth->finish(); $lc_sth->finish();
} else { } else {
Error( "Error check failed: '".$res->status_line()."'\n" ); Error('Error check failed: \''.$res->status_line().'\'');
} }
} }
sleep(3600); sleep(3600);
} }
print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); print('Update agent exiting at '.strftime('%y/%m/%d %H:%M:%S', localtime())."\n");
} }
if ( $rename ) { if ( $rename ) {
@ -207,17 +212,17 @@ if ( $rename ) {
if ( $file !~ /(capture|analyse)-(\d+)(\.jpg)/ ) { if ( $file !~ /(capture|analyse)-(\d+)(\.jpg)/ ) {
return; return;
} }
my $newFile = "$2-$1$3"; my $newFile = $2.'-'.$1.$3;
print("Renaming '$file' to '$newFile'\n"); print("Renaming '$file' to '$newFile'\n");
rename($file, $newFile) or warn("Can't rename '$file' to '$newFile'"); rename($file, $newFile) or warn("Can't rename '$file' to '$newFile'");
} }
File::Find::find(\&renameImage, '.'); File::Find::find(\&renameImage, '.');
} } # end if rename
if ( $zoneFix ) {
my $sql = "select Z.*, M.Width as MonitorWidth, M.Height as MonitorHeight from Zones as Z inner join Monitors as M on Z.MonitorId = M.Id where Z.Units = 'Percent'"; if ( $zoneFix ) {
my $sql = "SELECT Z.*, M.Width AS MonitorWidth, M.Height AS MonitorHeight FROM Zones AS Z INNER JOIN Monitors AS M ON Z.MonitorId = M.Id WHERE Z.Units = 'Percent'";
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
my @zones; my @zones;
@ -226,7 +231,7 @@ if ( $zoneFix ) {
} }
$sth->finish(); $sth->finish();
$sql = 'update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?'; $sql = 'UPDATE Zones SET MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? WHERE Id = ?';
$sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
foreach my $zone ( @zones ) { foreach my $zone ( @zones ) {
my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100;
@ -244,7 +249,8 @@ if ( $zoneFix ) {
) or die("Can't execute: ".$sth->errstr()); ) or die("Can't execute: ".$sth->errstr());
} }
$sth->finish(); $sth->finish();
} } # end if zoneFix
if ( $migrateEvents ) { if ( $migrateEvents ) {
my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2]; my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2];
my $webGid = (getgrnam($Config{ZM_WEB_USER}))[2]; my $webGid = (getgrnam($Config{ZM_WEB_USER}))[2];
@ -260,7 +266,11 @@ if ( $migrateEvents ) {
$< = $webUid; $< = $webUid;
$> = $webUid; $> = $webUid;
print( "\nAbout to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding.\nThis process is not easily reversible. Are you sure you wish to proceed?\n\nPress 'y' to continue or 'n' to abort : " ); print('
About to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding.
This process is not easily reversible. Are you sure you wish to proceed?
Press \'y\' to continue or \'n\' to abort : ');
my $response = <STDIN>; my $response = <STDIN>;
chomp($response); chomp($response);
while ( $response !~ /^[yYnN]$/ ) { while ( $response !~ /^[yYnN]$/ ) {
@ -273,7 +283,7 @@ if ( $migrateEvents ) {
print("Converting all events to deep storage.\n"); print("Converting all events to deep storage.\n");
chdir($Config{ZM_PATH_WEB}); chdir($Config{ZM_PATH_WEB});
my $sql = "select *, unix_timestamp(StartTime) as UnixStartTime from Events"; my $sql = 'SELECT *, unix_timestamp(StartTime) AS UnixStartTime FROM Events';
my $sth = $dbh->prepare_cached($sql) or Fatal("Can't prepare '$sql': ".$dbh->errstr()); my $sth = $dbh->prepare_cached($sql) or Fatal("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute(); my $res = $sth->execute();
if ( !$res ) { if ( !$res ) {
@ -288,9 +298,9 @@ if ( $migrateEvents ) {
next; next;
} }
print( "Converting event ".$event->{Id}."\n" ); print('Converting event '.$event->{Id}."\n");
my $newDatePath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d", localtime($event->{UnixStartTime}) ); my $newDatePath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime('%y/%m/%d', localtime($event->{UnixStartTime}));
my $newTimePath = strftime( "%H/%M/%S", localtime($event->{UnixStartTime}) ); my $newTimePath = strftime('%H/%M/%S', localtime($event->{UnixStartTime}));
my $newEventPath = $newDatePath.'/'.$newTimePath; my $newEventPath = $newDatePath.'/'.$newTimePath;
( my $truncEventPath = $newEventPath ) =~ s|/\d+$||; ( my $truncEventPath = $newEventPath ) =~ s|/\d+$||;
makePath($truncEventPath, $Config{ZM_PATH_WEB}); makePath($truncEventPath, $Config{ZM_PATH_WEB});
@ -311,6 +321,7 @@ if ( $migrateEvents ) {
print("Aborting event conversion.\n\n"); print("Aborting event conversion.\n\n");
} }
} }
if ( $freshen ) { if ( $freshen ) {
print("\nFreshening configuration in database\n"); print("\nFreshening configuration in database\n");
migratePaths(); migratePaths();
@ -333,8 +344,13 @@ if ( $interactive ) {
$sth->finish(); $sth->finish();
if ( @MyISAM_Tables ) { if ( @MyISAM_Tables ) {
print( "\nPrevious versions of ZoneMinder used the MyISAM database engine.\nHowever, the recommended database engine is InnoDB.\n"); print('
print( "\nHint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.\n\nPress 'y' to convert your tables to InnoDB or 'n' to skip : "); Previous versions of ZoneMinder used the MyISAM database engine.
However, the recommended database engine is InnoDB.
Hint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.
Press \'y\' to convert your tables to InnoDB or \'n\' to skip : ');
my $response = <STDIN>; my $response = <STDIN>;
chomp( $response ); chomp( $response );
if ( $response =~ /^[yY]$/ ) { if ( $response =~ /^[yY]$/ ) {
@ -360,18 +376,26 @@ if ( $version ) {
exit(0); exit(0);
} }
my $start_zm = 0;
print("\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n"); print("\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n");
if ( $interactive ) { if ( $interactive ) {
if ( $Config{ZM_DYN_DB_VERSION} && $Config{ZM_DYN_DB_VERSION} ne $version ) { if ( $Config{ZM_DYN_DB_VERSION} && ($Config{ZM_DYN_DB_VERSION} ne $version) ) {
print( "\nWARNING - You have specified an upgrade from version $version but the database version found is ".$Config{ZM_DYN_DB_VERSION}.". Is this correct?\nPress enter to continue or ctrl-C to abort : " ); print("\nWARNING - You have specified an upgrade from version $version but the database version found is $Config{ZM_DYN_DB_VERSION}. Is this correct?\nPress enter to continue or ctrl-C to abort : ");
my $response = <STDIN>; my $response = <STDIN>;
} }
print( "\nPlease ensure that ZoneMinder is stopped on your system prior to upgrading the database.\nPress enter to continue or ctrl-C to stop : " ); if ( systemStatus() eq 'running' ) {
print"\nZoneMinder system appears to be running. While not strictly required, it is advised to stop ZM during the update process. Would you like to stop ZM now? [Yn]:";
my $response = <STDIN>; my $response = <STDIN>;
chomp($response);
if ( $response !~ /Yy/ ) {
packageControl('stop');
$start_zm = 1;
}
}
print("\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : "); print("\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : ");
$response = <STDIN>; my $response = <STDIN>;
chomp($response); chomp($response);
while ( $response !~ /^[yYnN]$/ ) { while ( $response !~ /^[yYnN]$/ ) {
print("Please press 'y' for a backup or 'n' to continue only : "); print("Please press 'y' for a backup or 'n' to continue only : ");
@ -384,21 +408,22 @@ if ( $version ) {
my $command = 'mysqldump'; my $command = 'mysqldump';
if ( defined($portOrSocket) ) { if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) { if ( $portOrSocket =~ /^\// ) {
$command .= " -S".$portOrSocket; $command .= ' -S'.$portOrSocket;
} else { } else {
$command .= " -h".$host." -P".$portOrSocket; $command .= ' -h'.$host.' -P'.$portOrSocket;
} }
} else { } else {
$command .= " -h".$host; $command .= ' -h'.$host;
} }
if ( $dbUser ) { if ( $dbUser ) {
$command .= ' -u'.$dbUser; $command .= ' -u'.$dbUser;
$command .= ' -p"'.$dbPass.'"' if $dbPass; $command .= ' -p\''.$dbPass.'\'' if $dbPass;
} }
my $backup = "@ZM_TMPDIR@/".$Config{ZM_DB_NAME}."-".$version.".dump"; my $backup = '@ZM_TMPDIR@/'.$Config{ZM_DB_NAME}.'-'.$version.'.dump';
$command .= " --add-drop-table --databases ".$Config{ZM_DB_NAME}." > ".$backup; $command .= ' --add-drop-table --databases '.$Config{ZM_DB_NAME}.' > '.$backup;
print("Creating backup to $backup. This may take several minutes.\n"); print("Creating backup to $backup. This may take several minutes.\n");
print( "Executing '$command'\n" ) if ( logDebugging() ); ($command) = $command =~ /(.*)/; # detaint
print("Executing '$command'\n") if logDebugging();
my $output = qx($command); my $output = qx($command);
my $status = $? >> 8; my $status = $? >> 8;
if ( $status || logDebugging() ) { if ( $status || logDebugging() ) {
@ -933,7 +958,12 @@ if ( $version ) {
#my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); #my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
#my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() ); #my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
#$sth->finish(); #$sth->finish();
print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n"); print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n");
if ( $start_zm ) {
print("Starting ZM since we stopped it for the update\n");
packageControl('start');
}
} # end if version } # end if version
zmDbDisconnect(); zmDbDisconnect();
@ -969,7 +999,7 @@ sub patchDB {
my $dbh = shift; my $dbh = shift;
my $version = shift; my $version = shift;
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ) if $Config{ZM_DB_HOST};
my $command = 'mysql'; my $command = 'mysql';
if ( defined($portOrSocket) ) { if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) { if ( $portOrSocket =~ /^\// ) {
@ -977,12 +1007,12 @@ sub patchDB {
} else { } else {
$command .= ' -h'.$host.' -P'.$portOrSocket; $command .= ' -h'.$host.' -P'.$portOrSocket;
} }
} else { } elsif ( $host ) {
$command .= ' -h'.$host; $command .= ' -h'.$host;
} }
if ( $dbUser ) { if ( $dbUser ) {
$command .= ' -u'.$dbUser; $command .= ' -u'.$dbUser;
$command .= ' -p"'.$dbPass.'"' if $dbPass; $command .= ' -p\''.$dbPass.'\'' if $dbPass;
} }
$command .= ' '.$Config{ZM_DB_NAME}.' < '; $command .= ' '.$Config{ZM_DB_NAME}.' < ';
if ( $updateDir ) { if ( $updateDir ) {
@ -993,6 +1023,7 @@ sub patchDB {
$command .= '/zm_update-'.$version.'.sql'; $command .= '/zm_update-'.$version.'.sql';
print("Executing '$command'\n") if logDebugging(); print("Executing '$command'\n") if logDebugging();
($command) = $command =~ /(.*)/; # detaint
my $output = qx($command); my $output = qx($command);
my $status = $? >> 8; my $status = $? >> 8;
if ( $status || logDebugging() ) { if ( $status || logDebugging() ) {

View File

@ -976,10 +976,12 @@ int FfmpegCamera::CaptureAndRecord(
return -1; return -1;
} }
#if HAVE_LIBAVUTIL_HWCONTEXT_H #if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
if ( (ret == AVERROR_INVALIDDATA ) && (hw_pix_fmt != AV_PIX_FMT_NONE) ) { if ( (ret == AVERROR_INVALIDDATA ) && (hw_pix_fmt != AV_PIX_FMT_NONE) ) {
use_hwaccel = false; use_hwaccel = false;
return -1; return -1;
} }
#endif
#endif #endif
} }
zm_av_packet_unref(&packet); zm_av_packet_unref(&packet);

View File

@ -1967,10 +1967,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
unsigned char *temp_ptr = ptr; unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f; int f;
if (size == 2) if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
else } else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
if ( f & (zm_text_bitmask >> i) ) { if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) if ( !fg_trans )
@ -1989,10 +1998,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
unsigned char *temp_ptr = ptr; unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f; int f;
if (size == 2) if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
else } else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) { for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) {
if ( f & (zm_text_bitmask >> i) ) { if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) { if ( !fg_trans ) {
@ -2016,10 +2034,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
Rgb* temp_ptr = (Rgb*)ptr; Rgb* temp_ptr = (Rgb*)ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f; int f;
if (size == 2) if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
else } else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
if ( f & (zm_text_bitmask >> i) ) { if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) { if ( !fg_trans ) {

View File

@ -1276,54 +1276,60 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
bool LocalCamera::GetCurrentSettings(const char *device, char *output, int version, bool verbose) { bool LocalCamera::GetCurrentSettings(const char *device, char *output, int version, bool verbose) {
output[0] = 0; output[0] = 0;
char *output_ptr = output;
char queryDevice[PATH_MAX] = ""; char queryDevice[PATH_MAX] = "";
int devIndex = 0; int devIndex = 0;
do { do {
if ( device ) if ( device ) {
strncpy(queryDevice, device, sizeof(queryDevice)-1); strncpy(queryDevice, device, sizeof(queryDevice)-1);
else } else {
sprintf(queryDevice, "/dev/video%d", devIndex); sprintf(queryDevice, "/dev/video%d", devIndex);
}
if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) { if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) {
if ( device ) { if ( device ) {
Error("Failed to open video device %s: %s", queryDevice, strerror(errno)); Error("Failed to open video device %s: %s", queryDevice, strerror(errno));
if ( verbose ) if ( verbose )
sprintf(output+strlen(output), "Error, failed to open video device %s: %s\n", output_ptr += sprintf(output_ptr, "Error, failed to open video device %s: %s\n",
queryDevice, strerror(errno)); queryDevice, strerror(errno));
else else
sprintf(output+strlen(output), "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} else { } else {
return true; return true;
} }
} }
if ( verbose ) if ( verbose ) {
sprintf(output+strlen(output), "Video Device: %s\n", queryDevice); output_ptr += sprintf(output_ptr, "Video Device: %s\n", queryDevice);
else } else {
sprintf(output+strlen(output), "d:%s|", queryDevice); output_ptr += sprintf(output_ptr, "d:%s|", queryDevice);
}
#if ZM_HAS_V4L2 #if ZM_HAS_V4L2
if ( version == 2 ) { if ( version == 2 ) {
struct v4l2_capability vid_cap; struct v4l2_capability vid_cap;
if ( vidioctl(vid_fd, VIDIOC_QUERYCAP, &vid_cap) < 0 ) { if ( vidioctl(vid_fd, VIDIOC_QUERYCAP, &vid_cap) < 0 ) {
Error("Failed to query video device: %s", strerror(errno)); Error("Failed to query video device: %s", strerror(errno));
if ( verbose ) if ( verbose ) {
sprintf(output, "Error, failed to query video capabilities %s: %s\n", output_ptr += sprintf(output_ptr, "Error, failed to query video capabilities %s: %s\n",
queryDevice, strerror(errno)); queryDevice, strerror(errno));
else } else {
sprintf(output, "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
}
if ( device )
return false; return false;
} }
if ( verbose ) { if ( verbose ) {
sprintf(output+strlen(output), "General Capabilities\n"); output_ptr += sprintf(output_ptr, "General Capabilities\n"
sprintf(output+strlen(output), " Driver: %s\n", vid_cap.driver); " Driver: %s\n"
sprintf(output+strlen(output), " Card: %s\n", vid_cap.card); " Card: %s\n"
sprintf(output+strlen(output), " Bus: %s\n", vid_cap.bus_info); " Bus: %s\n"
sprintf(output+strlen(output), " Version: %u.%u.%u\n", " Version: %u.%u.%u\n"
(vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff); " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
sprintf(output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.driver, vid_cap.card, vid_cap.bus_info,
(vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff,
vid_cap.capabilities, vid_cap.capabilities,
capString(vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)"), capString(vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)"),
capString(vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output"), capString(vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output"),
@ -1345,17 +1351,16 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
capString(vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)") capString(vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)")
); );
} else { } else {
sprintf(output+strlen(output), "D:%s|", vid_cap.driver); output_ptr += sprintf(output_ptr, "D:%s|C:%s|B:%s|V:%u.%u.%u|T:0x%x|"
sprintf(output+strlen(output), "C:%s|", vid_cap.card); , vid_cap.driver
sprintf(output+strlen(output), "B:%s|", vid_cap.bus_info); , vid_cap.card
sprintf(output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff); , vid_cap.bus_info
sprintf(output+strlen(output), "T:0x%x|", vid_cap.capabilities); , (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff
, vid_cap.capabilities);
} }
if ( verbose ) output_ptr += sprintf(output_ptr, verbose ? " Standards:\n" : "S:");
sprintf(output+strlen(output), " Standards:\n");
else
sprintf(output+strlen(output), "S:");
struct v4l2_standard standard; struct v4l2_standard standard;
int standardIndex = 0; int standardIndex = 0;
do { do {
@ -1370,25 +1375,20 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} else { } else {
Error("Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno)); Error("Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno));
if ( verbose ) if ( verbose )
sprintf(output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno)); output_ptr += sprintf(output_ptr, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno));
else else
sprintf(output, "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
// Why return? Why not continue trying other things?
return false; return false;
} }
} }
if ( verbose ) output_ptr += sprintf(output_ptr, (verbose ? " %s\n" : "%s/"), standard.name);
sprintf(output+strlen(output), " %s\n", standard.name); } while ( standardIndex++ >= 0 );
else
sprintf(output+strlen(output), "%s/", standard.name);
}
while ( standardIndex++ >= 0 );
if ( !verbose && output[strlen(output)-1] == '/')
output[strlen(output)-1] = '|';
if ( verbose ) if ( !verbose && (*(output_ptr-1) == '/') )
sprintf(output+strlen(output), " Formats:\n"); *(output_ptr-1) = '|';
else
sprintf(output+strlen(output), "F:"); output_ptr += sprintf(output_ptr, verbose ? " Formats:\n" : "F:");
struct v4l2_fmtdesc format; struct v4l2_fmtdesc format;
int formatIndex = 0; int formatIndex = 0;
do { do {
@ -1403,15 +1403,15 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} else { } else {
Error("Failed to enumerate format %d: %s", format.index, strerror(errno)); Error("Failed to enumerate format %d: %s", format.index, strerror(errno));
if ( verbose ) if ( verbose )
sprintf(output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno)); output_ptr += sprintf(output_ptr, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno));
else else
sprintf(output, "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
} }
if ( verbose ) if ( verbose )
sprintf( output_ptr += sprintf(
output+strlen(output), output_ptr,
" %s (0x%02hhx%02hhx%02hhx%02hhx)\n", " %s (0x%02hhx%02hhx%02hhx%02hhx)\n",
format.description, format.description,
(format.pixelformat>>24)&0xff, (format.pixelformat>>24)&0xff,
@ -1419,18 +1419,19 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
(format.pixelformat>>8)&0xff, (format.pixelformat>>8)&0xff,
format.pixelformat&0xff); format.pixelformat&0xff);
else else
sprintf( output_ptr += sprintf(
output+strlen(output), output_ptr,
"0x%02hhx%02hhx%02hhx%02hhx/", "0x%02hhx%02hhx%02hhx%02hhx/",
(format.pixelformat>>24)&0xff, (format.pixelformat>>24)&0xff,
(format.pixelformat>>16)&0xff, (format.pixelformat>>16)&0xff,
(format.pixelformat>>8)&0xff, (format.pixelformat>>8)&0xff,
(format.pixelformat)&0xff); (format.pixelformat)&0xff);
} while ( formatIndex++ >= 0 ); } while ( formatIndex++ >= 0 );
if ( !verbose ) if ( !verbose )
output[strlen(output)-1] = '|'; *(output_ptr-1) = '|';
else else
sprintf(output+strlen(output), "Crop Capabilities\n"); output_ptr += sprintf(output_ptr, "Crop Capabilities\n");
struct v4l2_cropcap cropcap; struct v4l2_cropcap cropcap;
memset(&cropcap, 0, sizeof(cropcap)); memset(&cropcap, 0, sizeof(cropcap));
@ -1442,10 +1443,10 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} }
if ( verbose ) { if ( verbose ) {
sprintf(output+strlen(output), " Cropping is not supported\n"); output_ptr += sprintf(output_ptr, " Cropping is not supported\n");
} else { } else {
/* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */
sprintf(output+strlen(output), "B:%dx%d|",0,0); output_ptr += sprintf(output_ptr, "B:%dx%d|", 0, 0);
} }
} else { } else {
struct v4l2_crop crop; struct v4l2_crop crop;
@ -1459,19 +1460,23 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} }
if ( verbose ) { if ( verbose ) {
sprintf(output+strlen(output), " Cropping is not supported\n"); output_ptr += sprintf(output_ptr, " Cropping is not supported\n");
} else { } else {
/* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */
sprintf(output+strlen(output), "B:%dx%d|",0,0); output_ptr += sprintf(output_ptr, "B:%dx%d|",0,0);
} }
} else { } else {
/* Cropping supported */ /* Cropping supported */
if ( verbose ) { if ( verbose ) {
sprintf(output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height); output_ptr += sprintf(output_ptr,
sprintf(output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height); " Bounds: %d x %d\n"
sprintf(output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height); " Default: %d x %d\n"
" Current: %d x %d\n"
, cropcap.bounds.width, cropcap.bounds.height
, cropcap.defrect.width, cropcap.defrect.height
, crop.c.width, crop.c.height);
} else { } else {
sprintf(output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height); output_ptr += sprintf(output_ptr, "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height);
} }
} }
} /* Crop code */ } /* Crop code */
@ -1488,17 +1493,14 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} }
Error("Failed to enumerate input %d: %s", input.index, strerror(errno)); Error("Failed to enumerate input %d: %s", input.index, strerror(errno));
if ( verbose ) if ( verbose )
sprintf(output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno)); output_ptr += sprintf(output_ptr, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno));
else else
sprintf(output, "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
} while ( inputIndex++ >= 0 ); } while ( inputIndex++ >= 0 );
if ( verbose ) output_ptr += sprintf(output_ptr, verbose?"Inputs: %d\n":"I:%d|", inputIndex);
sprintf(output+strlen(output), "Inputs: %d\n", inputIndex);
else
sprintf(output+strlen(output), "I:%d|", inputIndex);
inputIndex = 0; inputIndex = 0;
do { do {
@ -1512,47 +1514,56 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} }
Error("Failed to enumerate input %d: %s", input.index, strerror(errno)); Error("Failed to enumerate input %d: %s", input.index, strerror(errno));
if ( verbose ) if ( verbose )
sprintf(output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno)); output_ptr += sprintf(output_ptr, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno));
else else
sprintf(output, "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
if ( vidioctl(vid_fd, VIDIOC_S_INPUT, &input.index) < 0 ) { if ( vidioctl(vid_fd, VIDIOC_S_INPUT, &input.index) < 0 ) {
Error("Failed to set video input %d: %s", input.index, strerror(errno)); Error("Failed to set video input %d: %s", input.index, strerror(errno));
if ( verbose ) if ( verbose )
sprintf(output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno)); output_ptr += sprintf(output_ptr, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno));
else else
sprintf(output, "error%d\n", errno); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
if ( verbose ) { if ( verbose ) {
sprintf( output+strlen(output), " Input %d\n", input.index ); output_ptr += sprintf( output,
sprintf( output+strlen(output), " Name: %s\n", input.name ); " Input %d\n"
sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); " Name: %s\n"
sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); " Type: %s\n"
sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); " Audioset: %08x\n"
" Standards: 0x%llx\n"
, input.index
, input.name
, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown")
, input.audioset
, input.std );
} else { } else {
sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); output_ptr += sprintf( output_ptr, "i%d:%s|i%dT:%s|i%dS:%llx|"
sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); , input.index, input.name
sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); , input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown")
, input.index, input.std);
} }
if ( verbose ) { if ( verbose ) {
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); output_ptr += sprintf( output_ptr, " %s %s %s %s"
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); , capString(input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)")
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); , capString(input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)")
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); , capString(input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "")
, capString(input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", ""));
} else { } else {
sprintf( output+strlen(output), "i%dSP:%d|", input.index, (input.status&V4L2_IN_ST_NO_POWER)?0:1 ); output_ptr += sprintf( output_ptr, "i%dSP:%d|i%dSS:%d|i%dSC:%d|i%dHP:%d|"
sprintf( output+strlen(output), "i%dSS:%d|", input.index, (input.status&V4L2_IN_ST_NO_SIGNAL)?0:1 ); , input.index, (input.status&V4L2_IN_ST_NO_POWER)?0:1
sprintf( output+strlen(output), "i%dSC:%d|", input.index, (input.status&V4L2_IN_ST_NO_COLOR)?0:1 ); , input.index, (input.status&V4L2_IN_ST_NO_SIGNAL)?0:1
sprintf( output+strlen(output), "i%dHP:%d|", input.index, (input.status&V4L2_IN_ST_NO_H_LOCK)?0:1 ); , input.index, (input.status&V4L2_IN_ST_NO_COLOR)?0:1
, input.index, (input.status&V4L2_IN_ST_NO_H_LOCK)?0:1 );
} }
} while ( inputIndex++ >= 0 ); } while ( inputIndex++ >= 0 );
if ( !verbose ) if ( !verbose )
output[strlen(output)-1] = '\n'; *(output_ptr-1) = '\n';
} }
#endif // ZM_HAS_V4L2 #endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1 #if ZM_HAS_V4L1
@ -1562,15 +1573,25 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
if ( ioctl(vid_fd, VIDIOCGCAP, &vid_cap) < 0 ) { if ( ioctl(vid_fd, VIDIOCGCAP, &vid_cap) < 0 ) {
Error("Failed to get video capabilities: %s", strerror(errno)); Error("Failed to get video capabilities: %s", strerror(errno));
if ( verbose ) if ( verbose )
sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); output_ptr += sprintf(output_ptr,
"Error, failed to get video capabilities %s: %s\n",
queryDevice, strerror(errno));
else else
sprintf( output, "error%d\n", errno ); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return( false ); return false;
} }
if ( verbose ) { if ( verbose ) {
sprintf( output+strlen(output), "Video Capabilities\n" ); output_ptr += sprintf( output_ptr, "Video Capabilities\n"
sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); " Name: %s\n"
sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
" Video Channels: %d\n"
" Audio Channels: %d\n"
" Maximum Width: %d\n"
" Maximum Height: %d\n"
" Minimum Width: %d\n"
" Minimum Height: %d\n",
vid_cap.name,
vid_cap.type,
(vid_cap.type&VID_TYPE_CAPTURE)?" Can capture\n":"", (vid_cap.type&VID_TYPE_CAPTURE)?" Can capture\n":"",
(vid_cap.type&VID_TYPE_TUNER)?" Can tune\n":"", (vid_cap.type&VID_TYPE_TUNER)?" Can tune\n":"",
(vid_cap.type&VID_TYPE_TELETEXT)?" Does teletext\n":"", (vid_cap.type&VID_TYPE_TELETEXT)?" Does teletext\n":"",
@ -1584,25 +1605,23 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
(vid_cap.type&VID_TYPE_MPEG_DECODER)?" Can decode MPEG streams\n":"", (vid_cap.type&VID_TYPE_MPEG_DECODER)?" Can decode MPEG streams\n":"",
(vid_cap.type&VID_TYPE_MPEG_ENCODER)?" Can encode MPEG streams\n":"", (vid_cap.type&VID_TYPE_MPEG_ENCODER)?" Can encode MPEG streams\n":"",
(vid_cap.type&VID_TYPE_MJPEG_DECODER)?" Can decode MJPEG streams\n":"", (vid_cap.type&VID_TYPE_MJPEG_DECODER)?" Can decode MJPEG streams\n":"",
(vid_cap.type&VID_TYPE_MJPEG_ENCODER)?" Can encode MJPEG streams\n":"" (vid_cap.type&VID_TYPE_MJPEG_ENCODER)?" Can encode MJPEG streams\n":"",
); vid_cap.channels,
sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); vid_cap.audios,
sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); vid_cap.maxwidth,
sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); vid_cap.maxheight,
sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); vid_cap.minwidth,
sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); vid_cap.minheight );
sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); } else {
} output_ptr += sprintf(output_ptr, "N:%s|T:%d|nC:%d|nA:%d|mxW:%d|mxH:%d|mnW:%d|mnH:%d|"
else , vid_cap.name
{ , vid_cap.type
sprintf( output+strlen(output), "N:%s|", vid_cap.name ); , vid_cap.channels
sprintf( output+strlen(output), "T:%d|", vid_cap.type ); , vid_cap.audios
sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); , vid_cap.maxwidth
sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); , vid_cap.maxheight
sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); , vid_cap.minwidth
sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); , vid_cap.minheight);
sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth );
sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight );
} }
struct video_window vid_win; struct video_window vid_win;
@ -1610,22 +1629,25 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
if ( ioctl(vid_fd, VIDIOCGWIN, &vid_win) < 0 ) { if ( ioctl(vid_fd, VIDIOCGWIN, &vid_win) < 0 ) {
Error("Failed to get window attributes: %s", strerror(errno)); Error("Failed to get window attributes: %s", strerror(errno));
if ( verbose ) if ( verbose )
sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); output_ptr += sprintf(output_ptr, "Error, failed to get window attributes: %s\n", strerror(errno));
else else
sprintf( output, "error%d\n", errno ); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
if ( verbose ) { if ( verbose ) {
sprintf( output+strlen(output), "Window Attributes\n" ); output_ptr += sprintf(output_ptr,
sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); "Window Attributes\n"
sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); " X Offset: %d\n"
sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); " Y Offset: %d\n"
sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); " Width: %d\n"
" Height: %d\n"
, vid_win.x
, vid_win.y
, vid_win.width
, vid_win.height );
} else { } else {
sprintf( output+strlen(output), "X:%d|", vid_win.x ); output_ptr += sprintf(output_ptr, "X:%d|Y:%d|W:%d|H:%d|",
sprintf( output+strlen(output), "Y:%d|", vid_win.y ); vid_win.height, vid_win.x, vid_win.y, vid_win.width);
sprintf( output+strlen(output), "W:%d|", vid_win.width );
sprintf( output+strlen(output), "H:%d|", vid_win.height );
} }
struct video_picture vid_pic; struct video_picture vid_pic;
@ -1633,14 +1655,22 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
if ( ioctl(vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { if ( ioctl(vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) {
Error("Failed to get picture attributes: %s", strerror(errno)); Error("Failed to get picture attributes: %s", strerror(errno));
if ( verbose ) if ( verbose )
sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); output_ptr += sprintf(output_ptr, "Error, failed to get picture attributes: %s\n", strerror(errno));
else else
sprintf( output, "error%d\n", errno ); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
if ( verbose ) { if ( verbose ) {
sprintf( output+strlen(output), "Picture Attributes\n" ); output_ptr += sprintf(output_ptr,
sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, "Picture Attributes\n"
" Palette: %d - %s\n"
" Colour Depth: %d\n"
" Brightness: %d\n"
" Hue: %d\n"
" Colour :%d\n"
" Contrast: %d\n"
" Whiteness: %d\n"
, vid_pic.palette,
vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":(
vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":(
vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":(
@ -1659,21 +1689,24 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":(
vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":(
vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown"
)))))))))))))))))); ))))))))))))))))),
sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); vid_pic.depth,
sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); vid_pic.brightness,
sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); vid_pic.hue,
sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); vid_pic.colour,
sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); vid_pic.contrast,
sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); vid_pic.whiteness
);
} else { } else {
sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); output_ptr += sprintf(output_ptr, "P:%d|D:%d|B:%d|h:%d|Cl:%d|Cn:%d|w:%d|",
sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); vid_pic.palette,
sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); vid_pic.depth,
sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); vid_pic.brightness,
sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); vid_pic.hue,
sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); vid_pic.colour,
sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); vid_pic.contrast,
vid_pic.whiteness
);
} }
for ( int chan = 0; chan < vid_cap.channels; chan++ ) { for ( int chan = 0; chan < vid_cap.channels; chan++ ) {
@ -1683,39 +1716,47 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
if ( ioctl(vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) { if ( ioctl(vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) {
Error("Failed to get channel %d attributes: %s", chan, strerror(errno)); Error("Failed to get channel %d attributes: %s", chan, strerror(errno));
if ( verbose ) if ( verbose )
sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); output_ptr += sprintf(output_ptr, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno));
else else
sprintf( output, "error%d\n", errno ); output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false; return false;
} }
if ( verbose ) { if ( verbose ) {
sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); output_ptr += sprintf(output_ptr,
sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); "Channel %d Attributes\n"
sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); " Name: %s\n"
sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, " Channel: %d\n"
(vid_src.flags&VIDEO_VC_TUNER)?" Channel has a tuner\n":"", " Flags: %d\n%s%s"
(vid_src.flags&VIDEO_VC_AUDIO)?" Channel has audio\n":"" " Type: %d - %s\n"
); " Format: %d - %s\n"
sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, , chan
, vid_src.name
, vid_src.channel
, vid_src.flags
, (vid_src.flags&VIDEO_VC_TUNER)?" Channel has a tuner\n":""
, (vid_src.flags&VIDEO_VC_AUDIO)?" Channel has audio\n":""
, vid_src.type,
vid_src.type==VIDEO_TYPE_TV?"TV":( vid_src.type==VIDEO_TYPE_TV?"TV":(
vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown"
)); )
sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, , vid_src.norm,
vid_src.norm==VIDEO_MODE_PAL?"PAL":( vid_src.norm==VIDEO_MODE_PAL?"PAL":(
vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( vid_src.norm==VIDEO_MODE_NTSC?"NTSC":(
vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( vid_src.norm==VIDEO_MODE_SECAM?"SECAM":(
vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown"
)))); ))));
} else { } else {
sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); output_ptr += sprintf(output_ptr, "n%d:%s|C%d:%d|Fl%d:%x|T%d:%d|F%d:%d%s|"
sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); , chan, vid_src.name
sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); , chan, vid_src.channel
sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); , chan, vid_src.flags
sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); , chan, vid_src.type
, chan, vid_src.norm, chan==(vid_cap.channels-1)?"":","
);
} }
} }
if ( !verbose ) if ( !verbose )
output[strlen(output)-1] = '\n'; *output_ptr = '\n';
} }
#endif // ZM_HAS_V4L1 #endif // ZM_HAS_V4L1
close(vid_fd); close(vid_fd);

View File

@ -1 +1 @@
1.35.4 1.35.5

View File

@ -24,6 +24,7 @@ class Filter extends ZM_Object {
'AutoCopy' => 0, 'AutoCopy' => 0,
'AutoCopyTo' => 0, 'AutoCopyTo' => 0,
'UpdateDiskSpace' => 0, 'UpdateDiskSpace' => 0,
'UserId' => 0,
'Background' => 0, 'Background' => 0,
'Concurrent' => 0, 'Concurrent' => 0,
'Query_json' => '', 'Query_json' => '',

View File

@ -19,9 +19,9 @@ class Group extends ZM_Object {
public function delete() { public function delete() {
if ( property_exists($this, 'Id') ) { if ( property_exists($this, 'Id') ) {
dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'})); dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($this->{'Id'}));
dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'})); dbQuery('UPDATE `Groups` SET `ParentId`=NULL WHERE `ParentId`=?', array($this->{'Id'}));
dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'})); dbQuery('DELETE FROM `Groups` WHERE Id=?', array($this->{'Id'}));
if ( isset($_COOKIE['zmGroup']) ) { if ( isset($_COOKIE['zmGroup']) ) {
if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) { if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) {
unset($_COOKIE['zmGroup']); unset($_COOKIE['zmGroup']);
@ -47,7 +47,7 @@ class Group extends ZM_Object {
public function MonitorIds( ) { public function MonitorIds( ) {
if ( ! property_exists($this, 'MonitorIds') ) { if ( ! property_exists($this, 'MonitorIds') ) {
$this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'})); $this->{'MonitorIds'} = dbFetchAll('SELECT `MonitorId` FROM `Groups_Monitors` WHERE `GroupId`=?', 'MonitorId', array($this->{'Id'}));
} }
return $this->{'MonitorIds'}; return $this->{'MonitorIds'};
} }

View File

@ -306,7 +306,7 @@ class ZM_Object {
$fields = array_keys($fields); $fields = array_keys($fields);
if ( $this->Id() ) { if ( $this->Id() ) {
$sql = 'UPDATE '.$table.' SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?'; $sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?';
$values = array_map(function($field){ return $this->{$field};}, $fields); $values = array_map(function($field){ return $this->{$field};}, $fields);
$values[] = $this->{'Id'}; $values[] = $this->{'Id'};
if ( dbQuery($sql, $values) ) if ( dbQuery($sql, $values) )
@ -314,8 +314,8 @@ class ZM_Object {
} else { } else {
unset($fields['Id']); unset($fields['Id']);
$sql = 'INSERT INTO '.$table. $sql = 'INSERT INTO `'.$table.
' ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)). '` ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)).
') VALUES ('. ') VALUES ('.
implode(', ', array_map(function($field){return '?';}, $fields)).')'; implode(', ', array_map(function($field){return '?';}, $fields)).')';
@ -331,7 +331,7 @@ class ZM_Object {
public function delete() { public function delete() {
$class = get_class($this); $class = get_class($this);
$table = $class::$table; $table = $class::$table;
dbQuery("DELETE FROM $table WHERE Id=?", array($this->{'Id'})); dbQuery("DELETE FROM `$table` WHERE Id=?", array($this->{'Id'}));
if ( isset($object_cache[$class]) and isset($object_cache[$class][$this->{'Id'}]) ) if ( isset($object_cache[$class]) and isset($object_cache[$class][$this->{'Id'}]) )
unset($object_cache[$class][$this->{'Id'}]); unset($object_cache[$class][$this->{'Id'}]);
} }

50
web/includes/User.php Normal file
View File

@ -0,0 +1,50 @@
<?php
namespace ZM;
require_once('database.php');
require_once('Object.php');
class User extends ZM_Object {
protected static $table = 'Users';
protected $defaults = array(
'Id' => null,
'Username' => '',
'Password' => '',
'Language' => '',
'Enabled' => 1,
'Stream' => 'None',
'Events' => 'None',
'Control' => 'None',
'Monitors' => 'None',
'Groups' => 'None',
'Devices' => 'None',
'System'=> 'None',
'MaxBandwidth' =>'',
'MonitorIds' =>'',
'TokenMinExpiry' => 0,
'APIEnabled'=> 1,
);
public static function find( $parameters = array(), $options = array() ) {
return ZM_Object::_find(get_class(), $parameters, $options);
}
public static function find_one( $parameters = array(), $options = array() ) {
return ZM_Object::_find_one(get_class(), $parameters, $options);
}
public function Name( ) {
return $this->{'Username'};
}
public static function Indexed_By_Id() {
$results = array();
foreach ( ZM_Object::_find('ZM\User', null, array('order'=>'lower(Username)')) as $Object ) {
$results[$Object->Id()] = $Object;
}
return $results;
}
} # end class User
?>

View File

@ -25,21 +25,28 @@ define('DB_LOG_DEBUG', 2);
$GLOBALS['dbLogLevel'] = DB_LOG_OFF; $GLOBALS['dbLogLevel'] = DB_LOG_OFF;
$GLOBALS['dbConn'] = false; $GLOBALS['dbConn'] = false;
require_once('logger.php');
function dbConnect() { function dbConnect() {
global $dbConn; global $dbConn;
$dsn = ZM_DB_TYPE;
if ( ZM_DB_HOST ) {
if ( strpos(ZM_DB_HOST, ':') ) { if ( strpos(ZM_DB_HOST, ':') ) {
// Host variable may carry a port or socket. // Host variable may carry a port or socket.
list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2);
if ( ctype_digit($portOrSocket) ) { if ( ctype_digit($portOrSocket) ) {
$socket = ':host='.$host . ';port='.$portOrSocket; $dsn .= ':host='.$host.';port='.$portOrSocket.';';
} else { } else {
$socket = ':unix_socket='.$portOrSocket; $dsn .= ':unix_socket='.$portOrSocket.';';
} }
} else { } else {
$socket = ':host='.ZM_DB_HOST; $dsn .= ':host='.ZM_DB_HOST.';';
} }
} else {
$dsn .= ':host=localhost;';
}
$dsn .= 'dbname='.ZM_DB_NAME;
try { try {
$dbOptions = null; $dbOptions = null;
@ -49,21 +56,24 @@ function dbConnect() {
PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY, PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY,
PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT, PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT,
); );
$dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); $dbConn = new PDO($dsn, ZM_DB_USER, ZM_DB_PASS, $dbOptions);
} else { } else {
$dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); $dbConn = new PDO($dsn, ZM_DB_USER, ZM_DB_PASS);
} }
$dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $ex) { } catch(PDOException $ex) {
echo 'Unable to connect to ZM db.' . $ex->getMessage(); echo "Unable to connect to ZM db using dsn $dsn host".ZM_DB_HOST.' user: ('.ZM_DB_USER.') password ('.ZM_DB_PASS.': '.$ex->getMessage();
error_log('Unable to connect to ZM DB ' . $ex->getMessage()); error_log('Unable to connect to ZM DB ' . $ex->getMessage());
$dbConn = null; $dbConn = null;
} }
} return $dbConn;
} // end function dbConnect
dbConnect(); if ( !dbConnect() ) {
ZM\Fatal('Failed db connection');
}
function dbDisconnect() { function dbDisconnect() {
global $dbConn; global $dbConn;

View File

@ -1505,15 +1505,15 @@ function getLoad() {
function getDiskPercent($path = ZM_DIR_EVENTS) { function getDiskPercent($path = ZM_DIR_EVENTS) {
$total = disk_total_space($path); $total = disk_total_space($path);
if ( $total === false ) { if ( $total === false ) {
Error('disk_total_space returned false. Verify the web account user has access to ' . $path); ZM\Error('disk_total_space returned false. Verify the web account user has access to ' . $path);
return 0; return 0;
} elseif ( $total == 0 ) { } elseif ( $total == 0 ) {
Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); ZM\Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path);
return 100; return 100;
} }
$free = disk_free_space($path); $free = disk_free_space($path);
if ( $free === false ) { if ( $free === false ) {
Error('disk_free_space returned false. Verify the web account user has access to ' . $path); ZM\Error('disk_free_space returned false. Verify the web account user has access to ' . $path);
} }
$space = round((($total - $free) / $total) * 100); $space = round((($total - $free) / $total) * 100);
return $space; return $space;
@ -2069,7 +2069,7 @@ function logState() {
if ( $count['Level'] <= ZM\Logger::PANIC ) if ( $count['Level'] <= ZM\Logger::PANIC )
$count['Level'] = ZM\Logger::FATAL; $count['Level'] = ZM\Logger::FATAL;
if ( !($levelCount = $levelCounts[$count['Level']]) ) { if ( !($levelCount = $levelCounts[$count['Level']]) ) {
Error('Unexpected Log level '.$count['Level']); ZM\Error('Unexpected Log level '.$count['Level']);
next; next;
} }
if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) {

View File

@ -100,14 +100,17 @@ class Logger {
$tempDatabaseLevel = $options['databaseLevel']; $tempDatabaseLevel = $options['databaseLevel'];
else else
$tempDatabaseLevel = ZM_LOG_LEVEL_DATABASE; $tempDatabaseLevel = ZM_LOG_LEVEL_DATABASE;
if ( isset($options['fileLevel']) ) if ( isset($options['fileLevel']) )
$tempFileLevel = $options['fileLevel']; $tempFileLevel = $options['fileLevel'];
else else
$tempFileLevel = ZM_LOG_LEVEL_FILE; $tempFileLevel = ZM_LOG_LEVEL_FILE;
if ( isset($options['weblogLevel']) ) if ( isset($options['weblogLevel']) )
$tempWeblogLevel = $options['weblogLevel']; $tempWeblogLevel = $options['weblogLevel'];
else else
$tempWeblogLevel = ZM_LOG_LEVEL_WEBLOG; $tempWeblogLevel = ZM_LOG_LEVEL_WEBLOG;
if ( isset($options['syslogLevel']) ) if ( isset($options['syslogLevel']) )
$tempSyslogLevel = $options['syslogLevel']; $tempSyslogLevel = $options['syslogLevel'];
else else

View File

@ -376,6 +376,7 @@ $SLANG = array(
'Filters' => 'Filters', 'Filters' => 'Filters',
'FilterUnset' => 'You must specify a filter width and height', 'FilterUnset' => 'You must specify a filter width and height',
'FilterUploadEvents' => 'Upload all matches', 'FilterUploadEvents' => 'Upload all matches',
'FilterUser' => 'User to run filter as',
'FilterVideoEvents' => 'Create video for all matches', 'FilterVideoEvents' => 'Create video for all matches',
'First' => 'First', 'First' => 'First',
'FlippedHori' => 'Flipped Horizontally', 'FlippedHori' => 'Flipped Horizontally',

View File

@ -205,6 +205,7 @@ getBodyTopHTML();
<?php <?php
ob_start(); ob_start();
?> ?>
<div class="table-responsive">
<table class="table table-striped table-hover table-condensed consoleTable"> <table class="table table-striped table-hover table-condensed consoleTable">
<thead class="thead-highlight"> <thead class="thead-highlight">
<tr> <tr>
@ -394,5 +395,6 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div>
</form> </form>
<?php xhtmlFooter() ?> <?php xhtmlFooter() ?>

View File

@ -27,6 +27,7 @@ require_once('includes/Storage.php');
require_once('includes/Filter.php'); require_once('includes/Filter.php');
require_once('includes/Monitor.php'); require_once('includes/Monitor.php');
require_once('includes/Zone.php'); require_once('includes/Zone.php');
require_once('includes/User.php');
parseSort(); parseSort();
$filterNames = array(''=>translate('ChooseFilter')); $filterNames = array(''=>translate('ChooseFilter'));
@ -200,6 +201,18 @@ if ( (null !== $filter->Concurrent()) and $filter->Concurrent() )
<label for="filter[Name]"><?php echo translate('Name') ?></label> <label for="filter[Name]"><?php echo translate('Name') ?></label>
<input type="text" id="filter[Name]" name="filter[Name]" value="<?php echo validHtmlStr($filter->Name()) ?>" data-on-input-this="updateButtons"/> <input type="text" id="filter[Name]" name="filter[Name]" value="<?php echo validHtmlStr($filter->Name()) ?>" data-on-input-this="updateButtons"/>
</p> </p>
<?php if ( ZM_OPT_USE_AUTH ) { ?>
<p><label><?php echo translate('FilterUser') ?></label>
<?php
global $user;
echo htmlSelect('filter[UserId]',
ZM\User::Indexed_By_Id(),
//ZM\User::find(),
$filter->UserId() ? $filter->UserId() : $user['Id']
); ?>
</p>
<?php } ?>
<p>
<table id="fieldsTable" class="filterTable"> <table id="fieldsTable" class="filterTable">
<tbody> <tbody>
<?php <?php

View File

@ -747,9 +747,11 @@ function fetchImage( streamImage ) {
} }
function handleClick( event ) { function handleClick( event ) {
var target = event.target; var $target = $(event.target);
var x = event.page.x - $(target).getLeft(); var scaleX = parseInt(monitorWidth / $target.getWidth());
var y = event.page.y - $(target).getTop(); var scaleY = parseInt(monitorHeight / $target.getHeight());
var x = (event.page.x - $target.getLeft()) * scaleX;
var y = (event.page.y - $target.getTop()) * scaleY;
if ( showMode == 'events' || !imageControlMode ) { if ( showMode == 'events' || !imageControlMode ) {
if ( event.shift ) { if ( event.shift ) {
@ -848,7 +850,7 @@ function initPage() {
if ( refreshApplet && appletRefreshTime ) { if ( refreshApplet && appletRefreshTime ) {
appletRefresh.delay(appletRefreshTime*1000); appletRefresh.delay(appletRefreshTime*1000);
} }
if ( scale == 'auto' ) changeScale(); if ( scale == '0' || scale == 'auto' ) changeScale();
if ( window.history.length == 1 ) { if ( window.history.length == 1 ) {
$j('#closeControl').html(''); $j('#closeControl').html('');
} }

View File

@ -179,12 +179,16 @@ function toPixels(field, maxValue) {
if ( field.value != '' ) { if ( field.value != '' ) {
field.value = Math.round((field.value*maxValue)/100); field.value = Math.round((field.value*maxValue)/100);
} }
field.setAttribute('step', 1);
field.setAttribute('max', monitorArea);
} }
function toPercent(field, maxValue) { function toPercent(field, maxValue) {
if ( field.value != '' ) { if ( field.value != '' ) {
field.value = Math.round((100*100*field.value)/maxValue)/100; field.value = Math.round((100*100*field.value)/maxValue)/100;
} }
field.setAttribute('step', 0.01);
field.setAttribute('max', 100);
} }
function applyZoneUnits() { function applyZoneUnits() {

View File

@ -151,11 +151,11 @@ if ( $showControl ) {
} }
if ( $showZones ) { if ( $showZones ) {
?> ?>
<a id="ShowZones" href="?view=montage&amp;showZones=0">Hide Zones</a> <a id="HideZones" href="?view=montage&amp;showZones=0"><?php echo translate('Hide Zones')?></a>
<?php <?php
} else { } else {
?> ?>
<a id="ShowZones" href="?view=montage&amp;showZones=1">Show Zones</a> <a id="ShowZones" href="?view=montage&amp;showZones=1"><?php echo translate('Show Zones')?></a>
<?php <?php
} }
?> ?>

View File

@ -96,12 +96,14 @@ function probeProfiles($device_ep, $soapversion, $username, $password) {
if ( $lines = @execONVIF("profiles $device_ep $soapversion $username $password") ) { if ( $lines = @execONVIF("profiles $device_ep $soapversion $username $password") ) {
foreach ( $lines as $line ) { foreach ( $lines as $line ) {
$line = rtrim( $line ); $line = rtrim( $line );
if ( preg_match('|^(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+)\s*$|', $line, $matches) ) {
$stream_uri = $matches[7]; if ( preg_match('|^([^,]+),\s*([^,]+),\s*([^,]+),\s*(\d+),\s*(\d+),\s*(\d+),\s*([^,]+),\s*(.+)\s*$|', $line, $matches) ) {
$stream_uri = $matches[8];
// add user@pass to URI // add user@pass to URI
if ( preg_match('|^(\S+://)(.+)$|', $stream_uri, $tokens) ) { if ( preg_match('|^(\S+://)(.+)$|', $stream_uri, $tokens) ) {
$stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2]; $stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2];
} }
ZM\Warning(print_r($matches,1));
$profile = array( # 'monitor' part of camera $profile = array( # 'monitor' part of camera
'Type' => 'Ffmpeg', 'Type' => 'Ffmpeg',
@ -113,6 +115,7 @@ function probeProfiles($device_ep, $soapversion, $username, $password) {
'Profile' => $matches[1], 'Profile' => $matches[1],
'Name' => $matches[2], 'Name' => $matches[2],
'Encoding' => $matches[3], 'Encoding' => $matches[3],
'Transport' => $matches[7],
); );
$profiles[] = $profile; $profiles[] = $profile;
} else { } else {
@ -261,10 +264,17 @@ if ( !isset($_REQUEST['step']) || ($_REQUEST['step'] == '1') ) {
$monitor = $camera['monitor']; $monitor = $camera['monitor'];
$sourceString = "${profile['Name']} : ${profile['Encoding']}" . $sourceString = "${profile['Name']} : ${profile['Encoding']}" .
" (${profile['Width']}x${profile['Height']} @ ${profile['MaxFPS']}fps)"; " (${profile['Width']}x${profile['Height']} @ ${profile['MaxFPS']}fps ${profile['Transport']})";
// copy technical details // copy technical details
$monitor['Width'] = $profile['Width']; $monitor['Width'] = $profile['Width'];
$monitor['Height'] = $profile['Height']; $monitor['Height'] = $profile['Height'];
if ( $profile['Transport'] == 'RTP-Multicast' ) {
$monitor['Method'] = 'rtpMulti';
} else if ( $profile['Transport'] == 'RTP-Unicast' ) {
$monitor['Method'] = 'rtpUni';
} else {
$monitor['Method'] = 'rtpRtsp';
}
// The maxfps fields do not work for ip streams. Can re-enable if that is fixed. // The maxfps fields do not work for ip streams. Can re-enable if that is fixed.
// $monitor['MaxFPS'] = $profile['MaxFPS']; // $monitor['MaxFPS'] = $profile['MaxFPS'];
// $monitor['AlarmMaxFPS'] = $profile['AlarmMaxFPS']; // $monitor['AlarmMaxFPS'] = $profile['AlarmMaxFPS'];

View File

@ -174,29 +174,36 @@ xhtmlHeaders(__FILE__, translate('Zone'));
<tr> <tr>
<th scope="row"><?php echo translate('Type') ?></th> <th scope="row"><?php echo translate('Type') ?></th>
<td colspan="2"><?php echo htmlSelect('newZone[Type]', $optTypes, $newZone['Type'], <td colspan="2"><?php echo htmlSelect('newZone[Type]', $optTypes, $newZone['Type'],
array('onchange'=>'applyZoneType()')); ?></td> array('data-on-change'=>'applyZoneType')); ?></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('Preset') ?></th> <th scope="row"><?php echo translate('Preset') ?></th>
<td colspan="2"> <td colspan="2">
<?php echo htmlSelect('presetSelector', $presetNames, <?php echo htmlSelect('presetSelector', $presetNames,
( isset($_REQUEST['presetSelector']) ? $_REQUEST['presetSelector'] : null), ( isset($_REQUEST['presetSelector']) ? $_REQUEST['presetSelector'] : null),
array('onchange'=>'applyPreset()', 'onblur'=>'this.selectedIndex=0') ) array('data-on-change'=>'applyPreset', 'onblur'=>'this.selectedIndex=0') )
?></td> ?></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('Units') ?></th> <th scope="row"><?php echo translate('Units') ?></th>
<td colspan="2"><?php echo htmlSelect('newZone[Units]', $optUnits, $newZone['Units'], <td colspan="2">
array('onchange'=>'applyZoneUnits()') ) ?></td> <?php
echo htmlSelect('newZone[Units]', $optUnits, $newZone['Units'],
array('data-on-change'=>'applyZoneUnits')
);
# Used later for number inputs
$step = $newZone['Units'] == 'Percent' ? ' step="0.01" max="100"' : '';
?>
</td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneAlarmColour') ?></th> <th scope="row"><?php echo translate('ZoneAlarmColour') ?></th>
<td colspan="2"> <td colspan="2">
<input type="number" name="newAlarmRgbR" value="<?php echo ($newZone['AlarmRGB']>>16)&0xff ?>" size="3"/> <input type="number" name="newAlarmRgbR" value="<?php echo ($newZone['AlarmRGB']>>16)&0xff ?>" min="0" max="255"/>
/ /
<input type="number" name="newAlarmRgbG" value="<?php echo ($newZone['AlarmRGB']>>8)&0xff ?>" size="3"/> <input type="number" name="newAlarmRgbG" value="<?php echo ($newZone['AlarmRGB']>>8)&0xff ?>" min="0" max="255"/>
/ /
<input type="number" name="newAlarmRgbB" value="<?php echo $newZone['AlarmRGB']&0xff ?>" size="3"/> <input type="number" name="newAlarmRgbB" value="<?php echo $newZone['AlarmRGB']&0xff ?>" min="0" max="255"/>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -205,45 +212,45 @@ xhtmlHeaders(__FILE__, translate('Zone'));
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneMinMaxPixelThres') ?></th> <th scope="row"><?php echo translate('ZoneMinMaxPixelThres') ?></th>
<td><input type="number" name="newZone[MinPixelThreshold]" value="<?php echo $newZone['MinPixelThreshold'] ?>" size="4"/></td> <td><input type="number" name="newZone[MinPixelThreshold]" value="<?php echo $newZone['MinPixelThreshold'] ?>"/></td>
<td><input type="number" name="newZone[MaxPixelThreshold]" value="<?php echo $newZone['MaxPixelThreshold'] ?>" size="4"/></td> <td><input type="number" name="newZone[MaxPixelThreshold]" value="<?php echo $newZone['MaxPixelThreshold'] ?>"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneFilterSize') ?></th> <th scope="row"><?php echo translate('ZoneFilterSize') ?></th>
<td><input type="number" name="newZone[FilterX]" value="<?php echo $newZone['FilterX'] ?>" size="4"/></td> <td><input type="number" name="newZone[FilterX]" value="<?php echo $newZone['FilterX'] ?>"/></td>
<td><input type="number" name="newZone[FilterY]" value="<?php echo $newZone['FilterY'] ?>" size="4"/></td> <td><input type="number" name="newZone[FilterY]" value="<?php echo $newZone['FilterY'] ?>"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneArea') ?></th> <th scope="row"><?php echo translate('ZoneArea') ?></th>
<td colspan="2"><input type="number" name="newZone[TempArea]" value="<?php echo $newZone['Area'] ?>" size="7" disabled="disabled"/></td> <td colspan="2"><input type="number" name="newZone[TempArea]" value="<?php echo $newZone['Area'] ?>" disabled="disabled"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneMinMaxAlarmArea') ?></th> <th scope="row"><?php echo translate('ZoneMinMaxAlarmArea') ?></th>
<td><input type="number" name="newZone[MinAlarmPixels]" value="<?php echo $newZone['MinAlarmPixels'] ?>" size="6"/></td> <td><input type="number" name="newZone[MinAlarmPixels]" value="<?php echo $newZone['MinAlarmPixels'] ?>"<?php echo $step ?> min="0"/></td>
<td><input type="number" name="newZone[MaxAlarmPixels]" value="<?php echo $newZone['MaxAlarmPixels'] ?>" size="6"/></td> <td><input type="number" name="newZone[MaxAlarmPixels]" value="<?php echo $newZone['MaxAlarmPixels'] ?>"<?php echo $step ?> min="0"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneMinMaxFiltArea') ?></th> <th scope="row"><?php echo translate('ZoneMinMaxFiltArea') ?></th>
<td><input type="number" name="newZone[MinFilterPixels]" value="<?php echo $newZone['MinFilterPixels'] ?>" size="6"/></td> <td><input type="number" name="newZone[MinFilterPixels]" value="<?php echo $newZone['MinFilterPixels'] ?>"<?php echo $step ?> min="0"/></td>
<td><input type="number" name="newZone[MaxFilterPixels]" value="<?php echo $newZone['MaxFilterPixels'] ?>" size="6"/></td> <td><input type="number" name="newZone[MaxFilterPixels]" value="<?php echo $newZone['MaxFilterPixels'] ?>"<?php echo $step ?> min="0"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneMinMaxBlobArea') ?></th> <th scope="row"><?php echo translate('ZoneMinMaxBlobArea') ?></th>
<td><input type="number" name="newZone[MinBlobPixels]" value="<?php echo $newZone['MinBlobPixels'] ?>" size="6"/></td> <td><input type="number" name="newZone[MinBlobPixels]" value="<?php echo $newZone['MinBlobPixels'] ?>"<?php echo $step ?> min="0"/></td>
<td><input type="number" name="newZone[MaxBlobPixels]" value="<?php echo $newZone['MaxBlobPixels'] ?>" size="6"/></td> <td><input type="number" name="newZone[MaxBlobPixels]" value="<?php echo $newZone['MaxBlobPixels'] ?>"<?php echo $step ?> min="0"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneMinMaxBlobs') ?></th> <th scope="row"><?php echo translate('ZoneMinMaxBlobs') ?></th>
<td><input type="number" name="newZone[MinBlobs]" value="<?php echo $newZone['MinBlobs'] ?>" size="4"/></td> <td><input type="number" name="newZone[MinBlobs]" value="<?php echo $newZone['MinBlobs'] ?>" min="0"/></td>
<td><input type="number" name="newZone[MaxBlobs]" value="<?php echo $newZone['MaxBlobs'] ?>" size="4"/></td> <td><input type="number" name="newZone[MaxBlobs]" value="<?php echo $newZone['MaxBlobs'] ?>" min="0"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneOverloadFrames') ?></th> <th scope="row"><?php echo translate('ZoneOverloadFrames') ?></th>
<td colspan="2"><input type="number" name="newZone[OverloadFrames]" value="<?php echo $newZone['OverloadFrames'] ?>" size="4"/></td> <td colspan="2"><input type="number" name="newZone[OverloadFrames]" value="<?php echo $newZone['OverloadFrames'] ?>" min="0"/></td>
</tr> </tr>
<tr> <tr>
<th scope="row"><?php echo translate('ZoneExtendAlarmFrames') ?></th> <th scope="row"><?php echo translate('ZoneExtendAlarmFrames') ?></th>
<td colspan="2"><input type="number" name="newZone[ExtendAlarmFrames]" value="<?php echo $newZone['ExtendAlarmFrames'] ?>" size="4"/></td> <td colspan="2"><input type="number" name="newZone[ExtendAlarmFrames]" value="<?php echo $newZone['ExtendAlarmFrames'] ?>" min="0"/></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>