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/ubuntu1204/zoneminder.dirs b/distros/ubuntu1204/zoneminder.dirs index 4178482c1..ff1ec7858 100644 --- a/distros/ubuntu1204/zoneminder.dirs +++ b/distros/ubuntu1204/zoneminder.dirs @@ -4,3 +4,5 @@ var/cache/zoneminder/events var/cache/zoneminder/images var/cache/zoneminder/temp usr/share/zoneminder/db +etc/zm +etc/zm/conf.d 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/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst index 9d786e27d..d01d27925 100644 --- a/distros/ubuntu1204/zoneminder.postinst +++ b/distros/ubuntu1204/zoneminder.postinst @@ -4,51 +4,51 @@ 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 + # 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 - # Do this every time the package is installed or upgraded + # Do this every time the package is installed or upgraded + # Ensure zoneminder is stopped + invoke-rc.d zoneminder stop || true - if [ "$ZM_DB_HOST" = "localhost" ]; 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 - invoke-rc.d mysql start - 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 - - # Ensure zoneminder is stopped - invoke-rc.d zoneminder stop || true - zmupdate.pl --nointeractive - zmupdate.pl --nointeractive -f - echo "Done Updating, starting ZoneMinder" - invoke-rc.d zoneminder start || true - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi - else - echo 'mysql not found, assuming remote server.' + if [ "$ZM_DB_HOST" = "localhost" ]; 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 + invoke-rc.d mysql start + 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 else - echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + echo 'mysql not found, assuming remote server.' fi + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + fi + echo "Done Updating, starting ZoneMinder" + invoke-rc.d zoneminder start || true fi #DEBHELPER# 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 9e3f117a4..e06cdd3b5 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -21,6 +21,8 @@ if [ "$1" = "configure" ]; then # 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 @@ -46,10 +48,10 @@ if [ "$1" = "configure" ]; then 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.' + echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' fi else - echo 'mysql not found, assuming remote server.' + echo 'mysql not found, assuming remote server.' fi else diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst index 80a491a2a..28259d4d3 100644 --- a/docs/userguide/definemonitor.rst +++ b/docs/userguide/definemonitor.rst @@ -72,6 +72,7 @@ Source Tab FFmpeg ^^^^^^ This is the recommended source type for most modern ip cameras. + Source Path Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this: 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_logger.cpp b/src/zm_logger.cpp index 6cfe00163..ee3a5a8b5 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -195,7 +195,7 @@ void Logger::initialise( const std::string &id, const Options &options ) { level( tempLevel ); mFlush = false; - if (envPtr = getenv( "LOG_FLUSH")) { + if ( (envPtr = getenv("LOG_FLUSH")) ) { mFlush = atoi( envPtr ); } else if ( config.log_debug ) { mFlush = true; 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_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; + } } ?>