diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 765e15519..f12bf0ffe 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -331,13 +331,13 @@ CREATE TABLE `Monitors` ( `Format` int(10) unsigned NOT NULL default '0', `V4LMultiBuffer` tinyint(1) unsigned, `V4LCapturesPerFrame` tinyint(3) unsigned, - `Protocol` varchar(16) NOT NULL default '', + `Protocol` varchar(16) default '', `Method` varchar(16) NOT NULL default '', `Host` varchar(64), `Port` varchar(8) NOT NULL default '', `SubPath` varchar(64) NOT NULL default '', `Path` varchar(255), - `Options` varchar(255) not null default '', + `Options` varchar(255) default '', `User` varchar(64), `Pass` varchar(64), `Width` smallint(5) unsigned NOT NULL default '0', diff --git a/distros/debian/install b/distros/debian/install index 97c5f7a03..337673313 100644 --- a/distros/debian/install +++ b/distros/debian/install @@ -6,3 +6,4 @@ usr/share/perl5/ZoneMinder.pm usr/share/zoneminder/db usr/share/zoneminder/www etc/zm +etc/zm/conf.d/* diff --git a/distros/redhat/sysvinit/zoneminder.conf.in b/distros/redhat/sysvinit/zoneminder.conf.in index 0fbee6a62..413910214 100644 --- a/distros/redhat/sysvinit/zoneminder.conf.in +++ b/distros/redhat/sysvinit/zoneminder.conf.in @@ -18,7 +18,7 @@ Alias /zm "@ZM_WEBDIR@" Allow from all -ScriptAlias /cgi-bin/zm "@ZM_CGIDIR@" +ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@" SSLRequireSSL AllowOverride All diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 374b7bf03..e20da017b 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -33,7 +33,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.31.0 +Version: 1.31.1 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/distros/ubuntu1204/zoneminder.install b/distros/ubuntu1204/zoneminder.install index 8a26777c0..67b135de5 100644 --- a/distros/ubuntu1204/zoneminder.install +++ b/distros/ubuntu1204/zoneminder.install @@ -1,4 +1,5 @@ etc/zm/zm.conf +etc/zm/conf.d/* usr/bin usr/lib/zoneminder usr/share/polkit-1 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install index bd0a03bc2..4fdac1082 100644 --- a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install @@ -1,4 +1,5 @@ etc/zm +etc/zm/conf.d/* usr/bin usr/share/polkit-1/actions usr/share/polkit-1/rules.d diff --git a/distros/ubuntu1604/zoneminder.install b/distros/ubuntu1604/zoneminder.install index 8a26777c0..67b135de5 100644 --- a/distros/ubuntu1604/zoneminder.install +++ b/distros/ubuntu1604/zoneminder.install @@ -1,4 +1,5 @@ etc/zm/zm.conf +etc/zm/conf.d/* usr/bin usr/lib/zoneminder usr/share/polkit-1 diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index 6962a5f2c..e06cdd3b5 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -4,60 +4,62 @@ set -e if [ "$1" = "configure" ]; then - . /etc/zm/zm.conf + . /etc/zm/zm.conf - # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group - chown www-data:root /var/log/zm - chown www-data:www-data /var/lib/zm - if [ -z "$2" ]; then - chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* - fi - if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ]; then - echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." - a2enmod cgi - fi + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ]; then + echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." + a2enmod cgi + fi # Do this every time the package is installed or upgraded + # Ensure zoneminder is stopped + deb-systemd-invoke stop zoneminder.service || exit $? # Ensure zoneminder is stopped deb-systemd-invoke stop zoneminder.service || exit $? if [ "$ZM_DB_HOST" = "localhost" ]; then - if [ -e "/etc/init.d/mysql" ]; then + if [ -e "/etc/init.d/mysql" ]; then - # - # Get mysql started if it isn't - # - if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then - deb-systemd-invoke start mysql.service || exit $? - fi + # + # Get mysql started if it isn't + # + if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then + deb-systemd-invoke start mysql.service || exit $? + fi - if $(/etc/init.d/mysql status >/dev/null 2>&1); then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - # This creates the user. - echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - else - echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - - zmupdate.pl --nointeractive - zmupdate.pl --nointeractive -f - - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi + if $(/etc/init.d/mysql status >/dev/null 2>&1); then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + # This creates the user. + echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql else - echo 'mysql not found, assuming remote server.' + echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + else + echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi else - echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + echo 'mysql not found, assuming remote server.' fi - echo "Done Updating, starting ZoneMinder" - deb-systemd-invoke start zoneminder.service || exit $? + + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + fi + echo "Done Updating, starting ZoneMinder" + deb-systemd-invoke restart zoneminder.service || exit $? + fi #DEBHELPER# diff --git a/misc/logrotate.conf.in b/misc/logrotate.conf.in index 664b206c3..7c3810f02 100644 --- a/misc/logrotate.conf.in +++ b/misc/logrotate.conf.in @@ -11,6 +11,8 @@ @ZM_LOGDIR@/*.log { missingok notifempty + delaycompress + compress sharedscripts postrotate /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 5846a39d2..f69a71ab1 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -33,8 +33,11 @@ FOREACH(PERLSCRIPT ${perlscripts}) ENDFOREACH(PERLSCRIPT ${perlscripts}) # Install the perl scripts -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(NOT ZM_NO_X10) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif(NOT ZM_NO_X10) +if(WITH_SYSTEMD) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +endif(WITH_SYSTEMD) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index 8563a95ef..44c7f7e8e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -41,7 +41,7 @@ our @ISA = qw(Exporter ZoneMinder::Base); # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'constants' => [ qw( + constants => [ qw( DEBUG INFO WARNING @@ -50,7 +50,7 @@ our %EXPORT_TAGS = ( PANIC NOLOG ) ], - 'functions' => [ qw( + functions => [ qw( logInit logReinit logTerm @@ -72,13 +72,14 @@ our %EXPORT_TAGS = ( Panic ) ] ); - push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; - our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; - our @EXPORT = qw(); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); - our $VERSION = $ZoneMinder::Base::VERSION; +our @EXPORT = qw(); + +our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # @@ -86,43 +87,43 @@ our %EXPORT_TAGS = ( # # ========================================================================== - use ZoneMinder::Config qw(:all); +use ZoneMinder::Config qw(:all); - use DBI; - use Carp; - use POSIX; - use IO::Handle; - use Data::Dumper; - use Time::HiRes qw/gettimeofday/; - use Sys::Syslog; +use DBI; +use Carp; +use POSIX; +use IO::Handle; +use Data::Dumper; +use Time::HiRes qw/gettimeofday/; +use Sys::Syslog; - use constant { - DEBUG => 1, - INFO => 0, - WARNING => -1, - ERROR => -2, - FATAL => -3, - PANIC => -4, - NOLOG => -5 - }; +use constant { + DEBUG => 1, + INFO => 0, + WARNING => -1, + ERROR => -2, + FATAL => -3, + PANIC => -4, + NOLOG => -5 +}; our %codes = ( - &DEBUG => "DBG", - &INFO => "INF", - &WARNING => "WAR", - &ERROR => "ERR", - &FATAL => "FAT", - &PANIC => "PNC", - &NOLOG => "OFF" + &DEBUG => 'DBG', + &INFO => 'INF', + &WARNING => 'WAR', + &ERROR => 'ERR', + &FATAL => 'FAT', + &PANIC => 'PNC', + &NOLOG => 'OFF' ); our %priorities = ( - &DEBUG => "debug", - &INFO => "info", - &WARNING => "warning", - &ERROR => "err", - &FATAL => "err", - &PANIC => "err" + &DEBUG => 'debug', + &INFO => 'info', + &WARNING => 'warning', + &ERROR => 'err', + &FATAL => 'err', + &PANIC => 'err' ); our $logger; @@ -134,10 +135,10 @@ sub new { $this->{initialised} = undef; -#$this->{id} = "zmundef"; +#$this->{id} = 'zmundef'; ( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|; $this->{idRoot} = $this->{id}; - $this->{idArgs} = ""; + $this->{idArgs} = ''; $this->{level} = INFO; $this->{termLevel} = NOLOG; @@ -151,7 +152,7 @@ sub new { ( $this->{fileName} = $0 ) =~ s|^.*/||; $this->{logPath} = $Config{ZM_PATH_LOGS}; - $this->{logFile} = $this->{logPath}."/".$this->{id}.".log"; + $this->{logFile} = $this->{logPath}.'/'.$this->{id}.".log"; $this->{trace} = 0; @@ -196,7 +197,7 @@ sub initialise( @ ) { $this->{logPath} = $options{logPath} if ( defined($options{logPath}) ); my $tempLogFile; - $tempLogFile = $this->{logPath}."/".$this->{id}.".log"; + $tempLogFile = $this->{logPath}.'/'.$this->{id}.".log"; $tempLogFile = $options{logFile} if ( defined($options{logFile}) ); if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) { $tempLogFile = $logFile; @@ -231,7 +232,6 @@ sub initialise( @ ) { my $level; $tempLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL')) ); - $tempTermLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM')) ); $tempDatabaseLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE')) ); $tempFileLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE')) ); @@ -240,9 +240,9 @@ sub initialise( @ ) { if ( $Config{ZM_LOG_DEBUG} ) { foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) { if ( $target eq $this->{id} - || $target eq "_".$this->{id} + || $target eq '_'.$this->{id} || $target eq $this->{idRoot} - || $target eq "_".$this->{idRoot} + || $target eq '_'.$this->{idRoot} || $target eq "" ) { if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) { @@ -271,13 +271,13 @@ sub initialise( @ ) { $this->{initialised} = !undef; - Debug( "LogOpts: level=".$codes{$this->{level}} - ."/".$codes{$this->{effectiveLevel}} - .", screen=".$codes{$this->{termLevel}} - .", database=".$codes{$this->{databaseLevel}} - .", logfile=".$codes{$this->{fileLevel}} - ."->".$this->{logFile} - .", syslog=".$codes{$this->{syslogLevel}} + Debug( 'LogOpts: level='.$codes{$this->{level}} + .'/'.$codes{$this->{effectiveLevel}} + .', screen='.$codes{$this->{termLevel}} + .', database='.$codes{$this->{databaseLevel}} + .', logfile='.$codes{$this->{fileLevel}} + .'->'.$this->{logFile} + .', syslog='.$codes{$this->{syslogLevel}} ); } @@ -322,11 +322,11 @@ sub limit { sub getTargettedEnv { my $this = shift; my $name = shift; - my $envName = $name."_".$this->{id}; + my $envName = $name.'_'.$this->{id}; my $value; $value = $ENV{$envName} if ( defined($ENV{$envName}) ); if ( !defined($value) && $this->{id} ne $this->{idRoot} ) { - $envName = $name."_".$this->{idRoot}; + $envName = $name.'_'.$this->{idRoot}; $value = $ENV{$envName} if ( defined($ENV{$envName}) ); } if ( !defined($value) ) { @@ -375,8 +375,8 @@ sub level { $this->{effectiveLevel} = $this->{termLevel} if ( $this->{termLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{databaseLevel} if ( $this->{databaseLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{fileLevel} if ( $this->{fileLevel} > $this->{effectiveLevel} ); - $this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{level} ); - $this->{effectiveLevel} = $this->{level} if ( $this->{effectiveLevel} > $this->{level} ); + $this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{effectiveLevel} ); + $this->{effectiveLevel} = $this->{level} if ( $this->{effectiveLevel} > $this->{effectiveLevel} ); } return( $this->{level} ); } @@ -490,7 +490,7 @@ sub syslogLevel { sub openSyslog { my $this = shift; - openlog( $this->{id}, "pid", "local1" ); + openlog( $this->{id}, 'pid', "local1" ); } sub closeSyslog { @@ -523,6 +523,7 @@ sub openFile { } } else { $this->fileLevel( NOLOG ); + $this->termLevel( INFO ); Error( "Can't open log file '".$this->{logFile}."': $!" ); } } @@ -544,10 +545,8 @@ sub logPrint { my ($seconds, $microseconds) = gettimeofday(); my $message = sprintf( - "%s.%06d %s[%d].%s [%s]" - , strftime( "%x %H:%M:%S" - ,localtime( $seconds ) - ) + '%s.%06d %s[%d].%s [%s]' + , strftime( '%x %H:%M:%S' ,localtime( $seconds ) ) , $microseconds , $this->{id} , $$ @@ -564,7 +563,7 @@ sub logPrint { } print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} ); if ( $level <= $this->{databaseLevel} ) { - my $sql = "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )"; + my $sql = 'insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )'; $this->{sth} = $this->{dbh}->prepare_cached( $sql ); if ( !$this->{sth} ) { $this->{databaseLevel} = NOLOG; @@ -589,7 +588,7 @@ sub logPrint { sub logInit( ;@ ) { my %options = @_ ? @_ : (); - $logger = ZoneMinder::Logger->new() if ( !$logger ); + $logger = ZoneMinder::Logger->new() if !$logger; $logger->initialise( %options ); } @@ -646,14 +645,14 @@ sub logSyslogLevel { sub Mark { my $level = shift; $level = DEBUG unless( defined($level) ); - my $tag = "Mark"; + my $tag = 'Mark'; fetch()->logPrint( $level, $tag ); } sub Dump { my $var = shift; my $label = shift; - $label = "VAR" unless( defined($label) ); + $label = 'VAR' unless( defined($label) ); fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) ); } @@ -695,10 +694,10 @@ ZoneMinder::Logger - ZoneMinder Logger module use ZoneMinder::Logger; use ZoneMinder::Logger qw(:all); -logInit( "myproc", DEBUG ); +logInit( 'myproc', DEBUG ); -Debug( "This is what is happening" ); -Info( "Something interesting is happening" ); +Debug( 'This is what is happening' ); +Info( 'Something interesting is happening' ); Warning( "Something might be going wrong." ); Error( "Something has gone wrong!!" ); Fatal( "Something has gone badly wrong, gotta stop!!" ); diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index 39e7b73ec..ccab3ad33 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -21,6 +21,304 @@ # # ========================================================================== +use strict; +use bytes; + +@EXTRA_PERL_LIB@ +use ZoneMinder; +use DBI; +use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); +use LWP::UserAgent; +use Sys::MemInfo qw(totalmem); +use Sys::CPU qw(cpu_count); +use POSIX qw(strftime uname); + +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +use constant CHECK_INTERVAL => (14*24*60*60); # Interval between version checks + +# Setting these as contants for now. +# Alternatively, we can put these in the dB and then retrieve using the Config hash. +use constant ZM_TELEMETRY_SERVER_ENDPOINT => 'https://zmanon:2b2d0b4skps@telemetry.zoneminder.com/zmtelemetry/testing5'; + +if ( $Config{ZM_TELEMETRY_DATA} ) { + print( 'Update agent starting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + + my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD}; + + while( 1 ) { + my $now = time(); + if ( ($now-$lastCheck) > CHECK_INTERVAL ) { + Info( 'Collecting data to send to ZoneMinder Telemetry server.' ); + my $dbh = zmDbConnect(); +# Build the telemetry hash +# We should keep *BSD systems in mind when calling system commands + my %telemetry; + $telemetry{uuid} = getUUID($dbh); + $telemetry{ip} = getIP(); + $telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() ); + $telemetry{monitor_count} = countQuery($dbh,'Monitors'); + $telemetry{event_count} = countQuery($dbh,'Events'); + $telemetry{architecture} = runSysCmd('uname -p'); + ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); + $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; + $telemetry{system_memory} = totalmem(); + $telemetry{processor_count} = cpu_count(); + $telemetry{monitors} = getMonitorRef($dbh); + + Info( 'Sending data to ZoneMinder Telemetry server.' ); + + my $result = jsonEncode( \%telemetry ); + + if ( sendData($result) ) { + $lastCheck = $now; + + my $sql = q`update Config set Value = ? where Name = 'ZM_TELEMETRY_LAST_UPLOAD'`; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( "$lastCheck" ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + } + } + sleep( 3600 ); + } + print( 'Update agent exiting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); +} + +############### +# SUBROUTINES # +############### + +# Find, verify, then run the supplied system command +sub runSysCmd { + my $msg = shift; + my @arguments = split(/ /,$msg); + chomp($arguments[0]); + my $path = qx( which $arguments[0] ); + + my $status = $? >> 8; + my $result = ''; + if ( !$path || $status ) { + Warning( "Cannot find the $arguments[0] executable." ); + } else { + chomp($path); + $arguments[0] = $path; + my $cmd = join(' ',@arguments); + $result = qx( $cmd ); + chomp($result); + } + + return $result; +} + +# Upload message data to ZoneMinder telemetry server +sub sendData { + my $msg = shift; + + my $ua = LWP::UserAgent->new; + my $server_endpoint = ZM_TELEMETRY_SERVER_ENDPOINT; + + if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { + $ua->proxy( 'https', $Config{ZM_UPDATE_CHECK_PROXY} ); + } + + Debug("Posting telemetry data to: $server_endpoint"); + +# set custom HTTP request header fields + my $req = HTTP::Request->new(POST => $server_endpoint); + $req->header('content-type' => 'application/x-www-form-urlencoded'); + $req->header('content-length' => length($msg)); + $req->header('connection' => 'Close'); + + $req->content($msg); + + my $resp = $ua->request($req); + my $resp_msg = $resp->decoded_content; + my $resp_code = $resp->code; + if ($resp->is_success) { + Info('Telemetry data uploaded successfully.'); + Debug("Telemetry server upload success response message: $resp_msg"); + } else { + Warning("Telemetry server returned HTTP POST error code: $resp_code"); + Debug("Telemetry server upload failure response message: $resp_msg"); + } + return $resp->is_success; +} + +# Retrieves the UUID from the database. Creates a new UUID if one does not exist. +sub getUUID { + my $dbh = shift; + my $uuid= ""; + +# Verify the current UUID is valid and not nil + if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne '00000000-0000-0000-0000-000000000000' )) { + $uuid = $Config{ZM_TELEMETRY_UUID}; + } else { + my $sql = 'SELECT uuid()'; + 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() ); + $uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array(); + $sth->finish(); + + $sql = q`UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'`; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + } + Debug("Using UUID of: $uuid"); + + return $uuid; +} + +# Retrieves the local server's external IP address +sub getIP { + my $ipaddr = '0.0.0.0'; + my $ua = LWP::UserAgent->new; + my $server_endpoint = 'https://wiki.zoneminder.com/ip.php'; + + my $req = HTTP::Request->new(GET => $server_endpoint); + my $resp = $ua->request($req); + + if ($resp->is_success) { + $ipaddr = $resp->decoded_content; + } + + Debug("Found external ip address of: $ipaddr"); + + return $ipaddr; +} + +# As the name implies, just your average mysql count query +sub countQuery { + my $dbh = shift; + my $table = shift; + + my $sql = "SELECT count(*) FROM $table"; + 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 $count = $sth->fetchrow_array(); + $sth->finish(); + + return $count +} + +# Returns a reference to an array of hashes containing data from all monitors +sub getMonitorRef { + my $dbh = shift; + + my $sql = 'SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors'; + 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 $arrayref = $sth->fetchall_arrayref({}); + + return $arrayref; +} + +sub getDistro { + my $kernel = ''; + my $distro = ''; + my $version = ''; + my @uname = uname(); + + if ( $uname[0] =~ /Linux/ ) { + Debug('Linux distro detected.'); + ($kernel, $distro, $version) = linuxDistro(); + } elsif ( $uname[0] =~ /.*BSD/ ) { + Debug('BSD distro detected.'); + $kernel = $uname[3]; + $distro = $uname[0]; + $version = $uname[2]; + } elsif ( $uname[0] =~ /Darwin/ ) { + Debug('Mac OS distro detected.'); + $kernel = $uname[3]; + $distro = runSysCmd('sw_vers -productName'); + $version = runSysCmd('sw_vers -productVersion'); + } elsif ( $uname[0] =~ /SunOS|Solaris/ ) { + Debug('Sun Solaris detected.'); + $kernel = $uname[3]; + $distro = $uname[1]; + $version = $uname[2]; + } else { + Warning('ZoneMinder was unable to determine the host system. Please report.'); + $kernel = 'Unknown'; + $distro = 'Unknown'; + $version = 'Unknown'; + } + + return ($kernel, $distro, $version); +} + +sub linuxDistro { + my @uname = uname(); + my $kernel = $uname[2]; + my $distro = 'Unknown Linux Distro'; + my $version = 'Unknown Linux Version'; + my $found = 0; + +# os-release is the standard for many new distros based on systemd + if ( -f '/etc/os-release' ) { + open(my $RELFILE,'<','/etc/os-release') or die( "Can't Open file: $!\n" ); + while (<$RELFILE>) { + if ( /^NAME=(")?(.*)(?(1)\1|).*$/ ) { + $distro = $2; + $found = 1; + } + if ( /^VERSION_ID=(")?(.*)(?(1)\1|).*$/ ) { + $version = $2; + $found = 1; + } + } + close $RELFILE; +# exists on many distros but does not always contain useful information, such as redhat + } elsif ( -f '/etc/lsb-release' ) { + open(my $RELFILE,'<','/etc/lsb-release') or die( "Can't Open file: $!\n" ); + while (<$RELFILE>) { + if ( /^DISTRIB_DESCRIPTION=(")?(.*)(?(1)\1|).*$/ ) { + $distro = $2; + $found = 1; + } + if ( /^DISTRIB_RELEASE=(")?(.*)(?(1)\1|).*$/ ) { + $version = $2; + $found = 1; + } + } + close $RELFILE; + } + +# If all else fails, search through a list of known release files until we find one + if ( !$found ) { + my @releasefile = ('/etc/SuSE-release', '/etc/redhat-release', '/etc/redhat_version', + '/etc/fedora-release', '/etc/slackware-release', '/etc/slackware-version', + '/etc/debian_release', '/etc/debian_version', '/etc/mandrake-release', + '/etc/yellowdog-release', '/etc/gentoo-release'); + foreach (@releasefile) { + if ( -f $_ ) { + open(my $RELFILE,'<',$_) or die( "Can't Open file: $!\n" ); + while (<$RELFILE>) { + if ( /(.*).* (\d+\.?\d*) .*/ ) { + $distro = $1; + $version = $2; + $found = 1; + } + } + close $RELFILE; + last; + } + } + } + + if ( !$found ) { + Warning('ZoneMinder was unable to determine Linux distro. Please report.'); + } + + return ($kernel, $distro, $version); +} + +1; +__END__ + =head1 NAME zmtelemetry.pl - Send usage information to the ZoneMinder development team @@ -44,300 +342,3 @@ console under Options. none currently =cut -use strict; -use bytes; - -@EXTRA_PERL_LIB@ -use ZoneMinder; -use DBI; -use Getopt::Long; -use autouse 'Pod::Usage'=>qw(pod2usage); -use LWP::UserAgent; -use Sys::MemInfo qw(totalmem); -use Sys::CPU qw(cpu_count); -use POSIX qw(strftime uname); - -$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; -$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; -delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; - -use constant CHECK_INTERVAL => (14*24*60*60); # Interval between version checks - -# Setting these as contants for now. -# Alternatively, we can put these in the dB and then retrieve using the Config hash. -use constant ZM_TELEMETRY_SERVER_ENDPOINT => 'https://zmanon:2b2d0b4skps@telemetry.zoneminder.com/zmtelemetry/testing5'; - -if ( $Config{ZM_TELEMETRY_DATA} ) -{ - print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - - my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD}; - - while( 1 ) { - my $now = time(); - if ( ($now-$lastCheck) > CHECK_INTERVAL ) { - Info( "Collecting data to send to ZoneMinder Telemetry server." ); - my $dbh = zmDbConnect(); - # Build the telemetry hash - # We should keep *BSD systems in mind when calling system commands - my %telemetry; - $telemetry{uuid} = getUUID($dbh); - $telemetry{ip} = getIP(); - $telemetry{timestamp} = strftime( "%Y-%m-%dT%H:%M:%S%z", localtime() ); - $telemetry{monitor_count} = countQuery($dbh,"Monitors"); - $telemetry{event_count} = countQuery($dbh,"Events"); - $telemetry{architecture} = runSysCmd("uname -p"); - ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); - $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; - $telemetry{system_memory} = totalmem(); - $telemetry{processor_count} = cpu_count(); - $telemetry{monitors} = getMonitorRef($dbh); - - Info( "Sending data to ZoneMinder Telemetry server." ); - - my $result = jsonEncode( \%telemetry ); - - if ( sendData($result) ) { - $lastCheck = $now; - - my $sql = "update Config set Value = ? where Name = 'ZM_TELEMETRY_LAST_UPLOAD'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$lastCheck" ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); - } - } - sleep( 3600 ); - } - print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); -} - -############### -# SUBROUTINES # -############### - -# Find, verify, then run the supplied system command -sub runSysCmd { - my $msg = shift; - my @arguments = split(/ /,$msg); - chomp($arguments[0]); - my $path = qx( which $arguments[0] ); - - my $status = $? >> 8; - my $result = ""; - if ( !$path || $status ) { - Warning( "Cannot find the $arguments[0] executable." ); - } else { - chomp($path); - $arguments[0] = $path; - my $cmd = join(" ",@arguments); - $result = qx( $cmd ); - chomp($result); - } - -return $result; -} - -# Upload message data to ZoneMinder telemetry server -sub sendData { - my $msg = shift; - - my $ua = LWP::UserAgent->new; - my $server_endpoint = ZM_TELEMETRY_SERVER_ENDPOINT; - - if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { - $ua->proxy( "https", $Config{ZM_UPDATE_CHECK_PROXY} ); - } - - Debug("Posting telemetry data to: $server_endpoint"); - - # set custom HTTP request header fields - my $req = HTTP::Request->new(POST => $server_endpoint); - $req->header('content-type' => 'application/x-www-form-urlencoded'); - $req->header('content-length' => length($msg)); - $req->header('connection' => 'Close'); - - $req->content($msg); - - my $resp = $ua->request($req); - my $resp_msg = $resp->decoded_content; - my $resp_code = $resp->code; - if ($resp->is_success) { - Info("Telemetry data uploaded successfully."); - Debug("Telemetry server upload success response message: $resp_msg"); - } else { - Warning("Telemetry server returned HTTP POST error code: $resp_code"); - Debug("Telemetry server upload failure response message: $resp_msg"); - } -return $resp->is_success; -} - -# Retrieves the UUID from the database. Creates a new UUID if one does not exist. -sub getUUID { - my $dbh = shift; - my $uuid= ""; - - # Verify the current UUID is valid and not nil - if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne "00000000-0000-0000-0000-000000000000" )) { - $uuid = $Config{ZM_TELEMETRY_UUID}; - } else { - my $sql = "SELECT uuid()"; - 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() ); - $uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array(); - $sth->finish(); - - $sql = "UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'"; - $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - $res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); - } - Debug("Using UUID of: $uuid"); - -return $uuid; -} - -# Retrieves the local server's external IP address -sub getIP { - my $ipaddr = "0.0.0.0"; - my $ua = LWP::UserAgent->new; - my $server_endpoint = "https://wiki.zoneminder.com/ip.php"; - - my $req = HTTP::Request->new(GET => $server_endpoint); - my $resp = $ua->request($req); - - if ($resp->is_success) { - $ipaddr = $resp->decoded_content; - } - - Debug("Found external ip address of: $ipaddr"); - -return $ipaddr; -} - -# As the name implies, just your average mysql count query -sub countQuery { - my $dbh = shift; - my $table = shift; - - my $sql = "SELECT count(*) FROM $table"; - 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 $count = $sth->fetchrow_array(); - $sth->finish(); - -return $count -} - -# Returns a reference to an array of hashes containing data from all monitors -sub getMonitorRef { - my $dbh = shift; - - my $sql = "SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors"; - 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 $arrayref = $sth->fetchall_arrayref({}); - -return $arrayref; -} - -sub getDistro { - my $kernel = ""; - my $distro = ""; - my $version = ""; - my @uname = uname(); - - if ( $uname[0] =~ /Linux/ ) { - Debug("Linux distro detected."); - ($kernel, $distro, $version) = linuxDistro(); - } elsif ( $uname[0] =~ /.*BSD/ ) { - Debug("BSD distro detected."); - $kernel = $uname[3]; - $distro = $uname[0]; - $version = $uname[2]; - } elsif ( $uname[0] =~ /Darwin/ ) { - Debug("Mac OS distro detected."); - $kernel = $uname[3]; - $distro = runSysCmd("sw_vers -productName"); - $version = runSysCmd("sw_vers -productVersion"); - } elsif ( $uname[0] =~ /SunOS|Solaris/ ) { - Debug("Sun Solaris detected."); - $kernel = $uname[3]; - $distro = $uname[1]; - $version = $uname[2]; - } else { - Warning("ZoneMinder was unable to determine the host system. Please report."); - $kernel = "Unknown"; - $distro = "Unknown"; - $version = "Unknown"; - } - -return ($kernel, $distro, $version); -} - -sub linuxDistro { - my @uname = uname(); - my $kernel = $uname[2]; - my $distro = "Unknown Linux Distro"; - my $version = "Unknown Linux Version"; - my $found = 0; - - # os-release is the standard for many new distros based on systemd - if ( -f "/etc/os-release" ) { - open(my $RELFILE,"<","/etc/os-release") or die( "Can't Open file: $!\n" ); - while (<$RELFILE>) { - if ( /^NAME=(")?(.*)(?(1)\1|).*$/ ) { - $distro = $2; - $found = 1; - } - if ( /^VERSION_ID=(")?(.*)(?(1)\1|).*$/ ) { - $version = $2; - $found = 1; - } - } - close $RELFILE; - # exists on many distros but does not always contain useful information, such as redhat - } elsif ( -f "/etc/lsb-release" ) { - open(my $RELFILE,"<","/etc/lsb-release") or die( "Can't Open file: $!\n" ); - while (<$RELFILE>) { - if ( /^DISTRIB_DESCRIPTION=(")?(.*)(?(1)\1|).*$/ ) { - $distro = $2; - $found = 1; - } - if ( /^DISTRIB_RELEASE=(")?(.*)(?(1)\1|).*$/ ) { - $version = $2; - $found = 1; - } - } - close $RELFILE; - } - - # If all else fails, search through a list of known release files until we find one - if ( !$found ) { - my @releasefile = ("/etc/SuSE-release", "/etc/redhat-release", "/etc/redhat_version", - "/etc/fedora-release", "/etc/slackware-release", "/etc/slackware-version", - "/etc/debian_release", "/etc/debian_version", "/etc/mandrake-release", - "/etc/yellowdog-release", "/etc/gentoo-release"); - foreach (@releasefile) { - if ( -f $_ ) { - open(my $RELFILE,"<",$_) or die( "Can't Open file: $!\n" ); - while (<$RELFILE>) { - if ( /(.*).* (\d+\.?\d*) .*/ ) { - $distro = $1; - $version = $2; - $found = 1; - } - } - close $RELFILE; - last; - } - } - } - - if ( !$found ) { - Warning("ZoneMinder was unable to determine Linux distro. Please report."); - } - -return ($kernel, $distro, $version); -} - - diff --git a/scripts/zmvideo.pl.in b/scripts/zmvideo.pl.in index 5f71451a3..ab9d0b347 100644 --- a/scripts/zmvideo.pl.in +++ b/scripts/zmvideo.pl.in @@ -94,20 +94,11 @@ my $size = ''; my $overwrite = 0; my $version = 0; -my @formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} ); -for ( my $i = 0; $i < @formats; $i++ ) -{ - if ( $i =~ /^(.+)\*$/ ) - { - $format = $formats[$i] = $1; - } -} - GetOptions( - 'concat|c:s' =>\$concat_name, + 'concat|c:s' =>\$concat_name, 'event|e=i' =>\$event_id, 'filter_name=s' =>\$filter_name, - 'filter_id=i' =>\$filter_id, + 'filter_id=i' =>\$filter_id, 'format|f=s' =>\$format, 'rate|r=f' =>\$rate, 'scale|s=f' =>\$scale, @@ -115,51 +106,51 @@ GetOptions( 'size|S=s' =>\$size, 'overwrite' =>\$overwrite, 'version' =>\$version -) or pod2usage(-exitstatus => -1); + ) or pod2usage(-exitstatus => -1); if ( $version ) { - print ZoneMinder::Base::ZM_VERSION . "\n"; - exit(0); + print ZoneMinder::Base::ZM_VERSION . "\n"; + exit(0); } -if ( !( $filter_id or $filter_name or $event_id ) || $event_id < 0 ) -{ - print( STDERR "Please give a valid event id or filter name\n" ); - pod2usage(-exitstatus => -1); +if ( !( $filter_id or $filter_name or $event_id ) || ($event_id and ( $event_id < 0 ) ) ) { + print( STDERR "Please give a valid event id or filter name\n" ); + pod2usage(-exitstatus => -1); } -if ( ! $Config{ZM_OPT_FFMPEG} ) -{ - print( STDERR "Mpeg encoding is not currently enabled\n" ); - exit(-1); +if ( ! $Config{ZM_OPT_FFMPEG} ) { + print( STDERR "Mpeg encoding is not currently enabled\n" ); + exit(-1); } -if ( !$rate && !$fps ) -{ - $rate = 1; +my @formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} ); +for ( my $i = 0; $i < @formats; $i++ ) { + if ( $i =~ /^(.+)\*$/ ) { + $format = $formats[$i] = $1; + } } -if ( !$scale && !$size ) -{ - $scale = 1; +if ( !$rate && !$fps ) { + $rate = 1; } -if ( $rate && ($rate < 0.25 || $rate > 100) ) -{ - print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" ); - pod2usage(-exitstatus => -1); +if ( !$scale && !$size ) { + $scale = 1; } -if ( $scale && ($scale < 0.25 || $scale > 4) ) -{ - print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" ); - pod2usage(-exitstatus => -1); +if ( $rate && ($rate < 0.25 || $rate > 100) ) { + print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" ); + pod2usage(-exitstatus => -1); } -if ( $fps && ($fps > 30) ) -{ - print( STDERR "FPS is out of range, <= 30\n" ); - pod2usage(-exitstatus => -1); +if ( $scale && ($scale < 0.25 || $scale > 4) ) { + print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" ); + pod2usage(-exitstatus => -1); +} + +if ( $fps && ($fps > 30) ) { + print( STDERR "FPS is out of range, <= 30\n" ); + pod2usage(-exitstatus => -1); } my ( $detaint_format ) = $format =~ /^(\w+)$/; @@ -181,19 +172,25 @@ my $cwd = getcwd; my $video_name; my @event_ids; if ( $event_id ) { - @event_ids = ( $event_id ); - + @event_ids = ( $event_id ); + } elsif ( $filter_name or $filter_id ) { - my $Filter = ZoneMinder::Filter->find_one( - ($filter_name ? ( Name => $filter_name ) : () ), - ($filter_id ? ( Id => $filter_name ) : () ), - ); - if ( ! $Filter ) { - Fatal("Filter $filter_name $filter_id not found."); - } - @event_ids = map { $_->{Id} }$Filter->Execute(); - Fatal( "No events found for $filter_name") if ! @event_ids; - $concat_name = $filter_name if $concat_name eq ''; + my $Filter = ZoneMinder::Filter->find_one( + ($filter_name ? ( Name => $filter_name ) : () ), + ($filter_id ? ( Id => $filter_name ) : () ), + ); + if ( ! $Filter ) { + Fatal("Filter $filter_name $filter_id not found."); + } + @event_ids = map { $_->{Id} } $Filter->Execute(); + if ( ! @event_ids ) { + Fatal( "No events found for $filter_name") + } else { + Debug(@event_ids . " events found for $filter_name"); + } + $concat_name = $filter_name if $concat_name eq ''; +} else { + Warning("Nothing to do"); } my $sql = " SELECT max(F.Delta)-min(F.Delta) as FullLength, @@ -207,53 +204,54 @@ my $sql = " SELECT max(F.Delta)-min(F.Delta) as FullLength, INNER JOIN Events as E on F.EventId = E.Id INNER JOIN Monitors as M on E.MonitorId = M.Id WHERE EventId = ? - GROUP BY F.EventId" - ; + GROUP BY F.EventId"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my @video_files; foreach my $event_id ( @event_ids ) { + my $res = $sth->execute( $event_id ) + or Fatal( "Can't execute: ".$sth->errstr() ); + my $event = $sth->fetchrow_hashref(); - my $res = $sth->execute( $event_id ) - or Fatal( "Can't execute: ".$sth->errstr() ); - my $event = $sth->fetchrow_hashref(); - - my $Event = new ZoneMinder::Event( $$event{Id}, $event ); - my $video_file = $Event->GenerateVideo( $rate, $fps, $scale, $size, $overwrite, $format ); - if ( $video_file ) { - push @video_files, $video_file; - print( STDOUT $video_file."\n" ); - } + my $Event = new ZoneMinder::Event( $$event{Id}, $event ); + my $video_file = $Event->GenerateVideo( $rate, $fps, $scale, $size, $overwrite, $format ); + if ( $video_file ) { + push @video_files, $video_file; + print( STDOUT $video_file."\n" ); + } else { + Warning("No video file generated for event $event_id"); + } } # end foreach event_id if ( $concat_name ) { - ($cwd) = $cwd =~ /(.*)/; # detaint - chdir( $cwd ); - ($concat_name ) = $concat_name =~ /([\-A-Za-z0-9_\.]*)/; - my $concat_list_file = "/tmp/$concat_name.concat.lst"; + ($cwd) = $cwd =~ /(.*)/; # detaint + chdir( $cwd ); + ($concat_name ) = $concat_name =~ /([\-A-Za-z0-9_\.]*)/; + my $concat_list_file = "/tmp/$concat_name.concat.lst"; - my $video_file = $concat_name . '.'. $detaint_format; + my $video_file = $concat_name . '.'. $detaint_format; - open( my $fd, '>', $concat_list_file ) or die "Can't open $concat_list_file: $!"; - foreach ( @video_files ) { - print $fd "file '$_'\n"; - } - close $fd; - my $command = $Config{ZM_PATH_FFMPEG} - . " -f concat -i $concat_list_file -c copy " - ." '$video_file' > ffmpeg.log 2>&1" - ; - Debug( $command."\n" ); - my $output = qx($command); + open( my $fd, '>', $concat_list_file ) or die "Can't open $concat_list_file: $!"; + foreach ( @video_files ) { + print $fd "file '$_'\n"; + } + close $fd; + my $command = $Config{ZM_PATH_FFMPEG} + . " -f concat -i $concat_list_file -c copy " + ." '$video_file' > ffmpeg.log 2>&1" + ; + Debug( $command."\n" ); + my $output = qx($command); - my $status = $? >> 8; + my $status = $? >> 8; - unlink $concat_list_file; - if ( $status ) - { - Error( "Unable to generate video, check /ffmpeg.log for details"); - exit(-1); - } - print( STDOUT $video_file."\n" ); + unlink $concat_list_file; + if ( $status ) { + Error( "Unable to generate video, check /ffmpeg.log for details"); + exit(-1); + } + print( STDOUT $video_file."\n" ); } exit( 0 ); + +__END__ diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index f13733b11..48f805a1f 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -32,8 +32,8 @@ #include #include -/* Workaround for GNU/kFreeBSD */ -#if defined(__FreeBSD_kernel__) +/* Workaround for GNU/kFreeBSD and FreeBSD */ +#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) #ifndef ENODATA #define ENODATA ENOATTR #endif diff --git a/src/zm_packet.h b/src/zm_packet.h index 9fd7ed8ee..303b3af35 100644 --- a/src/zm_packet.h +++ b/src/zm_packet.h @@ -24,6 +24,10 @@ extern "C" { #include } +#ifdef __FreeBSD__ +#include +#endif // __FreeBSD__ + class ZMPacket { public: diff --git a/src/zm_sendfile.h b/src/zm_sendfile.h index 0e35388ea..9f3b73026 100644 --- a/src/zm_sendfile.h +++ b/src/zm_sendfile.h @@ -1,30 +1,30 @@ #ifdef HAVE_SENDFILE4_SUPPORT #include int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { - int err; + int err; - err = sendfile(out_fd, in_fd, offset, size); - if (err < 0) - return -errno; + err = sendfile(out_fd, in_fd, offset, size); + if (err < 0) + return -errno; - return err; + return err; } #elif HAVE_SENDFILE7_SUPPORT #include #include #include int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) { - int err; - err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0); - if (err && errno != EAGAIN) - return -errno; + int err; + err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0); + if (err && errno != EAGAIN) + return -errno; - if (size) { - *offset += size; - return size; - } + if (size) { + *offset += size; + return size; + } - return -EAGAIN; + return -EAGAIN; } #else #error "Your platform does not support sendfile. Sorry." diff --git a/src/zm_signal.cpp b/src/zm_signal.cpp index 08f3d76a0..034ff7d66 100644 --- a/src/zm_signal.cpp +++ b/src/zm_signal.cpp @@ -63,13 +63,13 @@ RETSIGTYPE zm_die_handler(int signal) ucontext_t *uc = (ucontext_t *) context; cr2 = info->si_addr; #if defined(__x86_64__) - #ifdef __FreeBSD_kernel__ + #if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) ip = (void *)(uc->uc_mcontext.mc_rip); #else ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]); #endif #else - #ifdef __FreeBSD_kernel__ + #if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) ip = (void *)(uc->uc_mcontext.mc_eip); #else ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]); diff --git a/src/zm_video.cpp b/src/zm_video.cpp index ec1371ba3..7f88fa1ef 100644 --- a/src/zm_video.cpp +++ b/src/zm_video.cpp @@ -1,522 +1,577 @@ +// Copyright (C) 2001-2017 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// +// #include "zm.h" #include "zm_video.h" #include "zm_image.h" #include "zm_utils.h" #include "zm_rgb.h" #include +#include +#include -VideoWriter::VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) : -container(p_container), codec(p_codec), path(p_path), width(p_width), height(p_height), colours(p_colours), subpixelorder(p_subpixelorder), frame_count(0) { - Debug(7,"Video object created"); - - /* Parameter checking */ - if(path.empty()) { - Error("Invalid file path"); - } - if(!width || !height) { - Error("Invalid width or height"); - } +VideoWriter::VideoWriter( + const char* p_container, + const char* p_codec, + const char* p_path, + const unsigned int p_width, + const unsigned int p_height, + const unsigned int p_colours, + const unsigned int p_subpixelorder) : + container(p_container), + codec(p_codec), + path(p_path), + width(p_width), + height(p_height), + colours(p_colours), + subpixelorder(p_subpixelorder), + frame_count(0) { + Debug(7, "Video object created"); + /* Parameter checking */ + if ( path.empty() ) { + Error("Invalid file path"); + } + if ( !width || !height ) { + Error("Invalid width or height"); + } } VideoWriter::~VideoWriter() { - Debug(7,"Video object destroyed"); - + Debug(7, "Video object destroyed"); } int VideoWriter::Reset(const char* new_path) { - /* Common variables reset */ + /* Common variables reset */ - /* If there is a new path, use it */ - if(new_path != NULL) { - path = new_path; - } + /* If there is a new path, use it */ + if ( new_path != NULL ) { + path = new_path; + } - /* Reset frame counter */ - frame_count = 0; + /* Reset frame counter */ + frame_count = 0; - return 0; + return 0; } #if ZM_HAVE_VIDEOWRITER_X264MP4 -X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector* p_user_params) : VideoWriter("mp4", "h264", p_path, p_width, p_height, p_colours, p_subpixelorder), bOpen(false), bGotH264AVCInfo(false), bFirstFrame(true) { - - /* Initialize ffmpeg if it hasn't been initialized yet */ - FFMPEGInit(); +X264MP4Writer::X264MP4Writer( + const char* p_path, + const unsigned int p_width, + const unsigned int p_height, + const unsigned int p_colours, + const unsigned int p_subpixelorder, + const std::vector* p_user_params) : + VideoWriter( + "mp4", + "h264", + p_path, + p_width, + p_height, + p_colours, + p_subpixelorder), + bOpen(false), + bGotH264AVCInfo(false), + bFirstFrame(true) { + /* Initialize ffmpeg if it hasn't been initialized yet */ + FFMPEGInit(); - /* Initialize swscale */ - zm_pf = GetFFMPEGPixelFormat(colours,subpixelorder); - if(zm_pf == 0) { - Error("Unable to match ffmpeg pixelformat"); - } - codec_pf = AV_PIX_FMT_YUV420P; + /* Initialize swscale */ + zm_pf = GetFFMPEGPixelFormat(colours, subpixelorder); + if ( zm_pf == 0 ) { + Error("Unable to match ffmpeg pixelformat"); + } + codec_pf = AV_PIX_FMT_YUV420P; - swscaleobj.SetDefaults(zm_pf, codec_pf, width, height); + swscaleobj.SetDefaults(zm_pf, codec_pf, width, height); - /* Calculate the image sizes. We will need this for parameter checking */ - zm_imgsize = colours * width * height; + /* Calculate the image sizes. We will need this for parameter checking */ + zm_imgsize = colours * width * height; #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - codec_imgsize = av_image_get_buffer_size( codec_pf, width, height, 1 ); + codec_imgsize = av_image_get_buffer_size(codec_pf, width, height, 1); #else - codec_imgsize = avpicture_get_size( codec_pf, width, height); + codec_imgsize = avpicture_get_size(codec_pf, width, height); #endif - if(!codec_imgsize) { - Error("Failed calculating codec pixel format image size"); - } + if ( !codec_imgsize ) { + Error("Failed calculating codec pixel format image size"); + } - /* If supplied with user parameters to the encoder, copy them */ - if(p_user_params != NULL) { - user_params = *p_user_params; - } - - /* Setup x264 parameters */ - if(x264config() < 0) { - Error("Failed setting x264 parameters"); - } - - /* Allocate x264 input picture */ - x264_picture_alloc(&x264picin, X264_CSP_I420, x264params.i_width, x264params.i_height); - + /* If supplied with user parameters to the encoder, copy them */ + if ( p_user_params != NULL ) { + user_params = *p_user_params; + } + + /* Setup x264 parameters */ + if ( x264config() < 0 ) { + Error("Failed setting x264 parameters"); + } + + /* Allocate x264 input picture */ + x264_picture_alloc( + &x264picin, + X264_CSP_I420, + x264params.i_width, + x264params.i_height); } X264MP4Writer::~X264MP4Writer() { + /* Free x264 input picture */ + x264_picture_clean(&x264picin); - /* Free x264 input picture */ - x264_picture_clean(&x264picin); - - if(bOpen) - Close(); - + if ( bOpen ) + Close(); } int X264MP4Writer::Open() { + /* Open the encoder */ + x264enc = x264_encoder_open(&x264params); + if ( x264enc == NULL ) { + Error("Failed opening x264 encoder"); + return -1; + } - /* Open the encoder */ - x264enc = x264_encoder_open(&x264params); - if(x264enc == NULL) { - Error("Failed opening x264 encoder"); - return -1; - } + // Debug(4,"x264 maximum delayed frames: %d", + // x264_encoder_maximum_delayed_frames(x264enc)); - // Debug(4,"x264 maximum delayed frames: %d",x264_encoder_maximum_delayed_frames(x264enc)); - - x264_nal_t* nals; - int i_nals; - if(!x264_encoder_headers(x264enc,&nals,&i_nals)) { - Error("Failed getting encoder headers"); - return -2; - } - - /* Search SPS NAL for AVC information */ - for(int i=0;i 0; i-- ) { + x264encodeloop(true); + } - /* Flush all pending frames */ - for(int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) { - x264encodeloop(true); - } + /* Close the encoder */ + x264_encoder_close(x264enc); - /* Close the encoder */ - x264_encoder_close(x264enc); + /* Close MP4 handle */ + MP4Close(mp4h); - /* Close MP4 handle */ - MP4Close(mp4h); - - /* Required for proper HTTP streaming */ - MP4Optimize((path + ".incomplete").c_str(), path.c_str()); + /* Required for proper HTTP streaming */ + MP4Optimize((path + ".incomplete").c_str(), path.c_str()); - /* Delete the temporary file */ - unlink((path + ".incomplete").c_str()); - - bOpen = false; + /* Delete the temporary file */ + unlink((path + ".incomplete").c_str()); - Debug(7, "Video closed. Total frames: %d", frame_count); - - return 0; + bOpen = false; + + Debug(7, "Video closed. Total frames: %d", frame_count); + + return 0; } int X264MP4Writer::Reset(const char* new_path) { - - /* Close the encoder and file */ - if(bOpen) - Close(); + /* Close the encoder and file */ + if ( bOpen ) + Close(); - /* Reset common variables */ - VideoWriter::Reset(new_path); + /* Reset common variables */ + VideoWriter::Reset(new_path); - /* Reset local variables */ - bFirstFrame = true; - bGotH264AVCInfo = false; - prevnals.clear(); - prevpayload.clear(); - - /* Reset x264 parameters */ - x264config(); + /* Reset local variables */ + bFirstFrame = true; + bGotH264AVCInfo = false; + prevnals.clear(); + prevpayload.clear(); - /* Open the encoder */ - Open(); - - return 0; + /* Reset x264 parameters */ + x264config(); + + /* Open the encoder */ + Open(); + + return 0; } -int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) { +int X264MP4Writer::Encode( + const uint8_t* data, + const size_t data_size, + const unsigned int frame_time) { + /* Parameter checking */ + if ( data == NULL ) { + Error("NULL buffer"); + return -1; + } - /* Parameter checking */ - if(data == NULL) { - Error("NULL buffer"); - return -1; - } + if ( data_size != zm_imgsize ) { + Error("The data buffer size (%d) != expected (%d)", data_size, zm_imgsize); + return -2; + } - if(data_size != zm_imgsize) { - Error("The data buffer size does not match the expected size. Expected: %d Current: %d", zm_imgsize, data_size); - return -2; - } - - if(!bOpen) { - Warning("The encoder was not initialized, initializing now"); - Open(); - } - - /* Convert the image into the x264 input picture */ - if(swscaleobj.ConvertDefaults(data, data_size, x264picin.img.plane[0], codec_imgsize) < 0) { - Error("Image conversion failed"); - return -3; - } + if ( !bOpen ) { + Warning("The encoder was not initialized, initializing now"); + Open(); + } - /* Set PTS */ - x264picin.i_pts = frame_time; + /* Convert the image into the x264 input picture */ + if ( swscaleobj.ConvertDefaults(data, data_size, x264picin.img.plane[0], codec_imgsize) < 0 ) { + Error("Image conversion failed"); + return -3; + } - /* Do the encoding */ - x264encodeloop(); + /* Set PTS */ + x264picin.i_pts = frame_time; - /* Increment frame counter */ - frame_count++; + /* Do the encoding */ + x264encodeloop(); - return 0; + /* Increment frame counter */ + frame_count++; + + return 0; } int X264MP4Writer::Encode(const Image* img, const unsigned int frame_time) { - - if(img->Width() != width) { - Error("Source image width differs. Source: %d Output: %d",img->Width(), width); - return -12; - } + if ( img->Width() != width ) { + Error("Source image width differs. Source: %d Output: %d", img->Width(), width); + return -12; + } - if(img->Height() != height) { - Error("Source image height differs. Source: %d Output: %d",img->Height(), height); - return -13; - } - - return Encode(img->Buffer(),img->Size(),frame_time); + if ( img->Height() != height ) { + Error("Source image height differs. Source: %d Output: %d", img->Height(), height); + return -13; + } + + return Encode(img->Buffer(), img->Size(), frame_time); } int X264MP4Writer::x264config() { - /* Sets up the encoder configuration */ + /* Sets up the encoder configuration */ - int x264ret; + int x264ret; - /* Defaults */ - const char* preset = "veryfast"; - const char* tune = "stillimage"; - const char* profile = "main"; + /* Defaults */ + const char* preset = "veryfast"; + const char* tune = "stillimage"; + const char* profile = "main"; - /* Search the user parameters for preset, tune and profile */ - for(unsigned int i=0; i < user_params.size(); i++) { - if(strcmp(user_params[i].pname, "preset") == 0) { - /* Got preset */ - preset = user_params[i].pvalue; - } else if(strcmp(user_params[i].pname, "tune") == 0) { - /* Got tune */ - tune = user_params[i].pvalue; - } else if(strcmp(user_params[i].pname, "profile") == 0) { - /* Got profile */ - profile = user_params[i].pvalue; - } - } + /* Search the user parameters for preset, tune and profile */ + for ( unsigned int i = 0; i < user_params.size(); i++ ) { + if ( strcmp(user_params[i].pname, "preset") == 0 ) { + /* Got preset */ + preset = user_params[i].pvalue; + } else if ( strcmp(user_params[i].pname, "tune") == 0 ) { + /* Got tune */ + tune = user_params[i].pvalue; + } else if ( strcmp(user_params[i].pname, "profile") == 0 ) { + /* Got profile */ + profile = user_params[i].pvalue; + } + } - /* Set the defaults and preset and tune */ - x264ret = x264_param_default_preset(&x264params, preset, tune); - if(x264ret != 0) { - Error("Failed setting x264 preset %s and tune %s : %d",preset,tune,x264ret); - } - - /* Set the profile */ - x264ret = x264_param_apply_profile(&x264params, profile); - if(x264ret != 0) { - Error("Failed setting x264 profile %s : %d",profile,x264ret); - } - - /* Input format */ - x264params.i_width = width; - x264params.i_height = height; - x264params.i_csp = X264_CSP_I420; - - /* Quality control */ - x264params.rc.i_rc_method = X264_RC_CRF; - x264params.rc.f_rf_constant = 23.0; + /* Set the defaults and preset and tune */ + x264ret = x264_param_default_preset(&x264params, preset, tune); + if ( x264ret != 0 ) { + Error("Failed setting x264 preset %s and tune %s : %d", preset, tune, x264ret); + } - /* Enable b-frames */ - x264params.i_bframe = 16; - x264params.i_bframe_adaptive = 1; - - /* Timebase */ - x264params.i_timebase_num = 1; - x264params.i_timebase_den = 1000; + /* Set the profile */ + x264ret = x264_param_apply_profile(&x264params, profile); + if ( x264ret != 0 ) { + Error("Failed setting x264 profile %s : %d", profile, x264ret); + } - /* Enable variable frame rate */ - x264params.b_vfr_input = 1; + /* Input format */ + x264params.i_width = width; + x264params.i_height = height; + x264params.i_csp = X264_CSP_I420; - /* Disable annex-b (start codes) */ - x264params.b_annexb = 0; - - /* TODO: Setup error handler */ - //x264params.i_log_level = X264_LOG_DEBUG; + /* Quality control */ + x264params.rc.i_rc_method = X264_RC_CRF; + x264params.rc.f_rf_constant = 23.0; - /* Process user parameters (excluding preset, tune and profile) */ - for(unsigned int i=0; i < user_params.size(); i++) { - /* Skip preset, tune and profile */ - if( (strcmp(user_params[i].pname, "preset") == 0) || (strcmp(user_params[i].pname, "tune") == 0) || (strcmp(user_params[i].pname, "profile") == 0) ) { - continue; - } + /* Enable b-frames */ + x264params.i_bframe = 16; + x264params.i_bframe_adaptive = 1; - /* Pass the name and value to x264 */ - x264ret = x264_param_parse(&x264params, user_params[i].pname, user_params[i].pvalue); + /* Timebase */ + x264params.i_timebase_num = 1; + x264params.i_timebase_den = 1000; - /* Error checking */ - if(x264ret != 0) { - if(x264ret == X264_PARAM_BAD_NAME) { - Error("Failed processing x264 user parameter %s=%s : Bad name", user_params[i].pname, user_params[i].pvalue); - } else if(x264ret == X264_PARAM_BAD_VALUE) { - Error("Failed processing x264 user parameter %s=%s : Bad value", user_params[i].pname, user_params[i].pvalue); - } else { - Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)", user_params[i].pname, user_params[i].pvalue, x264ret); - } - } - } - - return 0; + /* Enable variable frame rate */ + x264params.b_vfr_input = 1; + + /* Disable annex-b (start codes) */ + x264params.b_annexb = 0; + + /* TODO: Setup error handler */ + // x264params.i_log_level = X264_LOG_DEBUG; + + /* Process user parameters (excluding preset, tune and profile) */ + for ( unsigned int i = 0; i < user_params.size(); i++ ) { + /* Skip preset, tune and profile */ + if ( + (strcmp(user_params[i].pname, "preset") == 0) || + (strcmp(user_params[i].pname, "tune") == 0) || + (strcmp(user_params[i].pname, "profile") == 0) ) { + continue; + } + + /* Pass the name and value to x264 */ + x264ret = x264_param_parse(&x264params, user_params[i].pname, user_params[i].pvalue); + + /* Error checking */ + if ( x264ret != 0 ) { + if ( x264ret == X264_PARAM_BAD_NAME ) { + Error("Failed processing x264 user parameter %s=%s : Bad name", + user_params[i].pname, user_params[i].pvalue); + } else if ( x264ret == X264_PARAM_BAD_VALUE ) { + Error("Failed processing x264 user parameter %s=%s : Bad value", + user_params[i].pname, user_params[i].pvalue); + } else { + Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)", + user_params[i].pname, user_params[i].pvalue, x264ret); + } + } + } + + return 0; } void X264MP4Writer::x264encodeloop(bool bFlush) { + x264_nal_t* nals; + int i_nals; + int frame_size; - x264_nal_t* nals; - int i_nals; - int frame_size; + if ( bFlush ) { + frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, NULL, &x264picout); + } else { + frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, &x264picin, &x264picout); + } - if(bFlush) { - frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, NULL, &x264picout); - } else { - frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, &x264picin, &x264picout); - } + if ( frame_size > 0 || bFlush ) { + Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n", + frame_count, x264picout.i_pts, x264picout.i_dts, frame_size); - if (frame_size > 0 || bFlush) { - Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",frame_count, x264picout.i_pts, x264picout.i_dts, frame_size); + /* Handle the previous frame */ + if ( !bFirstFrame ) { + buffer.clear(); - /* Handle the previous frame */ - if(!bFirstFrame) { + /* Process the NALs for the previous frame */ + for ( unsigned int i = 0; i < prevnals.size(); i++ ) { + Debug(9, "Processing NAL: Type %d Size %d", + prevnals[i].i_type, + prevnals[i].i_payload); - buffer.clear(); + switch ( prevnals[i].i_type ) { + case NAL_PPS: + /* PPS NAL */ + MP4AddH264PictureParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4); + break; + case NAL_SPS: + /* SPS NAL */ + MP4AddH264SequenceParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4); + break; + default: + /* Anything else, hopefully frames, so copy it into the sample */ + buffer.append(prevnals[i].p_payload, prevnals[i].i_payload); + } + } - /* Process the NALs for the previous frame */ - for(unsigned int i=0; i < prevnals.size(); i++) { - Debug(9,"Processing NAL: Type %d Size %d",prevnals[i].i_type,prevnals[i].i_payload); + /* Calculate frame duration and offset */ + int duration = x264picout.i_dts - prevDTS; + int offset = prevPTS - prevDTS; - switch(prevnals[i].i_type) { - case NAL_PPS: - /* PPS NAL */ - MP4AddH264PictureParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4); - break; - case NAL_SPS: - /* SPS NAL */ - MP4AddH264SequenceParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4); - break; - default: - /* Anything else, hopefully frames, so copy it into the sample */ - buffer.append(prevnals[i].p_payload, prevnals[i].i_payload); - } - } + /* Write the sample */ + if ( !buffer.empty() ) { + if ( !MP4WriteSample( + mp4h, + mp4vtid, + buffer.extract(buffer.size()), + buffer.size(), + duration, + offset, + prevKeyframe) ) { + Error("Failed writing sample"); + } + } - /* Calculate frame duration and offset */ - int duration = x264picout.i_dts - prevDTS; - int offset = prevPTS - prevDTS; + /* Cleanup */ + prevnals.clear(); + prevpayload.clear(); + } - /* Write the sample */ - if(!buffer.empty()) { - if(!MP4WriteSample(mp4h, mp4vtid, buffer.extract(buffer.size()), buffer.size(), duration, offset, prevKeyframe)) { - Error("Failed writing sample"); - } - } + /* Got a frame. Copy this new frame into the previous frame */ + if ( frame_size > 0 ) { + /* Copy the NALs and the payloads */ + for ( unsigned int i = 0; i < i_nals; i++ ) { + prevnals.push_back(nals[i]); + prevpayload.append(nals[i].p_payload, nals[i].i_payload); + } - /* Cleanup */ - prevnals.clear(); - prevpayload.clear(); + /* Update the payload pointers */ + /* This is done in a separate loop because the previous loop might reallocate memory when appending, + making the pointers invalid */ + unsigned int payload_offset = 0; + for ( unsigned int i = 0; i < prevnals.size(); i++ ) { + prevnals[i].p_payload = prevpayload.head() + payload_offset; + payload_offset += nals[i].i_payload; + } - } + /* We need this for the next frame */ + prevPTS = x264picout.i_pts; + prevDTS = x264picout.i_dts; + prevKeyframe = x264picout.b_keyframe; - /* Got a frame. Copy this new frame into the previous frame */ - if(frame_size > 0) { - /* Copy the NALs and the payloads */ - for(int i=0;i* vec) { - if(vec == NULL) { - Error("NULL Encoder parameters vector pointer"); - return -1; - } +int ParseEncoderParameters( + const char* str, + std::vector* vec + ) { + if ( vec == NULL ) { + Error("NULL Encoder parameters vector pointer"); + return -1; + } - if(str == NULL) { - Error("NULL Encoder parameters string"); - return -2; - } + if ( str == NULL ) { + Error("NULL Encoder parameters string"); + return -2; + } - vec->clear(); + vec->clear(); - if(str[0] == 0) { - /* Empty */ - return 0; - } + if ( str[0] == 0 ) { + /* Empty */ + return 0; + } - std::string line; - std::stringstream ss(str); - size_t valueoffset; - size_t valuelen; - unsigned int lineno = 0; - EncoderParameter_t param; + std::string line; + std::stringstream ss(str); + size_t valueoffset; + size_t valuelen; + unsigned int lineno = 0; + EncoderParameter_t param; - while(std::getline(ss, line) ) { - lineno++; + while ( std::getline(ss, line) ) { + lineno++; - /* Remove CR if exists */ - if(line.length() >= 1 && line[line.length()-1] == '\r') { - line.erase(line.length()-1); - } + /* Remove CR if exists */ + if ( line.length() >= 1 && line[line.length()-1] == '\r' ) { + line.erase(line.length() - 1); + } - /* Skip comments and empty lines */ - if(line.empty() || line[0] == '#') { - continue; - } + /* Skip comments and empty lines */ + if ( line.empty() || line[0] == '#' ) { + continue; + } - valueoffset = line.find('='); - if(valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0) { - Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno); - continue; - } + valueoffset = line.find('='); + if ( valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0 ) { + Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno); + continue; + } + if ( valueoffset > (sizeof(param.pname) - 1 ) ) { + Warning("Failed parsing encoder parameters line %d: Name too long", lineno); + continue; + } - if(valueoffset > (sizeof(param.pname)-1) ) { - Warning("Failed parsing encoder parameters line %d: Name too long", lineno); - continue; - } + valuelen = line.length() - (valueoffset+1); - valuelen = line.length() - (valueoffset+1); + if ( valuelen > (sizeof(param.pvalue) - 1 ) ) { + Warning("Failed parsing encoder parameters line %d: Value too long", lineno); + continue; + } - if( valuelen > (sizeof(param.pvalue)-1) ) { - Warning("Failed parsing encoder parameters line %d: Value too long", lineno); - continue; - } + /* Copy and NULL terminate */ + line.copy(param.pname, valueoffset, 0); + line.copy(param.pvalue, valuelen, valueoffset+1); + param.pname[valueoffset] = 0; + param.pvalue[valuelen] = 0; - /* Copy and NULL terminate */ - line.copy(param.pname, valueoffset, 0); - line.copy(param.pvalue, valuelen, valueoffset+1); - param.pname[valueoffset] = 0; - param.pvalue[valuelen] = 0; + /* Push to the vector */ + vec->push_back(param); - /* Push to the vector */ - vec->push_back(param); + Debug(7, "Parsed encoder parameter: %s = %s", param.pname, param.pvalue); + } - Debug(7, "Parsed encoder parameter: %s = %s", param.pname, param.pvalue); - } + Debug(7, "Parsed %d lines", lineno); - Debug(7, "Parsed %d lines", lineno); - - return 0; + return 0; } diff --git a/utils/packpack/rsync_xfer.sh b/utils/packpack/rsync_xfer.sh index e981f646f..46cda18cb 100755 --- a/utils/packpack/rsync_xfer.sh +++ b/utils/packpack/rsync_xfer.sh @@ -37,7 +37,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then # Don't keep packages older than 5 days find ./zmrepo/$targetfolder/ -maxdepth 1 -type f -mtime +5 -delete - rsync -vzh --ignore-errors build/* zmrepo/$targetfolder/ + rsync -vzlh --ignore-errors build/* zmrepo/$targetfolder/ fusermount -zu zmrepo else echo diff --git a/web/includes/actions.php b/web/includes/actions.php index 4bd811a41..ad9e03ce5 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -176,8 +176,8 @@ if ( !empty($action) ) { } else { Error("No new Id despite new name"); } + $refreshParent = '/index.php?view=filter&Id='.$_REQUEST['Id']; } - $refreshParent = '/index.php?view=filter&Id='.$_REQUEST['Id']; } } // end if canedit events } // end if action == filter diff --git a/web/index.php b/web/index.php index c2b17c061..0e2ade668 100644 --- a/web/index.php +++ b/web/index.php @@ -21,29 +21,26 @@ error_reporting( E_ALL ); $debug = false; -if ( $debug ) -{ - // Use these for debugging, though not both at once! - phpinfo( INFO_VARIABLES ); - //error_reporting( E_ALL ); +if ( $debug ) { + // Use these for debugging, though not both at once! + phpinfo( INFO_VARIABLES ); + //error_reporting( E_ALL ); } // Use new style autoglobals where possible -if ( version_compare( phpversion(), "4.1.0", "<") ) -{ - $_SESSION = &$HTTP_SESSION_VARS; - $_SERVER = &$HTTP_SERVER_VARS; +if ( version_compare( phpversion(), '4.1.0', '<') ) { + $_SESSION = &$HTTP_SESSION_VARS; + $_SERVER = &$HTTP_SERVER_VARS; } // Useful debugging lines for mobile devices -if ( false ) -{ - ob_start(); - phpinfo( INFO_VARIABLES ); - $fp = fopen( "/tmp/env.html", "w" ); - fwrite( $fp, ob_get_contents() ); - fclose( $fp ); - ob_end_clean(); +if ( false ) { + ob_start(); + phpinfo( INFO_VARIABLES ); + $fp = fopen( '/tmp/env.html', 'w' ); + fwrite( $fp, ob_get_contents() ); + fclose( $fp ); + ob_end_clean(); } require_once( 'includes/config.php' ); @@ -53,26 +50,23 @@ require_once( 'includes/Storage.php' ); require_once( 'includes/Event.php' ); require_once( 'includes/Monitor.php' ); -if ( isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' ) -{ - $protocol = 'https'; +if ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ) { + $protocol = 'https'; +} else { + $protocol = 'http'; } -else -{ - $protocol = 'http'; -} -define( "ZM_BASE_PROTOCOL", $protocol ); +define( 'ZM_BASE_PROTOCOL', $protocol ); // Absolute URL's are unnecessary and break compatibility with reverse proxies // define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] ); // Use relative URL's instead -define( "ZM_BASE_URL", "" ); +define( 'ZM_BASE_URL', '' ); // Check time zone is set if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) { - date_default_timezone_set('UTC'); - Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); + date_default_timezone_set('UTC'); + Fatal( "ZoneMinder is not installed properly: php's date.timezone is not set to a valid timezone" ); } if ( isset($_GET['skin']) ) @@ -82,7 +76,7 @@ elseif ( isset($_COOKIE['zmSkin']) ) elseif ( defined('ZM_SKIN_DEFAULT') ) $skin = ZM_SKIN_DEFAULT; else - $skin = "classic"; + $skin = 'classic'; $skins = array_map( 'basename', glob('skins/*',GLOB_ONLYDIR) ); if ( ! in_array( $skin, $skins ) ) { @@ -97,7 +91,7 @@ elseif ( isset($_COOKIE['zmCSS']) ) elseif (defined('ZM_CSS_DEFAULT')) $css = ZM_CSS_DEFAULT; else - $css = "classic"; + $css = 'classic'; $css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) ); if ( ! in_array( $css, $css_skins ) ) { @@ -105,9 +99,9 @@ if ( ! in_array( $css, $css_skins ) ) { $css = $css_skins[0]; } -define( "ZM_BASE_PATH", dirname( $_SERVER['REQUEST_URI'] ) ); -define( "ZM_SKIN_NAME", $skin ); -define( "ZM_SKIN_PATH", "skins/$skin" ); +define( 'ZM_BASE_PATH', dirname( $_SERVER['REQUEST_URI'] ) ); +define( 'ZM_SKIN_NAME', $skin ); +define( 'ZM_SKIN_PATH', "skins/$skin" ); $skinBase = array(); // To allow for inheritance of skins if ( !file_exists( ZM_SKIN_PATH ) ) @@ -117,26 +111,25 @@ $skinBase[] = $skin; $currentCookieParams = session_get_cookie_params(); Logger::Debug('Setting cookie parameters to lifetime('.$currentCookieParams['lifetime'].') path('.$currentCookieParams['path'].') domain ('.$currentCookieParams['domain'].') secure('.$currentCookieParams['secure'].') httpOnly(1)'); session_set_cookie_params( - $currentCookieParams["lifetime"], - $currentCookieParams["path"], - $currentCookieParams["domain"], - $currentCookieParams["secure"], + $currentCookieParams['lifetime'], + $currentCookieParams['path'], + $currentCookieParams['domain'], + $currentCookieParams['secure'], true ); -ini_set( "session.name", "ZMSESSID" ); +ini_set( 'session.name', 'ZMSESSID' ); session_start(); -if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) -{ +if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) { $_SESSION['skin'] = $skin; - setcookie( "zmSkin", $skin, time()+3600*24*30*12*10 ); + setcookie( 'zmSkin', $skin, time()+3600*24*30*12*10 ); } if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) { $_SESSION['css'] = $css; - setcookie( "zmCSS", $css, time()+3600*24*30*12*10 ); + setcookie( 'zmCSS', $css, time()+3600*24*30*12*10 ); } if ( ZM_OPT_USE_AUTH ) @@ -149,14 +142,12 @@ else require_once( 'includes/lang.php' ); require_once( 'includes/functions.php' ); -require_once( 'includes/csrf/csrf-magic.php' ); # Add Cross domain access headers CORSHeaders(); // Check for valid content dirs -if ( !is_writable(ZM_DIR_EVENTS) || !is_writable(ZM_DIR_IMAGES) ) -{ +if ( !is_writable(ZM_DIR_EVENTS) || !is_writable(ZM_DIR_IMAGES) ) { Error( "Cannot write to content dirs('".ZM_DIR_EVENTS."','".ZM_DIR_IMAGES."'). Check that these exist and are owned by the web account user"); } @@ -177,58 +168,51 @@ isset($view) || $view = NULL; isset($request) || $request = NULL; isset($action) || $action = NULL; -if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' ) { - Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); - csrf_check(); +if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' ) { + require_once( 'includes/csrf/csrf-magic.php' ); + Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); + csrf_check(); } require_once( 'includes/actions.php' ); # If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in. if ( ZM_OPT_USE_AUTH && ! isset($user) && $view != 'login' ) { - $view = 'login'; + $view = 'login'; } # Only one request can open the session file at a time, so let's close the session here to improve concurrency. # Any file/page that uses the session must re-open it. session_write_close(); -if ( isset( $_REQUEST['request'] ) ) -{ - foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) - { - if ( !file_exists( $includeFile ) ) - Fatal( "Request '$request' does not exist" ); +if ( isset( $_REQUEST['request'] ) ) { + foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) { + if ( !file_exists( $includeFile ) ) + Fatal( "Request '$request' does not exist" ); + require_once $includeFile; + } + return; +} else { + if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) { + foreach ( $includeFiles as $includeFile ) { + if ( !file_exists( $includeFile ) ) + Fatal( "View '$view' does not exist" ); + require_once $includeFile; + } + // If the view overrides $view to 'error', and the user is not logged in, then the + // issue is probably resolvable by logging in, so provide the opportunity to do so. + // The login view should handle redirecting to the correct location afterward. + if ( $view == 'error' && !isset($user) ) { + $view = 'login'; + foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile ) require_once $includeFile; } - return; -} -else -{ - if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) - { - foreach ( $includeFiles as $includeFile ) - { - if ( !file_exists( $includeFile ) ) - Fatal( "View '$view' does not exist" ); - require_once $includeFile; - } - // If the view overrides $view to 'error', and the user is not logged in, then the - // issue is probably resolvable by logging in, so provide the opportunity to do so. - // The login view should handle redirecting to the correct location afterward. - if ( $view == 'error' && !isset($user) ) - { - $view = 'login'; - foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile ) - require_once $includeFile; - } - } - // If the view is missing or the view still returned error with the user logged in, - // then it is not recoverable. - if ( !$includeFiles || $view == 'error' ) - { - foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile ) - require_once $includeFile; - } + } + // If the view is missing or the view still returned error with the user logged in, + // then it is not recoverable. + if ( !$includeFiles || $view == 'error' ) { + foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile ) + require_once $includeFile; + } } ?>