From e7de8e6d7b51a16e1c4ce31e4e7b366fd934466d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Jun 2017 10:59:50 -0400 Subject: [PATCH 001/538] use variable instead of recalculating --- scripts/zmtelemetry.pl.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index e00f8ec0a..b3b339593 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -76,7 +76,7 @@ if ( $Config{ZM_TELEMETRY_DATA} or $force ) { Warning( 'Seconds since last check is negative! Which means that lastCheck is in the future!' ); next; } - if ( ( ($now-$lastCheck) > $interval ) or $force ) { + if ( ( ($since_last_check) > $interval ) or $force ) { print "Collecting data to send to ZoneMinder Telemetry server.\n"; my $dbh = zmDbConnect(); # Build the telemetry hash From 52d5099677c13c840218d030b52112f1df6135ff Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Jun 2017 11:01:57 -0400 Subject: [PATCH 002/538] fix quotes --- scripts/zmtelemetry.pl.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index b3b339593..62dbf67f8 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -149,7 +149,7 @@ sub sendData { my $server_endpoint = $Config{ZM_TELEMETRY_SERVER_ENDPOINT}; if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { - $ua->proxy( "https", $Config{ZM_UPDATE_CHECK_PROXY} ); + $ua->proxy( 'https', $Config{ZM_UPDATE_CHECK_PROXY} ); } Debug("Posting telemetry data to: $server_endpoint"); @@ -166,7 +166,7 @@ sub sendData { my $resp_msg = $resp->decoded_content; my $resp_code = $resp->code; if ($resp->is_success) { - Info("Telemetry data uploaded successfully."); + 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"); From e0d99018aaab10b367bde40833afd496b3b9ee25 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 19 Oct 2017 16:08:20 -0400 Subject: [PATCH 003/538] Google code style, add a warning if no sd --- src/zm_stream.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index c0b8701a2..2fd7666bb 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -77,17 +77,13 @@ void StreamBase::updateFrameRate( double fps ) Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); } -bool StreamBase::checkCommandQueue() -{ - if ( sd >= 0 ) - { +bool StreamBase::checkCommandQueue() { + if ( sd >= 0 ) { CmdMsg msg; memset( &msg, 0, sizeof(msg) ); int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); - if ( nbytes < 0 ) - { - if ( errno != EAGAIN ) - { + if ( nbytes < 0 ) { + if ( errno != EAGAIN ) { Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); } } @@ -95,11 +91,12 @@ bool StreamBase::checkCommandQueue() //{ //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); //} - else - { + else { processCommand( &msg ); return( true ); } + } else { + Warning("No sd in checkCommandQueue, comms not open?"); } return( false ); } From b6a1885167c40e67f13f86c4a61216e83c04adcb Mon Sep 17 00:00:00 2001 From: digital-gnome <31593470+digital-gnome@users.noreply.github.com> Date: Sat, 18 Nov 2017 19:02:56 -0500 Subject: [PATCH 004/538] fix generated video download (#2002) Fixes out of memory php error when downloading large generated video files --- web/skins/classic/views/video.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/skins/classic/views/video.php b/web/skins/classic/views/video.php index cfbb6f929..6a20ed319 100644 --- a/web/skins/classic/views/video.php +++ b/web/skins/classic/views/video.php @@ -101,6 +101,9 @@ if ( isset($_REQUEST['downloadIndex']) ) header( "Content-Transfer-Encoding: binary" ); header( "Content-Type: application/force-download" ); header( "Content-Length: ".filesize($videoFiles[$downloadIndex]) ); + while ( ob_get_level() > 0 ) { + ob_end_clean(); + } readfile( $videoFiles[$downloadIndex] ); exit; } From 630dde26d1df9dacecbab6a304413f82359c58b6 Mon Sep 17 00:00:00 2001 From: flashoftheblades Date: Mon, 5 Feb 2018 18:52:05 -0500 Subject: [PATCH 005/538] Add files via upload --- .../lib/ZoneMinder/Control/Reolink.pm | 630 ++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm new file mode 100644 index 000000000..3ccc5a38e --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm @@ -0,0 +1,630 @@ +# ========================================================================== +# +# ZoneMinder Reolink IP Control Protocol Module, Date: 2016-01-19 +# Converted for use with Reolink IP Camera by Chris Swertfeger +# Copyright (C) 2016 Chris Swertfeger +# +# 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. +# +# ========================================================================== +# +# This module contains the first implementation of the Reolink IP camera control +# protocol +# +package ZoneMinder::Control::Reolink; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +our %CamParams = (); + +# ========================================================================== +# +# Reolink IP Control Protocol +# This script sends ONVIF compliant commands and may work with other cameras +# that require authentication +# +# The script was developed against a RLC-423 and RLC-420. +# +# Basic preset functions are supported, but more advanced features, which make +# use of abnormally high preset numbers (ir lamp control, tours, pan speed, etc) +# may or may not work. +# +# +# On ControlAddress use the format : +# USERNAME:PASSWORD@ADDRESS:PORT +# eg : admin:pass@10.1.2.1:8899 +# admin:password@10.0.100.1:8899 +# +# Use port 8000 by default for Reolink cameras +# +# Make sure and place a value in the Auto Stop Timeout field. +# Recommend starting with a value of 1 second, and adjust accordingly. +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + +use Time::HiRes qw( usleep ); + +use MIME::Base64; +use Digest::SHA; +use DateTime; + +my ($username,$password,$host,$port); + +sub new +{ + + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + my $logindetails = ""; + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref( ) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); + } + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + # + # Extract the username/password host/port from ControlAddress + # + if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ ) + { # user:pass@host... + $username = $1; + $password = $2; + $host = $3; + } + elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ ) + { # user@host... + $username = $1; + $host = $2; + } + else { # Just a host + $host = $self->{Monitor}{ControlAddress}; + } + # Check if it is a host and port or just a host + if( $host =~ /([^:]+):(.+)/ ) + { + $host = $1; + $port = $2; + } + else + { + $port = 80; + } + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + my $msg = shift; + my $content_type = shift; + my $result = undef; + + printMsg( $cmd, "Tx" ); + + my $server_endpoint = "http://".$host.":".$port."/$cmd"; + my $req = HTTP::Request->new( POST => $server_endpoint ); + $req->header('content-type' => $content_type); + $req->header('Host' => $host.":".$port); + $req->header('content-length' => length($msg)); + $req->header('accept-encoding' => 'gzip, deflate'); + $req->header('connection' => 'close'); + $req->content($msg); + + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + } else { + Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" ); + } + return( $result ); +} + +sub getCamParams +{ + my $self = shift; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg = ''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging"; + my $req = HTTP::Request->new( POST => $server_endpoint ); + $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); + $req->header('Host' => $host.":".$port); + $req->header('content-length' => length($msg)); + $req->header('accept-encoding' => 'gzip, deflate'); + $req->header('connection' => 'Close'); + $req->content($msg); + + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + # We should really use an xml or soap library to parse the xml tags + my $content = $res->decoded_content; + + if ($content =~ /.*(.+)<\/tt:Brightness>.*/) { + $CamParams{$1} = $2; + } + if ($content =~ /.*(.+)<\/tt:Contrast>.*/) { + $CamParams{$1} = $2; + } + } + else + { + Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" ); + } +} + +#autoStop +#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab +sub autoStop +{ + my $self = shift; + my $autostop = shift; + + if( $autostop ) { + Debug( "Auto Stop" ); + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg = ''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000truefalse'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + usleep( $autostop ); + $self->sendCmd( $cmd, $msg, $content_type ); + } +} + +# Reset the Camera +sub reset +{ + Debug( "Camera Reset" ); + my $self = shift; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $cmd = ""; + my $msg = ''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Up Arrow +sub moveConUp +{ + Debug( "Move Up" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Down Arrow +sub moveConDown +{ + Debug( "Move Down" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Left Arrow +sub moveConLeft +{ + Debug( "Move Left" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Right Arrow +sub moveConRight +{ + Debug( "Move Right" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Zoom In +sub zoomConTele +{ + Debug( "Zoom Tele" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Zoom Out +sub zoomConWide +{ + Debug( "Zoom Wide" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Right Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConUpRight +{ + Debug( "Move Diagonally Up Right" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Down Right Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConDownRight +{ + Debug( "Move Diagonally Down Right" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Left Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConUpLeft +{ + Debug( "Move Diagonally Up Left" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Down Left Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConDownLeft +{ + Debug( "Move Diagonally Down Left" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Stop +sub moveStop +{ + Debug( "Move Stop" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000truefalse'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Set Camera Preset +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Set Preset $preset" ); + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'.$preset.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Recall Camera Preset +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $num = sprintf("%03d", $preset); + $num=~ tr/ /0/; + Debug( "Goto Preset $preset" ); + my $cmd = 'onvif/PTZ'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'.$num.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Horizontal Patrol +#To be determined if this camera supports this feature +sub horizontalPatrol +{ + Debug( "Horizontal Patrol" ); + my $self = shift; + my $cmd = ''; + my $msg =''; + my $content_type = ''; +# $self->sendCmd( $cmd, $msg, $content_type ); + Error( "PTZ Command not implemented in control script." ); +} + +#Horizontal Patrol Stop +#To be determined if this camera supports this feature +sub horizontalPatrolStop +{ + Debug( "Horizontal Patrol Stop" ); + my $self = shift; + my $cmd = ''; + my $msg =''; + my $content_type = ''; +# $self->sendCmd( $cmd, $msg, $content_type ); + Error( "PTZ Command not implemented in control script." ); +} + +# Increase Brightness +sub irisAbsOpen +{ + Debug( "Iris $CamParams{'Brightness'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'Brightness'}); + my $step = $self->getParam( $params, 'step' ); + my $max = 100; + + $CamParams{'Brightness'} += $step; + $CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max); + + my $cmd = 'onvif/imaging'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'.$CamParams{'Brightness'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +# Decrease Brightness +sub irisAbsClose +{ + Debug( "Iris $CamParams{'Brightness'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'brightness'}); + my $step = $self->getParam( $params, 'step' ); + my $min = 0; + + $CamParams{'Brightness'} -= $step; + $CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min); + + my $cmd = 'onvif/imaging'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'.$CamParams{'Brightness'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +# Increase Contrast +sub whiteAbsIn +{ + Debug( "Iris $CamParams{'Contrast'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'Contrast'}); + my $step = $self->getParam( $params, 'step' ); + my $max = 100; + + $CamParams{'Contrast'} += $step; + $CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max); + + my $cmd = 'onvif/imaging'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'.$CamParams{'Contrast'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; +} + +# Decrease Contrast +sub whiteAbsOut +{ + Debug( "Iris $CamParams{'Contrast'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'Contrast'}); + my $step = $self->getParam( $params, 'step' ); + my $min = 0; + + $CamParams{'Contrast'} -= $step; + $CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min); + + my $cmd = 'onvif/imaging'; + my $nonce; + for (0..20){$nonce .= chr(int(rand(254)));} + my $mydate = DateTime->now()->iso8601().'Z'; + my $sha = Digest::SHA->new(1); + $sha->add($nonce.$mydate.$password); + my $digest = encode_base64($sha->digest,""); + my $msg =''.$username.''.$digest.''.encode_base64($nonce,"").''.$mydate.'000'.$CamParams{'Contrast'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; +} + +1; + From 34f65840818e7398e7ece22520cb31540ac3e0f9 Mon Sep 17 00:00:00 2001 From: flashoftheblades Date: Mon, 5 Feb 2018 18:53:47 -0500 Subject: [PATCH 006/538] Update zm_create.sql.in --- db/zm_create.sql.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 7af72b613..8f4958cf1 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -604,6 +604,9 @@ INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); -- -- Add some monitor preset values From 6e240f669cf0188019e55e81ece9d4682c7896b2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 8 Feb 2018 14:25:58 -0800 Subject: [PATCH 007/538] fix filtering --- web/skins/classic/views/montagereview.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 031de0896..0bbb17653 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -122,8 +122,8 @@ if ( ! empty( $user['MonitorIds'] ) ) { $eventsSql .= ' AND M.Id IN ('.$user['MonitorIds'].')'; $frameSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')'; } -if ( count($selected_monitor_ids) ) { - $monitor_ids_sql = ' IN (' . implode(',',$selected_monitor_ids).')'; +if ( count($displayMonitors) ) { + $monitor_ids_sql = ' IN (' . implode(',',array_map( function($Monitor){return $Monitor['Id'];}, $displayMonitors) ) . ')'; $eventsSql .= ' AND M.Id '.$monitor_ids_sql; $frameSql .= ' AND E.MonitorId '.$monitor_ids_sql; } From ec7f2e48d0b08c16806efcecb205be520f05d4e8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 17 Feb 2018 10:54:53 -0500 Subject: [PATCH 008/538] add back AnalysisFPS reporting and move the gettimeofday in after it is actually needed --- src/zm_monitor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index daaa5cdb3..c8e75b5ce 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1225,9 +1225,9 @@ void Monitor::CheckAction() { } void Monitor::UpdateAnalysisFPS() { - struct timeval now; - gettimeofday( &now, NULL ); if ( analysis_image_count && fps_report_interval && !(analysis_image_count%fps_report_interval) ) { + struct timeval now; + gettimeofday(&now, NULL); double new_analysis_fps = double(fps_report_interval)/(now.tv_sec - last_analysis_fps_time); Info("%s: %d - Analysing at %.2f fps", name, image_count, new_analysis_fps); if ( new_analysis_fps != analysis_fps ) { @@ -1562,6 +1562,7 @@ bool Monitor::Analyse() { analysis_image_count++; snap->unlock(); } // end while not at end of packetqueue + UpdateAnalysisFPS(); if ( packets_processed > 0 ) return true; return false; From 1ace6861c8328c4b9b525389b02ed5faee99e254 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 19 Feb 2018 11:30:27 -0600 Subject: [PATCH 009/538] raspbian support to buildsystem --- utils/packpack/startpackpack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 4bf9e9ff6..9d58a40a9 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -295,7 +295,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then execpackpack # Steps common to Debian based distros - elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then + elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then echo "Begin ${OS} ${DIST} build..." setdebpkgname From 2bc47ca6ec6a6ea187243dd62ddf477e37909a39 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 19 Feb 2018 19:31:22 -0600 Subject: [PATCH 010/538] add qemu arm support to travis ci --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 52ba2d486..8068f952e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,12 @@ addons: - curl - sshfs - sed + - binfmt-support + - qemu + - qemu-user-static +install: + - update-binfmts --enable qemu-arm + env: matrix: - OS=el DIST=6 @@ -34,6 +40,7 @@ env: - OS=ubuntu DIST=xenial - OS=ubuntu DIST=trusty ARCH=i386 - OS=ubuntu DIST=xenial ARCH=i386 + compiler: - gcc services: From c7aa45c4ce8913cbd1b8207f5f2a4cffc2c86027 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 19 Feb 2018 20:21:21 -0600 Subject: [PATCH 011/538] libmysqlclient-dev debian package name changes --- distros/ubuntu1604/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index efbaa0651..09c4ce9bc 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -16,7 +16,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa ,libcurl4-gnutls-dev ,libgnutls-openssl-dev ,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev - ,libmysqlclient-dev + ,default-libmysqlclient-dev | libmysqlclient-dev ,libpcre3-dev ,libpolkit-gobject-1-dev ,libv4l-dev (>= 0.8.3) [!hurd-any] From 3fa4c525f8eb973e9400c7a543a9663adfd7c450 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 19 Feb 2018 20:33:41 -0600 Subject: [PATCH 012/538] Add Raspbian Stretch to buildsystem --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8068f952e..92aeeec56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,7 @@ env: - OS=ubuntu DIST=xenial - OS=ubuntu DIST=trusty ARCH=i386 - OS=ubuntu DIST=xenial ARCH=i386 + - OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack compiler: - gcc From d228cc79d3c10e5eb7e1faf44a1ec2699d6453b6 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 20 Feb 2018 18:31:19 -0600 Subject: [PATCH 013/538] attempt to speed up the build --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 92aeeec56..dc1653c30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,8 @@ install: - update-binfmts --enable qemu-arm env: + global: + - DEB_BUILD_OPTIONS="parallel=4" matrix: - OS=el DIST=6 - OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack From 11660b42d498864637314c0d588754e3619e33dc Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 20 Feb 2018 18:40:50 -0600 Subject: [PATCH 014/538] fix zoneminder github url --- docs/installationguide/packpack.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst index 6b558d11c..8e52ad876 100644 --- a/docs/installationguide/packpack.rst +++ b/docs/installationguide/packpack.rst @@ -44,7 +44,7 @@ Clone the ZoneMinder project if you have not done so already. :: - git clone https://ZoneMinder/ZoneMinder + git clone https://github.com/ZoneMinder/ZoneMinder cd ZoneMinder Alternatively, if you have already cloned the repo and wish to update it, do the following. From 7d5f73d7c4ddf654e962a2763765fc550804806b Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 20 Feb 2018 20:35:37 -0600 Subject: [PATCH 015/538] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dc1653c30..c629385d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ install: env: global: - DEB_BUILD_OPTIONS="parallel=4" + - SMPFLAGS="-j4" matrix: - OS=el DIST=6 - OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack From aaf3b4137bea8790074e2777d3d38a2473ae3e8c Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 20 Feb 2018 20:50:10 -0600 Subject: [PATCH 016/538] skip lintian checks for debian builds --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c629385d7..ddcd1e519 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ install: env: global: - DEB_BUILD_OPTIONS="parallel=4" + - DEBUILD_LINTIAN="no" - SMPFLAGS="-j4" matrix: - OS=el DIST=6 From ebc6bf0dcc679340ec9713d08a1777bde377e946 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 21 Feb 2018 09:56:19 -0600 Subject: [PATCH 017/538] move debian tweaks to startpackpack.sh --- .travis.yml | 4 ---- utils/packpack/startpackpack.sh | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddcd1e519..92aeeec56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,10 +30,6 @@ install: - update-binfmts --enable qemu-arm env: - global: - - DEB_BUILD_OPTIONS="parallel=4" - - DEBUILD_LINTIAN="no" - - SMPFLAGS="-j4" matrix: - OS=el DIST=6 - OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 9d58a40a9..9cb5f7e21 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -231,6 +231,7 @@ execpackpack () { parms="-f utils/packpack/redhat_package.mk redhat_package" else parms="" + export SMPFLAGS="-j$(nproc) --no-lintian" fi if [ "${TRAVIS}" == "true" ]; then From f8000d7438d7afe8848fa43a94821e08f3281715 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 21 Feb 2018 10:16:35 -0600 Subject: [PATCH 018/538] Update startpackpack.sh --- utils/packpack/startpackpack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 9cb5f7e21..a2c7f4314 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -231,7 +231,7 @@ execpackpack () { parms="-f utils/packpack/redhat_package.mk redhat_package" else parms="" - export SMPFLAGS="-j$(nproc) --no-lintian" + export SMPFLAGS="-j4 --lintian --no-lintian" fi if [ "${TRAVIS}" == "true" ]; then From 4832de9217165d0a8b474ead53438ddf12f7b9a9 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 21 Feb 2018 10:19:35 -0600 Subject: [PATCH 019/538] fix typo --- utils/packpack/startpackpack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index a2c7f4314..7a7dc7d2f 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -231,7 +231,7 @@ execpackpack () { parms="-f utils/packpack/redhat_package.mk redhat_package" else parms="" - export SMPFLAGS="-j4 --lintian --no-lintian" + export SMPFLAGS="-j4 --lintian-opts --no-lintian" fi if [ "${TRAVIS}" == "true" ]; then From b6ad2b6ad75a57c4223cdfb29a458c64de7de6af Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 21 Feb 2018 12:15:10 -0600 Subject: [PATCH 020/538] patch packpack to support custom environment vars --- utils/packpack/environment | 3 +++ utils/packpack/envvars.patch | 25 +++++++++++++++++++++++++ utils/packpack/startpackpack.sh | 9 ++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 utils/packpack/environment create mode 100644 utils/packpack/envvars.patch diff --git a/utils/packpack/environment b/utils/packpack/environment new file mode 100644 index 000000000..4c8305d6f --- /dev/null +++ b/utils/packpack/environment @@ -0,0 +1,3 @@ +DEB_BUILD_OPTIONS="parallel=4" +DEBUILD_LINTIAN="no" + diff --git a/utils/packpack/envvars.patch b/utils/packpack/envvars.patch new file mode 100644 index 000000000..b1406a09b --- /dev/null +++ b/utils/packpack/envvars.patch @@ -0,0 +1,25 @@ +From 871835ecc4ee380fe42d1ef2a58dca9dc4e9af5f Mon Sep 17 00:00:00 2001 +From: Andrew Bauer +Date: Wed, 21 Feb 2018 12:07:12 -0600 +Subject: [PATCH] allow one to load custom environment variables + +--- + packpack | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/packpack b/packpack +index 6f4c80f..abab604 100755 +--- a/packpack ++++ b/packpack +@@ -128,6 +128,11 @@ chmod a+x ${BUILDDIR}/userwrapper.sh + env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=" \ + > ${BUILDDIR}/env + ++# Load user environment variables ++if [ -f "${USER_ENV_FILE}" ]; then ++ cat ${USER_ENV_FILE} >> ${BUILDDIR}/env ++fi ++ + # + # Setup cache directory + # diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 7a7dc7d2f..49446a9bf 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -108,6 +108,12 @@ commonprep () { patch -p1 < utils/packpack/packpack-rpm.patch fi + # Patch packpack + patch --dry-run --silent -f -p1 < utils/packpack/envvars.patch + if [ $? -eq 0 ]; then + patch -p1 < utils/packpack/envvars.patch + fi + # The rpm specfile requires we download the tarball and manually move it into place # Might as well do this for Debian as well, rather than git submodule init CRUDVER="3.0.10" @@ -231,7 +237,8 @@ execpackpack () { parms="-f utils/packpack/redhat_package.mk redhat_package" else parms="" - export SMPFLAGS="-j4 --lintian-opts --no-lintian" + export SMPFLAGS="-j4" + export USER_ENV_FILE="utils/packpack/environment" fi if [ "${TRAVIS}" == "true" ]; then From 8ef0386db2d4f1f60db3774756962f0a9b0d5a6a Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 21 Feb 2018 12:30:33 -0600 Subject: [PATCH 021/538] adjust the path in the packpack patch file --- utils/packpack/envvars.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/packpack/envvars.patch b/utils/packpack/envvars.patch index b1406a09b..3a3f2682a 100644 --- a/utils/packpack/envvars.patch +++ b/utils/packpack/envvars.patch @@ -9,8 +9,8 @@ Subject: [PATCH] allow one to load custom environment variables diff --git a/packpack b/packpack index 6f4c80f..abab604 100755 ---- a/packpack -+++ b/packpack +--- a/packpack/packpack ++++ b/packpack/packpack @@ -128,6 +128,11 @@ chmod a+x ${BUILDDIR}/userwrapper.sh env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=" \ > ${BUILDDIR}/env From 075f8d461c08d9360f5b277971f8057816f77aeb Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Wed, 21 Feb 2018 17:15:18 -0600 Subject: [PATCH 022/538] Docker treats quotes literally in env-file see: https://github.com/moby/moby/issues/11443#issuecomment-99198051 --- utils/packpack/environment | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/packpack/environment b/utils/packpack/environment index 4c8305d6f..64ed12781 100644 --- a/utils/packpack/environment +++ b/utils/packpack/environment @@ -1,3 +1,3 @@ -DEB_BUILD_OPTIONS="parallel=4" -DEBUILD_LINTIAN="no" +DEB_BUILD_OPTIONS=parallel=4 +DEBUILD_LINTIAN=no From 7b9cb59df92a7a340ced39ae951ca560a8b56a12 Mon Sep 17 00:00:00 2001 From: Andy Bauer Date: Thu, 22 Feb 2018 08:58:33 -0600 Subject: [PATCH 023/538] force no-lintian flag --- .travis.yml | 2 ++ utils/packpack/environment | 3 --- utils/packpack/envvars.patch | 25 ------------------------- utils/packpack/nolintian.patch | 22 ++++++++++++++++++++++ utils/packpack/startpackpack.sh | 8 +++----- 5 files changed, 27 insertions(+), 33 deletions(-) delete mode 100644 utils/packpack/environment delete mode 100644 utils/packpack/envvars.patch create mode 100644 utils/packpack/nolintian.patch diff --git a/.travis.yml b/.travis.yml index 92aeeec56..531fed4da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,8 @@ install: - update-binfmts --enable qemu-arm env: + global: + - SMPFLAGS=-j4 matrix: - OS=el DIST=6 - OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack diff --git a/utils/packpack/environment b/utils/packpack/environment deleted file mode 100644 index 64ed12781..000000000 --- a/utils/packpack/environment +++ /dev/null @@ -1,3 +0,0 @@ -DEB_BUILD_OPTIONS=parallel=4 -DEBUILD_LINTIAN=no - diff --git a/utils/packpack/envvars.patch b/utils/packpack/envvars.patch deleted file mode 100644 index 3a3f2682a..000000000 --- a/utils/packpack/envvars.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 871835ecc4ee380fe42d1ef2a58dca9dc4e9af5f Mon Sep 17 00:00:00 2001 -From: Andrew Bauer -Date: Wed, 21 Feb 2018 12:07:12 -0600 -Subject: [PATCH] allow one to load custom environment variables - ---- - packpack | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/packpack b/packpack -index 6f4c80f..abab604 100755 ---- a/packpack/packpack -+++ b/packpack/packpack -@@ -128,6 +128,11 @@ chmod a+x ${BUILDDIR}/userwrapper.sh - env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=" \ - > ${BUILDDIR}/env - -+# Load user environment variables -+if [ -f "${USER_ENV_FILE}" ]; then -+ cat ${USER_ENV_FILE} >> ${BUILDDIR}/env -+fi -+ - # - # Setup cache directory - # diff --git a/utils/packpack/nolintian.patch b/utils/packpack/nolintian.patch new file mode 100644 index 000000000..3ef36451c --- /dev/null +++ b/utils/packpack/nolintian.patch @@ -0,0 +1,22 @@ +From 634281a4204467b9a3c8a1a5febcc8dd9828e0f6 Mon Sep 17 00:00:00 2001 +From: Andy Bauer +Date: Thu, 22 Feb 2018 08:53:50 -0600 +Subject: [PATCH] don't run lintian checks to speed up build + +--- + pack/deb.mk | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pack/deb.mk b/pack/deb.mk +index de4a0b7..bddf9df 100644 +--- a/pack/deb.mk ++++ b/pack/deb.mk +@@ -130,7 +130,7 @@ $(BUILDDIR)/$(DPKG_CHANGES): $(BUILDDIR)/$(PRODUCT)-$(VERSION)/debian \ + @echo "Building Debian packages" + @echo "-------------------------------------------------------------------" + cd $(BUILDDIR)/$(PRODUCT)-$(VERSION) && \ +- debuild --preserve-envvar CCACHE_DIR --prepend-path=/usr/lib/ccache \ ++ debuild --no-lintian --preserve-envvar CCACHE_DIR --prepend-path=/usr/lib/ccache \ + -Z$(TARBALL_COMPRESSOR) -uc -us $(SMPFLAGS) + rm -rf $(BUILDDIR)/$(PRODUCT)-$(VERSION)/ + @echo "------------------------------------------------------------------" diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 49446a9bf..146399690 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -108,10 +108,10 @@ commonprep () { patch -p1 < utils/packpack/packpack-rpm.patch fi - # Patch packpack - patch --dry-run --silent -f -p1 < utils/packpack/envvars.patch + # Skip deb lintian checks to speed up the build + patch --dry-run --silent -f -p1 < utils/packpack/nolintian.patch if [ $? -eq 0 ]; then - patch -p1 < utils/packpack/envvars.patch + patch -p1 < utils/packpack/nolintian.patch fi # The rpm specfile requires we download the tarball and manually move it into place @@ -237,8 +237,6 @@ execpackpack () { parms="-f utils/packpack/redhat_package.mk redhat_package" else parms="" - export SMPFLAGS="-j4" - export USER_ENV_FILE="utils/packpack/environment" fi if [ "${TRAVIS}" == "true" ]; then From b19cb60540356fa1603abffbc24d0693665cc093 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Thu, 22 Feb 2018 09:16:59 -0600 Subject: [PATCH 024/538] update path in patch file (again) --- utils/packpack/nolintian.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/packpack/nolintian.patch b/utils/packpack/nolintian.patch index 3ef36451c..89b93a4c3 100644 --- a/utils/packpack/nolintian.patch +++ b/utils/packpack/nolintian.patch @@ -9,8 +9,8 @@ Subject: [PATCH] don't run lintian checks to speed up build diff --git a/pack/deb.mk b/pack/deb.mk index de4a0b7..bddf9df 100644 ---- a/pack/deb.mk -+++ b/pack/deb.mk +--- a/packpack/pack/deb.mk ++++ b/packpack/pack/deb.mk @@ -130,7 +130,7 @@ $(BUILDDIR)/$(DPKG_CHANGES): $(BUILDDIR)/$(PRODUCT)-$(VERSION)/debian \ @echo "Building Debian packages" @echo "-------------------------------------------------------------------" From 9480adcc317844572a290ab37df8806a155b87cc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 22 Feb 2018 13:05:53 -0500 Subject: [PATCH 025/538] include authentication info when building the link to mp4 --- web/includes/Event.php | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/web/includes/Event.php b/web/includes/Event.php index 6f92836ce..6915cf7c5 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -168,15 +168,22 @@ class Event { } else { $streamSrc .= $_SERVER['HTTP_HOST']; } - $streamSrc .= ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'}; - return $streamSrc; + $streamSrc .= ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php'; + $args['eid'] = $this->{'Id'}; + $args['view'] = 'view_video'; + } else { + $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; + + $args['source'] = 'event'; + $args['event'] = $this->{'Id'}; + if ( ( (!isset($args['mode'])) or ( $args['mode'] != 'single' ) ) && !empty($GLOBALS['connkey']) ) { + $args['connkey'] = $GLOBALS['connkey']; + } + if ( ZM_RAND_STREAM ) { + $args['rand'] = time(); + } } - $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; - - $args['source'] = 'event'; - $args['event'] = $this->{'Id'}; - if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $args['auth'] = generateAuthHash( ZM_AUTH_HASH_IPS ); @@ -187,16 +194,10 @@ class Event { $args['user'] = $_SESSION['username']; } } - if ( ( (!isset($args['mode'])) or ( $args['mode'] != 'single' ) ) && !empty($GLOBALS['connkey']) ) { - $args['connkey'] = $GLOBALS['connkey']; - } - if ( ZM_RAND_STREAM ) { - $args['rand'] = time(); - } $streamSrc .= '?'.http_build_query( $args,'', $querySep ); - return( $streamSrc ); + return $streamSrc; } // end function getStreamSrc function DiskSpace( $new='' ) { From 5b55bef721bb7ea19cdd7d8c3e10a6ef487b4af6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 22 Feb 2018 18:33:11 -0500 Subject: [PATCH 026/538] Add support for h264_mmal --- src/zm_ffmpeg_camera.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 576e83868..4c7d273c1 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -506,13 +506,20 @@ int FfmpegCamera::OpenFfmpeg() { #endif } // end if h264 #endif + if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) { + if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL ) { + Debug(1, "Failed to find decoder (h264_mmal)" ); + } else { + Debug(1, "Success finding decoder (h264_mmal)" ); + } + } if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) { // Try and get the codec from the codec context Error("Can't find codec for video stream from %s", mPath.c_str()); return -1; } else { - Debug(1, "Video Found decoder"); + Debug(1, "Video Found decoder %s", mVideoCodec->name); zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0); // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) From 834edafc634480cc1ac8145bc286e492a83baafc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 23 Feb 2018 07:46:24 -0500 Subject: [PATCH 027/538] install triggers as well --- db/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt index e1b551b1c..fda03b463 100644 --- a/db/CMakeLists.txt +++ b/db/CMakeLists.txt @@ -11,3 +11,6 @@ install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db # install zm_create.sql install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") + +# install triggers.sql +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") From 0a6b46330120d33808e618ce3562bac31deed673 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 23 Feb 2018 07:46:37 -0500 Subject: [PATCH 028/538] FIx MontageLayout=>MontageLayouts --- db/zm_create.sql.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 1740d8213..4adc9500e 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -1138,7 +1138,7 @@ CREATE TABLE Maps ( PRIMARY KEY (`Id`) ); -DROP TABLE IF EXISTS MontageLayout; +DROP TABLE IF EXISTS MontageLayouts; CREATE TABLE MontageLayouts ( `Id` int(10) unsigned NOT NULL auto_increment, From cece2f35f0f949cd1b445bf249261bae97909e6c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 23 Feb 2018 07:50:05 -0500 Subject: [PATCH 029/538] remove triggers from zm_create.sql.in as they are now contained in triggers.sql --- db/zm_create.sql.in | 272 -------------------------------------------- 1 file changed, 272 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 4adc9500e..5ec1c6fe9 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -228,37 +228,6 @@ CREATE TABLE `Events_Hour` ( KEY `Events_Hour_StartTime_idx` (`StartTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; -delimiter // -DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// -CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour -FOR EACH ROW BEGIN - UPDATE Monitors SET - HourEvents = COALESCE(HourEvents,1)-1, - HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) - WHERE Id=OLD.MonitorId; -END; -// - -DROP TRIGGER IF EXISTS Events_Hour_update_trigger// - -CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; - ELSE - UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; - END IF; - END IF; - END; -// -DELIMITER ; - DROP TABLE IF EXISTS `Events_Day`; CREATE TABLE `Events_Day` ( `EventId` int(10) unsigned NOT NULL, @@ -270,37 +239,6 @@ CREATE TABLE `Events_Day` ( KEY `Events_Day_StartTime_idx` (`StartTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; -delimiter // -DROP TRIGGER IF EXISTS Events_Day_delete_trigger// -CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day -FOR EACH ROW BEGIN - UPDATE Monitors SET - DayEvents = COALESCE(DayEvents,1)-1, - DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) - WHERE Id=OLD.MonitorId; -END; -// - -DROP TRIGGER IF EXISTS Events_Day_update_trigger; -CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; - ELSE - UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; - END IF; - END IF; - END; - // - - -DELIMITER ; DROP TABLE IF EXISTS `Events_Week`; CREATE TABLE `Events_Week` ( `EventId` int(10) unsigned NOT NULL, @@ -312,37 +250,6 @@ CREATE TABLE `Events_Week` ( KEY `Events_Week_StartTime_idx` (`StartTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; -delimiter // -DROP TRIGGER IF EXISTS Events_Week_delete_trigger// -CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week -FOR EACH ROW BEGIN - UPDATE Monitors SET - WeekEvents = COALESCE(WeekEvents,1)-1, - WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) - WHERE Id=OLD.MonitorId; -END; -// - -DROP TRIGGER IF EXISTS Events_Week_update_trigger; -CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId; - ELSE - UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; - END IF; - END IF; - END; - // - -DELIMITER ; - DROP TABLE IF EXISTS `Events_Month`; CREATE TABLE `Events_Month` ( `EventId` int(10) unsigned NOT NULL, @@ -354,38 +261,6 @@ CREATE TABLE `Events_Month` ( KEY `Events_Month_StartTime_idx` (`StartTime`) ) ENGINE=@ZM_MYSQL_ENGINE@; -delimiter // -DROP TRIGGER IF EXISTS Events_Month_delete_trigger// -CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month -FOR EACH ROW BEGIN - UPDATE Monitors SET - MonthEvents = COALESCE(MonthEvents,1)-1, - MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) - WHERE Id=OLD.MonitorId; -END; -// - - -DROP TRIGGER IF EXISTS Events_Month_update_trigger; -CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month -FOR EACH ROW - BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( diff ) THEN - IF ( NEW.MonitorID != OLD.MonitorID ) THEN - UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId; - UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId; - ELSE - UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId; - END IF; - END IF; - END; - // - - -DELIMITER ; DROP TABLE IF EXISTS `Events_Archived`; CREATE TABLE `Events_Archived` ( @@ -396,135 +271,6 @@ CREATE TABLE `Events_Archived` ( KEY `Events_Archived_MonitorId_idx` (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; - -drop procedure if exists update_storage_stats; - -delimiter // - -create procedure update_storage_stats(IN StorageId smallint(5), IN space BIGINT) - -sql security invoker - -deterministic - -begin - - update Storage set DiskSpace = COALESCE(DiskSpace,0) + COALESCE(space,0) where Id = StorageId; - -end; - -// - -drop trigger if exists event_update_trigger// - -CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events -FOR EACH ROW -BEGIN - declare diff BIGINT default 0; - - set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); - IF ( NEW.StorageId = OLD.StorageID ) THEN - IF ( diff ) THEN - call update_storage_stats(OLD.StorageId, diff); - END IF; - ELSE - IF ( NEW.DiskSpace ) THEN - call update_storage_stats(NEW.StorageId, NEW.DiskSpace); - END IF; - IF ( OLD.DiskSpace ) THEN - call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); - END IF; - END IF; - - UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - - IF ( NEW.Archived != OLD.Archived ) THEN - IF ( NEW.Archived ) THEN - INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace); - UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId; - ELSEIF ( OLD.Archived ) THEN - DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId; - ELSE - IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN - UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - UPDATE Monitors SET - ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) - WHERE Id=OLD.MonitorId; - END IF; - END IF; - ELSE IF ( NEW.Archived AND diff ) THEN - UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; - END IF; - - IF ( diff ) THEN - UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId; - END IF; - -END; - -// - -delimiter ; - -DROP TRIGGER IF EXISTS event_insert_trigger; - -delimiter // -/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count. - * The DiskSpace will get update in the Event Update Trigger - */ -CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events -FOR EACH ROW - BEGIN - - INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); - INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); - INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); - INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); - UPDATE Monitors SET - HourEvents = COALESCE(DayEvents,0)+1, - DayEvents = COALESCE(DayEvents,0)+1, - WeekEvents = COALESCE(DayEvents,0)+1, - MonthEvents = COALESCE(DayEvents,0)+1, - TotalEvents = COALESCE(TotalEvents,0)+1 - WHERE Id=NEW.MonitorId; -END; -// - -DROP TRIGGER IF EXISTS event_delete_trigger// - -CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events -FOR EACH ROW -BEGIN - IF ( OLD.DiskSpace ) THEN - call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); - END IF; - DELETE FROM Events_Hour WHERE EventId=OLD.Id; - DELETE FROM Events_Day WHERE EventId=OLD.Id; - DELETE FROM Events_Week WHERE EventId=OLD.Id; - DELETE FROM Events_Month WHERE EventId=OLD.Id; - IF ( OLD.Archived ) THEN - DELETE FROM Events_Archived WHERE EventId=OLD.Id; - UPDATE Monitors SET - ArchivedEvents = COALESCE(ArchivedEvents,1) - 1, - ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0), - TotalEvents = COALESCE(TotalEvents,1) - 1, - TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) - WHERE Id=OLD.MonitorId; - ELSE - UPDATE Monitors SET - TotalEvents = COALESCE(TotalEvents,1)-1, - TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) - WHERE Id=OLD.MonitorId; - END IF; -END; - -// - -delimiter ; -- -- Table structure for table `Filters` -- @@ -949,24 +695,6 @@ CREATE TABLE `Zones` ( KEY `MonitorId` (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; -DELIMITER // -DROP TRIGGER IF EXISTS Zone_Insert_Trigger// -CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones -FOR EACH ROW - BEGIN - UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID; - END -// -DROP TRIGGER IF EXISTS Zone_Delete_Trigger// -CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones -FOR EACH ROW - BEGIN - UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID; - END -// - -DELIMITER ; - DROP TABLE IF EXISTS `Storage`; CREATE TABLE `Storage` ( `Id` smallint(5) unsigned NOT NULL auto_increment, From a888dac8341a5479a3f60c73c5ab820d5df5d917 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 23 Feb 2018 09:30:47 -0500 Subject: [PATCH 030/538] source files in /etc/zm/conf.d as well --- distros/debian/postinst | 3 +++ distros/ubuntu1204/zoneminder.postinst | 4 ++++ distros/ubuntu1604/zoneminder.postinst | 3 +++ 3 files changed, 10 insertions(+) diff --git a/distros/debian/postinst b/distros/debian/postinst index 20f715793..3cd3fd277 100644 --- a/distros/debian/postinst +++ b/distros/debian/postinst @@ -5,6 +5,9 @@ set -e if [ "$1" = "configure" ]; then . /etc/zm/zm.conf + for i in /etc/zm/conf.d/*.conf; do + . $i + done; # 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 diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst index 06b976037..ef715375b 100644 --- a/distros/ubuntu1204/zoneminder.postinst +++ b/distros/ubuntu1204/zoneminder.postinst @@ -5,6 +5,10 @@ set -e if [ "$1" = "configure" ]; then . /etc/zm/zm.conf + for i in /etc/zm/conf.d/*.conf; do + . $i + done; + # 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 diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index 3d8e09a1c..d69619b54 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -5,6 +5,9 @@ set -e if [ "$1" = "configure" ]; then . /etc/zm/zm.conf + for i in /etc/zm/conf.d/*.conf; do + . $i + done; # 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 From ab11e9ec38d562c3227356ac3c35008294ce37b2 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 23 Feb 2018 11:48:35 -0600 Subject: [PATCH 031/538] Update zoneminder.spec --- distros/redhat/zoneminder.spec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 10ce1ff0a..0226d777f 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -83,6 +83,10 @@ BuildRequires: libcurl-devel BuildRequires: libv4l-devel BuildRequires: ffmpeg-devel +# Required for mp4 container support +BuildRequires: libmp4v2-devel +BuildRequires: x264-devel + %{?with_nginx:Requires: nginx} %{?with_nginx:Requires: fcgiwrap} %{?with_nginx:Requires: php-fpm} From a231f500eca74df1d1ffc4f98926d669095c9b35 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 23 Feb 2018 19:01:42 -0500 Subject: [PATCH 032/538] better debug, whitespace --- src/zm_monitor.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index dc097d1ab..99cafd150 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -570,19 +570,19 @@ bool Monitor::connect() { Debug(3,"Aligning shared memory images to the next 64 byte boundary"); shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64))); } - Debug(3, "Allocating %d image buffers", image_buffer_count ); - image_buffer = new Snapshot[image_buffer_count]; - for ( int i = 0; i < image_buffer_count; i++ ) { - image_buffer[i].timestamp = &(shared_timestamps[i]); - image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); - image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ - } - if ( (deinterlacing & 0xff) == 4) { - /* Four field motion adaptive deinterlacing in use */ - /* Allocate a buffer for the next image */ - next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); - next_buffer.timestamp = new struct timeval; - } + Debug(3, "Allocating %d image buffers", image_buffer_count ); + image_buffer = new Snapshot[image_buffer_count]; + for ( int i = 0; i < image_buffer_count; i++ ) { + image_buffer[i].timestamp = &(shared_timestamps[i]); + image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); + image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ + } + if ( (deinterlacing & 0xff) == 4) { + /* Four field motion adaptive deinterlacing in use */ + /* Allocate a buffer for the next image */ + next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); + next_buffer.timestamp = new struct timeval; + } if ( ( purpose == ANALYSIS ) && analysis_fps ) { // Size of pre event buffer must be greater than pre_event_count // if alarm_frame_count > 1, because in this case the buffer contains @@ -802,7 +802,7 @@ double Monitor::GetFPS() const { Snapshot *snap1 = &image_buffer[index1]; if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) { // This should be impossible - Warning("Impossible situation. No timestamp on captured image"); + Warning("Impossible situation. No timestamp on captured image index was %d, image-buffer_count was (%d)", index1, image_buffer_count); return 0.0; } struct timeval time1 = *snap1->timestamp; From 464b588f0873041fa52d4f6a02b6fdb4284296ac Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 07:29:49 -0800 Subject: [PATCH 033/538] add a case for toggle, which are booleans and default them to false --- web/includes/functions.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/web/includes/functions.php b/web/includes/functions.php index b85624335..4e94b2f7e 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -641,6 +641,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { if ( !isset($types[$key]) ) $types[$key] = false; + switch( $types[$key] ) { case 'set' : { @@ -702,6 +703,16 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { } break; } + case 'toggle' : + if ( (!isset($values[$key])) or $values[$key] != $value ) { + if ( empty($value) ) { + $changes[$key] = "$key = 0"; + } else { + $changes[$key] = "$key = 0"; + //$changes[$key] = $key . ' = '.dbEscape(trim($value)); + } + } + break; default : { if ( !isset($values[$key]) || ($values[$key] != $value) ) { From c6f632325e459005b7b37c8f49cbe62254e54175 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 07:31:22 -0800 Subject: [PATCH 034/538] create an object for Controls entries, with appropriate defaults --- web/includes/Control.php | 228 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 web/includes/Control.php diff --git a/web/includes/Control.php b/web/includes/Control.php new file mode 100644 index 000000000..343a7b31a --- /dev/null +++ b/web/includes/Control.php @@ -0,0 +1,228 @@ + 0, + 'CanMoveDiag' => 0, + 'CanMoveMap' => 0, + 'CanMoveAbs' => 0, + 'CanMoveRel' => 0, + 'CanMoveCon' => 0, + 'CanPan' => 0, + 'MinPanRange' => NULL, + 'MaxPanRange' => NULL, + 'MinPanStep' => NULL, + 'MaxPanStep' => NULL, + 'HasPanSpeed' => 0, + 'MinPanSpeed' => NULL, + 'MaxPanSpeed' => NULL, + 'HasTurboPan' => 0, + 'TurboPanSpeed' => NULL, + 'CanTilt' => 0, + 'MinTiltRange' => NULL, + 'MaxTiltRange' => NULL, + 'MinTiltStep' => NULL, + 'MaxTiltStep' => NULL, + 'HasTiltSpeed' => 0, + 'MinTiltSpeed' => NULL, + 'MaxTiltSpeed' => NULL, + 'HasTurboTilt' => 0, + 'TurboTiltSpeed' => NULL, + 'CanZoom' => 0, + 'CanZoomAbs' => 0, + 'CanZoomRel' => 0, + 'CanZoomCon' => 0, + 'MinZoomRange' => NULL, + 'MaxZoomRange' => NULL, + 'MinZoomStep' => NULL, + 'MaxZoomStep' => NULL, + 'HasZoomSpeed' => 0, + 'MinZoomSpeed' => NULL, + 'MaxZoomSpeed' => NULL, + 'CanFocus' => 0, + 'CanAutoFocus' => 0, + 'CanFocusAbs' => 0, + 'CanFocusRel' => 0, + 'CanFocusCon' => 0, + 'MinFocusRange' => NULL, + 'MaxFocusRange' => NULL, + 'MinFocusStep' => NULL, + 'MaxFocusStep' => NULL, + 'HasFocusSpeed' => 0, + 'MinFocusSpeed' => NULL, + 'MaxFocusSpeed' => NULL, + 'CanIris' => 0, + 'CanAutoIris' => 0, + 'CanIrisAbs' => 0, + 'CanIrisRel' => 0, + 'CanIrisCon' => 0, + 'MinIrisRange' => NULL, + 'MaxIrisRange' => NULL, + 'MinIrisStep' => NULL, + 'MaxIrisStep' => NULL, + 'HasIrisSpeed' => 0, + 'MinIrisSpeed' => NULL, + 'MaxIrisSpeed' => NULL, + 'CanGain' => 0, + 'CanAutoGain' => 0, + 'CanGainAbs' => 0, + 'CanGainRel' => 0, + 'CanGainCon' => 0, + 'MinGainRange' => NULL, + 'MaxGainRange' => NULL, + 'MinGainStep' => NULL, + 'MaxGainStep' => NULL, + 'HasGainSpeed' => 0, + 'MinGainSpeed' => NULL, + 'MaxGainSpeed' => NULL, + 'CanWhite' => 0, + 'CanAutoWhite' => 0, + 'CanWhiteAbs' => 0, + 'CanWhiteRel' => 0, + 'CanWhiteCon' => 0, + 'MinWhiteRange' => NULL, + 'MaxWhiteRange' => NULL, + 'MinWhiteStep' => NULL, + 'MaxWhiteStep' => NULL, + 'HasWhiteSpeed' => 0, + 'MinWhiteSpeed' => NULL, + 'MaxWhiteSpeed' => NULL, + 'HasPresets' => 0, + 'NumPresets' => 0, + 'HasHomePreset' => 0, + 'CanSetPresets' => 0, + 'Name' => 'New', + 'Type' => 'Local', + 'Protocol' => NULL + ); + + public function __construct( $IdOrRow = NULL ) { + if ( $IdOrRow ) { + $row = NULL; + if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { + $row = dbFetchOne( 'SELECT * FROM Control WHERE Id=?', NULL, array( $IdOrRow ) ); + if ( ! $row ) { + Error("Unable to load Control record for Id=" . $IdOrRow ); + } + } elseif ( is_array( $IdOrRow ) ) { + $row = $IdOrRow; + } else { + Error("Unknown argument passed to Control Constructor ($IdOrRow)"); + return; + } + + if ( $row ) { + foreach ($row as $k => $v) { + $this->{$k} = $v; + } + } else { + Error('No row for Control ' . $IdOrRow ); + } + } # end if isset($IdOrRow) + } // end function __construct + + public function __call($fn, array $args){ + if ( count($args) ) { + $this->{$fn} = $args[0]; + } + if ( array_key_exists($fn, $this) ) { + return $this->{$fn}; + #array_unshift($args, $this); + #call_user_func_array( $this->{$fn}, $args); + } else { + if ( array_key_exists($fn, $this->control_fields) ) { + return $this->control_fields{$fn}; + } else if ( array_key_exists( $fn, $this->defaults ) ) { + return $this->defaults{$fn}; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Warning( "Unknown function call Control->$fn from $file:$line" ); + } + } + } + + public function set( $data ) { + foreach ($data as $k => $v) { + if ( is_array( $v ) ) { + # perhaps should turn into a comma-separated string + $this->{$k} = implode(',',$v); + } else if ( is_string( $v ) ) { + $this->{$k} = trim( $v ); + } else if ( is_integer( $v ) ) { + $this->{$k} = $v; + } else if ( is_bool( $v ) ) { + $this->{$k} = $v; + } else { + Error( "Unknown type $k => $v of var " . gettype( $v ) ); + $this->{$k} = $v; + } + } + } + public static function find_all( $parameters = null, $options = null ) { + $filters = array(); + $sql = 'SELECT * FROM Controls '; + $values = array(); + + if ( $parameters ) { + $fields = array(); + $sql .= 'WHERE '; + foreach ( $parameters as $field => $value ) { + if ( $value == null ) { + $fields[] = $field.' IS NULL'; + } else if ( is_array( $value ) ) { + $func = function(){return '?';}; + $fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')'; + $values += $value; + + } else { + $fields[] = $field.'=?'; + $values[] = $value; + } + } + $sql .= implode(' AND ', $fields ); + } + if ( $options and isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + $result = dbQuery($sql, $values); + $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Control'); + foreach ( $results as $row => $obj ) { + $filters[] = $obj; + } + return $filters; + } + + public function save( $new_values = null ) { + + if ( $new_values ) { + foreach ( $new_values as $k=>$v ) { + $this->{$k} = $v; + } + } + foreach ( $this->defaults as $k=>$v ) { + if ( ( ! array_key_exists( $k, $this ) ) or ( $this->{$k} == '' ) ) { + $this->{$k} = $v; + } + } + + $fields = array_keys( $this->defaults ); + +if ( array_key_exists( 'Id', $this ) ) { + $sql = 'UPDATE Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?'; + $values = array_map( function($field){return $this->{$field};}, $fields ); + $values[] = $this->{'Id'}; + dbQuery( $sql, $values ); +} else { + $sql = 'INSERT INTO Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ''; + $values = array_map( function($field){return $this->{$field};}, $fields ); + dbQuery( $sql, $values ); +} + } // end function save + +} // end class Control +?> From bfbefc4c32888b188a390d38e26868222fd211b8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 07:31:48 -0800 Subject: [PATCH 035/538] Use our new Control object to do the saving instead of a generic function --- web/includes/actions.php | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/web/includes/actions.php b/web/includes/actions.php index ad9e03ce5..e01801e55 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -249,34 +249,12 @@ if ( !empty($action) ) { // Control capability actions, require control edit permissions if ( canEdit( 'Control' ) ) { if ( $action == 'controlcap' ) { - if ( !empty($_REQUEST['cid']) ) { - $control = dbFetchOne( 'SELECT * FROM Controls WHERE Id = ?', NULL, array($_REQUEST['cid']) ); - } else { - $control = array(); - } + require_once( 'Control.php' ); + $Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null ); - // Define a field type for anything that's not simple text equivalent - $types = array( - // Empty - ); - - $columns = getTableColumns( 'Controls' ); - foreach ( $columns as $name=>$type ) { - if ( preg_match( '/^(Can|Has)/', $name ) ) { - $types[$name] = 'toggle'; - } - } - $changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); - - if ( count( $changes ) ) { - if ( !empty($_REQUEST['cid']) ) { - dbQuery( "update Controls set ".implode( ", ", $changes )." where Id = ?", array($_REQUEST['cid']) ); - } else { - dbQuery( "insert into Controls set ".implode( ", ", $changes ) ); - //$_REQUEST['cid'] = dbInsertId(); - } - $refreshParent = true; - } + //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); + $Control->save( $_REQUEST['newControl'] ); + $refreshParent = true; $view = 'none'; } elseif ( $action == 'delete' ) { if ( isset($_REQUEST['markCids']) ) { From 79037f56c13debdd81c69ac4ef1c2d35e2ac1019 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Mon, 26 Feb 2018 10:41:29 -0600 Subject: [PATCH 036/538] buildsystem - fix 32bit rpm builds --- utils/packpack/setarch.patch | 57 +++++++++++++++++++++++++++++++++ utils/packpack/startpackpack.sh | 6 ++++ 2 files changed, 63 insertions(+) create mode 100644 utils/packpack/setarch.patch diff --git a/utils/packpack/setarch.patch b/utils/packpack/setarch.patch new file mode 100644 index 000000000..4f196ec86 --- /dev/null +++ b/utils/packpack/setarch.patch @@ -0,0 +1,57 @@ +From 62a98b36fd62d328956503bc9427ae128bb811af Mon Sep 17 00:00:00 2001 +From: Andrew Bauer +Date: Mon, 26 Feb 2018 10:05:02 -0600 +Subject: [PATCH] fix 32bit rpm builds + +--- + pack/rpm.mk | 2 +- + packpack | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/packpack/pack/rpm.mk b/packpack/pack/rpm.mk +index c74e942..9a6b016 100644 +--- a/packpack/pack/rpm.mk ++++ b/packpack/pack/rpm.mk +@@ -124,7 +124,7 @@ package: $(BUILDDIR)/$(RPMSRC) + @echo "-------------------------------------------------------------------" + @echo "Building RPM packages" + @echo "-------------------------------------------------------------------" +- rpmbuild \ ++ setarch $(ARCH) rpmbuild \ + --define '_topdir $(BUILDDIR)' \ + --define '_sourcedir $(BUILDDIR)' \ + --define '_specdir $(BUILDDIR)' \ +diff --git a/packpack/packpack b/packpack/packpack +index 6f4c80f..c329399 100755 +--- a/packpack/packpack ++++ b/packpack/packpack +@@ -125,7 +125,7 @@ chmod a+x ${BUILDDIR}/userwrapper.sh + # + # Save defined configuration variables to ./env file + # +-env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=" \ ++env | grep -E "^PRODUCT=|^VERSION=|^RELEASE=|^ABBREV=|^TARBALL_|^CHANGELOG_|^CCACHE_|^PACKAGECLOUD_|^SMPFLAGS=|^OS=|^DIST=|^ARCH=" \ + > ${BUILDDIR}/env + + # +diff --git a/packpack/packpack b/packpack/packpack +index c329399..6ffaa9c 100755 +--- a/packpack/packpack ++++ b/packpack/packpack +@@ -19,11 +19,11 @@ DOCKER_REPO=${DOCKER_REPO:-packpack/packpack} + if [ -z "${ARCH}" ]; then + # Use uname -m instead of HOSTTYPE + case "$(uname -m)" in +- i*86) ARCH="i386" ;; +- arm*) ARCH="armhf" ;; +- x86_64) ARCH="x86_64"; ;; +- aarch64) ARCH="aarch64" ;; +- *) ARCH="${HOSTTYPE}" ;; ++ i*86) export ARCH="i386" ;; ++ arm*) export ARCH="armhf" ;; ++ x86_64) export ARCH="x86_64"; ;; ++ aarch64) export ARCH="aarch64" ;; ++ *) export ARCH="${HOSTTYPE}" ;; + esac + fi + diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 146399690..6e1cff817 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -114,6 +114,12 @@ commonprep () { patch -p1 < utils/packpack/nolintian.patch fi + # fix 32bit rpm builds + patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch + if [ $? -eq 0 ]; then + patch -p1 < utils/packpack/setarch.patch + fi + # The rpm specfile requires we download the tarball and manually move it into place # Might as well do this for Debian as well, rather than git submodule init CRUDVER="3.0.10" From 9d1a8b1e18ed5ce426b50cb9bd03358e78927e90 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 10:39:45 -0800 Subject: [PATCH 037/538] Add the danger css tag to cpuload > 5, freeram < 10% and freeswap < 10% --- web/skins/classic/views/options.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 2a38dbbd4..4375081c4 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -223,11 +223,17 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI - + - - - + + + From 92c34f60435b640337782e6fe7f99f6048ef28d3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 16:09:36 -0800 Subject: [PATCH 038/538] add Groups to monitor edit --- web/skins/classic/views/monitor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index e057732b2..d4758ae66 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -726,6 +726,9 @@ switch ( $tab ) { + Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) { From 944298428a1815c7f9f02fd55292fa626f273b6f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 16:10:10 -0800 Subject: [PATCH 039/538] break out get_groups_dropdown to call a function called get_dropdown_options to populate thje options --- web/includes/Group.php | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/web/includes/Group.php b/web/includes/Group.php index ee86a320c..f8836ebca 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -135,7 +135,7 @@ public $defaults = array( return $this->{'MonitorIds'}; } - public static function get_group_dropdown() { + public static function get_group_dropdown( ) { session_start(); $selected_group_id = 0; @@ -148,13 +148,23 @@ public $defaults = array( } session_write_close(); + return htmlSelect( 'Group[]', Group::get_dropdown_options(), isset($_SESSION['Group'])?$_SESSION['Group']:null, array( + 'onchange' => 'this.form.submit();', + 'class'=>'chosen', + 'multiple'=>'multiple', + 'data-placeholder'=>'All', + ) ); + + } # end public static function get_group_dropdown + + public static function get_dropdown_options() { $Groups = array(); foreach ( Group::find_all( ) as $Group ) { $Groups[$Group->Id()] = $Group; } # This array is indexed by parent_id -global $children; + global $children; $children = array(); foreach ( $Groups as $id=>$Group ) { @@ -181,16 +191,10 @@ global $children; $group_options += get_options( $Group ); } } - return htmlSelect( 'Group[]', $group_options, isset($_SESSION['Group'])?$_SESSION['Group']:null, array( - 'onchange' => 'this.form.submit();', - 'class'=>'chosen', - 'multiple'=>'multiple', - 'data-placeholder'=>'All', - ) ); + return $group_options; + } - } # end public static function get_group_dropdown - - public static function get_group_dropdowns() { + public static function get_group_dropdowns( $selected = null ) { # This will end up with the group_id of the deepest selection $group_id = 0; $depth = 0; @@ -205,6 +209,7 @@ global $children; break; $parent_group_ids = array(); +if ( ! $selected ) { $selected_group_id = 0; if ( isset($_REQUEST['group'.$depth]) ) { $selected_group_id = $group_id = $_SESSION['group'.$depth] = $_REQUEST['group'.$depth]; @@ -213,6 +218,9 @@ global $children; } else if ( isset($_REQUEST['filtering']) ) { unset($_SESSION['group'.$depth]); } +} else { + $selected_group_id = $selected; +} foreach ( $Groups as $Group ) { if ( ! isset( $groups[$depth] ) ) { From 902f5f098a04ec1f3470aed7408fb5376d6efdbe Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 16:10:27 -0800 Subject: [PATCH 040/538] remove debug code --- src/zm_ffmpeg_camera.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 4c7d273c1..c43bb3389 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -171,7 +171,6 @@ int FfmpegCamera::PrimeCapture() { } int FfmpegCamera::PreCapture() { - Debug(1, "PreCapture"); // If Reopen was called, then ffmpeg is closed and we need to reopen it. if ( ! mCanCapture ) return OpenFfmpeg(); From 3586825c908c3139ff0fc4aeec93c94a030cebe2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 17:08:05 -0800 Subject: [PATCH 041/538] fix preevent_count==0 causing div/0 --- src/zm_monitor.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index e149a78f6..58e758577 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1202,19 +1202,20 @@ bool Monitor::Analyse() { gettimeofday( &now, NULL ); if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) { + if ( now.tv_sec != last_fps_time ) { + double new_fps = double(fps_report_interval)/(now.tv_sec - last_fps_time); + Info("%s: %d - Analysing at %.2f fps", name, image_count, new_fps); + if ( fps != new_fps ) { + fps = new_fps; + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + } + } // end if fps != new_fps - double new_fps = double(fps_report_interval)/(now.tv_sec - last_fps_time); - Info("%s: %d - Analysing at %.2f fps", name, image_count, new_fps); - if ( fps != new_fps ) { - fps = new_fps; - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - } // end if fps != new_fps - - last_fps_time = now.tv_sec; + last_fps_time = now.tv_sec; + } } int index; @@ -1690,7 +1691,7 @@ bool Monitor::Analyse() { //shared_data->last_read_time = image_buffer[index].timestamp->tv_sec; shared_data->last_read_time = now.tv_sec; - if ( analysis_fps ) { + if ( analysis_fps && pre_event_buffer_count ) { // If analysis fps is set, add analysed image to dedicated pre event buffer int pre_index = image_count%pre_event_buffer_count; pre_event_buffer[pre_index].image->Assign(*snap->image); From 735e36c2a8ee07af755c93c1db875d15ab410108 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 17:08:30 -0800 Subject: [PATCH 042/538] split htmlSelect into htmlOptions --- web/includes/functions.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index 0f003801e..b2b08cca1 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -553,20 +553,19 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) { } } - $html = "".htmlOptions( $contents, $values ).''; +} + +function htmlOptions( $contents, $values ) { + $html = ''; foreach ( $contents as $value=>$text ) { if ( is_array( $text ) ) $text = $text['Name']; else if ( is_object( $text ) ) $text = $text->Name(); - //for ( $i = 0; $i < count($contents); $i +=2 ) { - //$value = $contents[$i]; - //$text = $contents[$i+1]; $selected = is_array( $values ) ? in_array( $value, $values ) : !strcmp($value, $values); - //Warning("Selected is $selected from $value and $values"); $html .= ""; } - $html .= ''; return $html; } From 793e87c26d109ff84421c3f9dd303f9be0794896 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 17:57:50 -0800 Subject: [PATCH 043/538] Apply chosen style to group and linked monitors dropdown --- web/skins/classic/views/monitor.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index d4758ae66..67d0a8413 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -708,7 +708,7 @@ switch ( $tab ) { - LinkedMonitors() ) @@ -1018,4 +1018,7 @@ if ( $monitor->Type() == 'Local' ) { + From 6a6eb8768633f557b4e638e7aac32f9a6238f59f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 26 Feb 2018 18:00:02 -0800 Subject: [PATCH 044/538] fix defaults --- web/skins/classic/views/monitor.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 67d0a8413..2815b2da8 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -89,12 +89,12 @@ if ( ! $monitor ) { 'Pass' => '', 'Colours' => 4, 'Palette' => 0, - 'Width' => '1280', - 'Height' => '962', + 'Width' => '', + 'Height' => '', 'Orientation' => '0', 'Deinterlacing' => 0, 'RTSPDescribe' => 0, - 'SaveJPEGs' => '4', + 'SaveJPEGs' => '0', 'VideoWriter' => '1', 'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n", 'RecordAudio' => '0', @@ -102,9 +102,9 @@ if ( ! $monitor ) { 'LabelX' => 0, 'LabelY' => 0, 'LabelSize' => 1, - 'ImageBufferCount' => 40, - 'WarmupCount' => 1, - 'PreEventCount' => 1, + 'ImageBufferCount' => 20, + 'WarmupCount' => 0, + 'PreEventCount' => 0, 'PostEventCount' => 5, 'StreamReplayBuffer' => 0, 'AlarmFrameCount' => 1, From 67cc7dcf7c5c89945be503a2ca814a40090f1cc5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 11:19:20 -0800 Subject: [PATCH 045/538] Add a GroupIds function to return the groups --- web/includes/Monitor.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 6fb19c1ed..46de04aaa 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -392,5 +392,15 @@ Logger::Debug("sending command to $url"); } } // end if we are on the recording server } + public function GroupIds( ) { + if ( !array_key_exists('GroupIds', $this) ) { + if ( array_key_exists('Id', $this) and $this->{'Id'} ) { + $this->{'GroupIds'} = dbFetchAll( 'SELECT GroupId FROM Groups_Monitors WHERE MonitorId=?', 'GroupId', array($this->{'Id'}) ); + } else { + $this0->{'GroupIds'} = array(); + } + } + return $this->{'GroupIds'}; + } } // end class Monitor ?> From 0312dc3c2d2bae560b1a3316de439bc5b12df140 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 11:19:44 -0800 Subject: [PATCH 046/538] Add Group saving to Monitor saving --- web/includes/actions.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/web/includes/actions.php b/web/includes/actions.php index 3676a88b9..a5d615caf 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -471,7 +471,7 @@ if ( canEdit( 'Monitors' ) ) { $mid = 0; if ( !empty($_REQUEST['mid']) ) { $mid = validInt($_REQUEST['mid']); - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid) ); + $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); if ( ZM_OPT_X10 ) { $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) ); @@ -484,6 +484,7 @@ if ( canEdit( 'Monitors' ) ) { $x10Monitor = array(); } } + $Monitor = new Monitor( $monitor ); // Define a field type for anything that's not simple text equivalent $types = array( @@ -519,6 +520,7 @@ if ( canEdit( 'Monitors' ) ) { zmaControl( $monitor, 'stop' ); zmcControl( $monitor, 'stop' ); dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); + // Groups will be added below if ( isset($changes['Name']) or isset($changes['StorageId']) ) { $OldStorage = new Storage( $monitor['StorageId'] ); $saferOldName = basename( $monitor['Name'] ); @@ -578,16 +580,25 @@ if ( canEdit( 'Monitors' ) ) { mkdir( $Storage->Path().'/'.$mid, 0755 ); $saferName = basename($_REQUEST['newMonitor']['Name']); symlink( $mid, $Storage->Path().'/'.$saferName ); - if ( isset($_COOKIE['zmGroup']) ) { - dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) ); - } + } else { Error("Error saving new Monitor."); return; } } else { Error("Users with Monitors restrictions cannot create new monitors."); + return; } + if ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) or array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds() ) ) { + if ( $Monitor->Id() ) + dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', null, array($Mid)); + + if ( isset($_POST['newMonitor']['GroupIds']) ) { + foreach ( $_POST['newMonitor']['GroupIds'] as $group_id ) { + dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) ); + } + } + } // end if there has been a change of groups $restart = true; } # end if count(changes) From c50627fd882923c5c1e29059473447110f112b8c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 11:20:03 -0800 Subject: [PATCH 047/538] GroupIds needs to be an array --- web/skins/classic/views/monitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 2815b2da8..17f6eb584 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -726,7 +726,7 @@ switch ( $tab ) { -GroupIds() ); ?> From 83fad681b306feb94981e9819031823a1bfdf5b8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 11:49:13 -0800 Subject: [PATCH 048/538] use button tags and hide the button content after click --- web/skins/classic/views/monitor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 17f6eb584..d2d416852 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -1011,8 +1011,8 @@ if ( $monitor->Type() == 'Local' ) {
- disabled="disabled"/> - + +
From c5eec7e2fd4b35fa866daca923044186dea93f3d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 15:10:20 -0800 Subject: [PATCH 049/538] hide save button on successful form completion so we don't get a duplicate submit --- web/skins/classic/views/monitor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index d2d416852..98d68f62b 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -519,7 +519,7 @@ foreach ( $tabs as $name=>$value ) { ?>
-
+ @@ -1011,7 +1011,7 @@ if ( $monitor->Type() == 'Local' ) {
- +
From 5ac003d448f6671590e0b235125cfc48792a9a5d Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 27 Feb 2018 17:39:58 -0600 Subject: [PATCH 050/538] Update packpack.rst --- docs/installationguide/packpack.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst index 8e52ad876..377af68f5 100644 --- a/docs/installationguide/packpack.rst +++ b/docs/installationguide/packpack.rst @@ -1,4 +1,4 @@ -All Distros - A Simpler Way to Build ZoneMinder +All Distros - A Docker Way to Build ZoneMinder =============================================== .. contents:: @@ -27,6 +27,8 @@ Procedure - If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here. +- If the desired distro architecture is arm, refer to Appendix A to enable qemu emulation on your amd64 host machine. + **Step 2:** Install Docker. You need to have a working installation of Docker so head over to the `Docker site `_ and get it working. Before continuing to the next step, verify you can run the Docker "Hello World" container as a normal user. To run a Docker container as a normal user, issue the following: @@ -99,7 +101,27 @@ For advanced users who really want to go out into uncharted waters, it is theore Building arm packages in this manner has not been tested by us, however. +Appendix A - Enable Qemu On the Host +------------------------------------ +If you intend to build ZoneMinder packages for arm on an amd64 host, then Debian users can following these steps to enable transparent Qemu emulation: +:: + sudo apt-get install binfmt-support qemu qemu-user-static +Verify arm emulation is enabled by issuing: + +:: + + sudo update-binfmts --enable qemu-arm + +You may get an message stating emulation for this processor is already enabled. + +More testing needs to be done for Redhat distros but it appears Fedora users can just run: + +:: + + sudo systemctl start systemd-binfmt + +TO-DO: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome. From 35c63ac0bce00fbafdceab569c358142c7ecf0e8 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 27 Feb 2018 17:43:36 -0600 Subject: [PATCH 051/538] Update packpack.rst --- docs/installationguide/packpack.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst index 377af68f5..6f4355839 100644 --- a/docs/installationguide/packpack.rst +++ b/docs/installationguide/packpack.rst @@ -27,7 +27,7 @@ Procedure - If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here. -- If the desired distro architecture is arm, refer to Appendix A to enable qemu emulation on your amd64 host machine. +- If the desired distro architecture is arm, refer to `Appendix A - Enable Qemu On the Host`_ to enable qemu emulation on your amd64 host machine. **Step 2:** Install Docker. From f00983b450a8b7f8667f75f65bcf6316a3f39e72 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 27 Feb 2018 20:19:39 -0500 Subject: [PATCH 052/538] Don't setup snap and timestamp if it's an audio packet --- src/zm_monitor.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index e6519acd4..5ce02bbd7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1273,17 +1273,19 @@ bool Monitor::Analyse() { Debug(2, "Analysis index (%d), last_Write(%d)", index, shared_data->last_write_index); packets_processed += 1; - struct timeval *timestamp = snap->timestamp; - Image *snap_image = snap->image; if ( snap->image_index == -1 ) { snap->unlock(); Debug(2, "skipping because audio"); + // We want to skip, but if we return, we may sleep. + // if ( ! packetqueue->increment_analysis_it() ) { Debug(2, "No more packets to analyse"); return false; } continue; } + struct timeval *timestamp = snap->timestamp; + Image *snap_image = snap->image; // signal is set by capture bool signal = shared_data->signal; @@ -2831,6 +2833,7 @@ int Monitor::Capture() { return 0; } } else { + Debug(4, "Capturing"); captureResult = camera->Capture(*packet); gettimeofday( packet->timestamp, NULL ); if ( captureResult < 0 ) { From 7fed5fd596d5a0af6232d4e3ec6a8861f0d52343 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Tue, 27 Feb 2018 19:46:44 -0600 Subject: [PATCH 053/538] spelling --- docs/installationguide/packpack.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst index 6f4355839..dd54ef4b2 100644 --- a/docs/installationguide/packpack.rst +++ b/docs/installationguide/packpack.rst @@ -116,7 +116,7 @@ Verify arm emulation is enabled by issuing: sudo update-binfmts --enable qemu-arm -You may get an message stating emulation for this processor is already enabled. +You may get a message stating emulation for this processor is already enabled. More testing needs to be done for Redhat distros but it appears Fedora users can just run: From c443168389467f2cec636a6b3fe190c025e6483b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Feb 2018 07:17:16 -0800 Subject: [PATCH 054/538] split out codec and encoder, allowing one to specify which encoder to use --- db/zm_create.sql.in | 3 +- db/zm_update-1.31.38.sql | 7 +- src/zm_event.cpp | 2 +- src/zm_monitor.cpp | 32 +- src/zm_monitor.h | 9 +- src/zm_videostore.cpp | 2006 +++++++++++++-------------- src/zm_videostore.h | 9 + version | 2 +- web/includes/Monitor.php | 3 +- web/skins/classic/views/monitor.php | 14 +- 10 files changed, 1036 insertions(+), 1051 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index b50c5934b..0431456ea 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -710,7 +710,8 @@ CREATE TABLE `Monitors` ( `Deinterlacing` int(10) unsigned NOT NULL default '0', `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' , `VideoWriter` TINYINT NOT NULL DEFAULT '0', - `OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2'), + `OutputCodec` int(10) unsigned NOT NULL default 0, + `Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2'), `OutputContainer` enum('auto','mp4','mkv'), `EncoderParameters` TEXT, `RecordAudio` TINYINT NOT NULL DEFAULT '0', diff --git a/db/zm_update-1.31.38.sql b/db/zm_update-1.31.38.sql index 0ca0be6ea..12fd3e96d 100644 --- a/db/zm_update-1.31.38.sql +++ b/db/zm_update-1.31.38.sql @@ -176,8 +176,10 @@ BEGIN WHERE Id=OLD.MonitorId; END IF; END IF; - ELSEIF ( NEW.Archived AND diff ) THEN - UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + ELSE + IF ( NEW.Archived AND diff ) THEN + UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; + END IF; END IF; IF ( diff ) THEN @@ -185,7 +187,6 @@ BEGIN END IF; END; - // delimiter ; diff --git a/src/zm_event.cpp b/src/zm_event.cpp index da23288ad..cd7360ace 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -190,7 +190,7 @@ Event::Event( if ( monitor->GetOptVideoWriter() != 0 ) { std::string container = monitor->OutputContainer(); if ( container == "auto" || container == "" ) { - if ( monitor->OutputCodec() == "h264" ) { + if ( monitor->OutputCodec() == AV_CODEC_ID_H264 ) { container = "mp4"; } else { container = "mkv"; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index e6519acd4..1ae4425c0 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -249,7 +249,8 @@ Monitor::Monitor( int p_colours, VideoWriter p_videowriter, std::string &p_encoderparams, - std::string &p_output_codec, + int p_output_codec, + std::string &p_encoder, std::string &p_output_container, bool p_record_audio, const char *p_event_prefix, @@ -292,6 +293,7 @@ Monitor::Monitor( videowriter( p_videowriter ), encoderparams( p_encoderparams ), output_codec( p_output_codec ), + encoder( p_encoder ), output_container( p_output_container ), record_audio( p_record_audio ), label_coord( p_label_coord ), @@ -1761,7 +1763,7 @@ void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) { #if ZM_HAS_V4L int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; ; if ( device[0] ) { sql += " AND Device='"; @@ -1826,7 +1828,8 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -1907,6 +1910,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -1955,7 +1959,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose #endif // ZM_HAS_V4L int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; if ( staticConfig.SERVER_ID ) { sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); } @@ -2001,7 +2005,8 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2096,6 +2101,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -2143,7 +2149,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c } int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; if ( file[0] ) { sql += " AND Path='"; sql += file; @@ -2185,7 +2191,8 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col]; col++; - std::string output_codec = dbrow[col]; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col]; col++; std::string output_container = dbrow[col]; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2250,6 +2257,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -2298,7 +2306,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu #if HAVE_LIBAVFORMAT int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; if ( file[0] ) { sql += " AND Path = '"; sql += file; @@ -2343,7 +2351,8 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2414,6 +2423,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, @@ -2524,7 +2534,8 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { int savejpegs = atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; - std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++; + int output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string encoder = dbrow[col] ? dbrow[col] : ""; col++; std::string output_container = dbrow[col] ? dbrow[col] : ""; col++; bool record_audio = (*dbrow[col] != '0'); col++; @@ -2746,6 +2757,7 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { videowriter, encoderparams, output_codec, + encoder, output_container, record_audio, event_prefix, diff --git a/src/zm_monitor.h b/src/zm_monitor.h index d35d8e30d..acb76fda9 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -239,7 +239,8 @@ protected: int colours; VideoWriter videowriter; std::string encoderparams; - std::string output_codec; + int output_codec; + std::string encoder; std::string output_container; std::vector encoderparamsvec; _AVPIXELFORMAT imagePixFormat; @@ -359,7 +360,8 @@ public: int p_colours, VideoWriter p_videowriter, std::string &p_encoderparams, - std::string &p_output_codec, + int p_output_codec, + std::string &p_encoder, std::string &p_output_container, bool p_record_audio, const char *p_event_prefix, @@ -458,7 +460,8 @@ public: VideoWriter GetOptVideoWriter() const { return( videowriter ); } const std::vector* GetOptEncoderParams() const { return( &encoderparamsvec ); } const std::string &GetEncoderOptions() const { return( encoderparams ); } - const std::string &OutputCodec() const { return output_codec; } + const std::string &Encoder() const { return encoder; } + const int OutputCodec() const { return output_codec; } const std::string &OutputContainer() const { return output_container; } uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; } diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 253eb500a..8f099ae0c 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -31,6 +31,13 @@ extern "C" { #include "libavutil/time.h" } +VideoStore::CodecData VideoStore::codec_data[] = { + { AV_CODEC_ID_H264, "h264", "h264_omx", AV_PIX_FMT_YUV420P }, + { AV_CODEC_ID_H264, "h264", "h264", AV_PIX_FMT_YUV420P }, + { AV_CODEC_ID_H264, "h264", "libx264", AV_PIX_FMT_YUV420P }, + { AV_CODEC_ID_MJPEG, "mjpeg", "mjpeg", AV_PIX_FMT_YUVJ422P }, +}; + VideoStore::VideoStore( const char *filename_in, const char *format_in, @@ -140,95 +147,34 @@ bool VideoStore::open() { video_in_stream_index = 0; } - if ( monitor->OutputCodec() == "mjpeg" ) { -Debug(2,"Using mjpeg"); - video_out_codec = avcodec_find_encoder_by_name("mjpeg"); - if ( ! video_out_codec ) { - Debug(1, "Didn't find mjpeg encoder"); - video_out_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); - } - video_out_ctx = avcodec_alloc_context3( video_out_codec ); - video_out_ctx->codec_id = video_out_codec->id; - video_out_ctx->pix_fmt = AV_PIX_FMT_YUVJ422P; - - } else if ( monitor->OutputCodec() == "h264" || monitor->OutputCodec() == "" ) { - AVPixelFormat pf = AV_PIX_FMT_YUV420P; - - // First try hardware accell - video_out_codec = avcodec_find_encoder_by_name("h264_omx"); - if ( ! video_out_codec ) { - Debug(1, "Didn't find omx"); - video_out_codec = avcodec_find_encoder(AV_CODEC_ID_H264); - } - if ( ! video_out_codec ) { - if ( AV_CODEC_ID_NONE == -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - video_in_stream->codecpar->codec_id -#else - video_in_stream->codec->codec_id + video_out_ctx = avcodec_alloc_context3(NULL); + if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { +#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) + video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +#else + video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif - ) { - Debug(1, "trying xh264rgb"); - // We will be encoding rgb images, so prefer - video_out_codec = avcodec_find_encoder_by_name("libx264rgb"); - if ( ! video_out_codec ) { - video_out_codec = avcodec_find_encoder_by_name("libx264"); - } else { - pf = -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - (AVPixelFormat)video_in_stream->codecpar->format; -#else - video_in_stream->codec->pix_fmt; -#endif - } - } else { - video_out_codec = avcodec_find_encoder_by_name("libx264"); - pf = AV_PIX_FMT_YUV420P; - } - } - // Need to do lookup by codec_id - if ( ! video_out_codec ) { - Error("Didn't find h264 encoder"); - video_out_codec = NULL; - return false; - } - Debug(1, "Using %s for codec", video_out_codec->name); - video_out_ctx = avcodec_alloc_context3(video_out_codec); - if ( AV_CODEC_ID_H264 != video_out_ctx->codec_id ) { - Warning("Have to set codec_id?"); - video_out_ctx->codec_id = AV_CODEC_ID_H264; - } - - video_out_ctx->pix_fmt = pf; - video_out_ctx->level = 32; - - } else { - Error("Unsupported output codec selected"); - return false; + } + int wanted_codec = monitor->OutputCodec(); + if ( ! wanted_codec ) { + // default to h264 + wanted_codec = AV_CODEC_ID_H264; } - // Copy params from instream to ctx // // FIXME SHould check that we are set to passthrough - if ( video_in_stream && ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) ) { + if ( video_in_stream && ( video_in_ctx->codec_id == wanted_codec ) ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar); #else ret = avcodec_copy_context( video_out_ctx, video_in_ctx ); #endif + // Copy params from instream to ctx if ( ret < 0 ) { Error("Could not initialize ctx parameteres"); return false; } //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate video_out_ctx->time_base = video_in_ctx->time_base; - - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { -#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; -#else - video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; -#endif - } // Fix deprecated formats switch ( video_out_ctx->pix_fmt ) { case AV_PIX_FMT_YUVJ422P : @@ -243,128 +189,9 @@ Debug(2,"Using mjpeg"); case AV_PIX_FMT_NONE : case AV_PIX_FMT_YUVJ420P : default: - video_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P; + video_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P; break; } - zm_dump_codec(video_out_ctx); - - } else { - - /** Create a new frame to store the */ - if ( !(video_in_frame = zm_av_frame_alloc()) ) { - Error("Could not allocate video_in frame"); - return false; - } - // Don't have an input stream, so need to tell it what we are sending it, or are transcoding - video_out_ctx->width = monitor->Width(); - video_out_ctx->height = monitor->Height(); - video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { -#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; -#else - video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; -#endif - } - - /* video time_base can be set to whatever is handy and supported by encoder */ - //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate - video_out_ctx->time_base = (AVRational){1, 30}; // microseconds as base frame rate - video_out_ctx->framerate = (AVRational){30,1}; - //video_out_ctx->gop_size = 12; - //video_out_ctx->qmin = 10; - //video_out_ctx->qmax = 51; - //video_out_ctx->qcompress = 0.6; - video_out_ctx->bit_rate = 400*1024; - video_out_ctx->thread_count = 0; - - if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { - video_out_ctx->max_b_frames = 1; - if ( video_out_ctx->priv_data ) { - av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); - //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); - } else { - Debug(2, "Not setting priv_data"); - } - } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - /* just for testing, we also add B frames */ - video_out_ctx->max_b_frames = 2; - } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { - /* Needed to avoid using macroblocks in which some coeffs overflow. - * This does not happen with normal video, it just happens here as - * the motion of the chroma plane does not match the luma plane. */ - video_out_ctx->mb_decision = 2; - } - - AVDictionary *opts = 0; - std::string Options = monitor->GetEncoderOptions(); - ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); - if ( ret < 0 ) { - Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); - } else { - AVDictionaryEntry *e = NULL; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { - Debug( 3, "Encoder Option %s=%s", e->key, e->value ); - } - } - - if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { - Warning("Can't open video codec (%s)! %s, trying h264", - video_out_codec->name, - av_make_error_string(ret).c_str() - ); - video_out_codec = avcodec_find_encoder_by_name("h264"); - if ( ! video_out_codec ) { - Error("Can't find h264 encoder"); - video_out_codec = avcodec_find_encoder_by_name("libx264"); - if ( ! video_out_codec ) { - Error("Can't find libx264 encoder"); - return false; - } - } - if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { - Error("Can't open video codec (%s)! %s", video_out_codec->name, - av_make_error_string(ret).c_str() ); -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_out_ctx); -#endif - video_out_ctx=NULL; - - return false; - } - } // end if can't open codec - Debug(2,"Sucess opening codec"); - AVDictionaryEntry *e = NULL; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { - Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key); - } - av_dict_free(&opts); - - if ( !video_out_ctx->codec_tag ) { - video_out_ctx->codec_tag = - av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id ); - Debug(2, "No codec_tag, setting to h264 ? "); - } - } // end if copying or trasncoding - - video_out_stream = avformat_new_stream(oc, video_out_codec); - if ( ! video_out_stream ) { - Error("Unable to create video out stream"); - return false; - } -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx); - if ( ret < 0 ) { - Error("Could not initialize stream parameteres"); - return false; - } -#else - avcodec_copy_context(video_out_stream->codec, video_out_ctx); -#endif - - if ( video_in_stream && ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) ) { // Only set orientation if doing passthrough, otherwise the frame image will be rotated Monitor::Orientation orientation = monitor->getOrientation(); if ( orientation ) { @@ -382,15 +209,138 @@ Debug(2,"Using mjpeg"); } else { Warning("Unsupported Orientation(%d)", orientation); } + } // end if orientation + } else { + /** Create a new frame to store the */ + if ( !(video_in_frame = zm_av_frame_alloc()) ) { + Error("Could not allocate video_in frame"); + return false; } + for (int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) { + if ( codec_data[i].codec_id != monitor->OutputCodec() ) + continue; + + video_out_codec = avcodec_find_encoder_by_name(codec_data[i].codec_name); + if ( ! video_out_codec ) { + Debug(1, "Didn't find encoder for %s", codec_data[i].codec_name); + continue; + } + + Debug(1, "Using %s for codec", video_out_codec->name); + //video_out_ctx = avcodec_alloc_context3(video_out_codec); + if ( video_out_codec->id != video_out_ctx->codec_id ) { + Warning("Have to set codec_id?"); + video_out_ctx->codec_id = AV_CODEC_ID_H264; + } + + video_out_ctx->pix_fmt = codec_data[i].pix_fmt; + video_out_ctx->level = 32; + + // Don't have an input stream, so need to tell it what we are sending it, or are transcoding + video_out_ctx->width = monitor->Width(); + video_out_ctx->height = monitor->Height(); + video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; + + /* video time_base can be set to whatever is handy and supported by encoder */ + //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate + video_out_ctx->time_base = (AVRational){1, 30}; // microseconds as base frame rate + video_out_ctx->framerate = (AVRational){30,1}; + //video_out_ctx->gop_size = 12; + //video_out_ctx->qmin = 10; + //video_out_ctx->qmax = 51; + //video_out_ctx->qcompress = 0.6; + video_out_ctx->bit_rate = 400*1024; + video_out_ctx->thread_count = 0; + + if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { + video_out_ctx->max_b_frames = 1; + if ( video_out_ctx->priv_data ) { + av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); + //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); + } else { + Debug(2, "Not setting priv_data"); + } + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + video_out_ctx->max_b_frames = 2; + } else if (video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + video_out_ctx->mb_decision = 2; + } + + AVDictionary *opts = 0; + std::string Options = monitor->GetEncoderOptions(); + ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); + if ( ret < 0 ) { + Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); + } else { + AVDictionaryEntry *e = NULL; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + Debug( 3, "Encoder Option %s=%s", e->key, e->value ); + } + } + + if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { + Warning("Can't open video codec (%s) %s", + video_out_codec->name, + av_make_error_string(ret).c_str() + ); + video_out_codec = NULL; + } + + AVDictionaryEntry *e = NULL; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key); + } + av_dict_free(&opts); + if ( video_out_codec ) break; + + } // end foreach codec + + if ( ! video_out_codec ) { + Error("Can't open video codec!"); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&video_out_ctx); +#endif + video_out_ctx = NULL; + + return false; + } // end if can't open codec + + Debug(2,"Sucess opening codec"); + + } // end if copying or trasncoding + + if ( !video_out_ctx->codec_tag ) { + video_out_ctx->codec_tag = + av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id ); + Debug(2, "No codec_tag, setting to h264 ? "); } + video_out_stream = avformat_new_stream(oc, video_out_codec); + if ( ! video_out_stream ) { + Error("Unable to create video out stream"); + return false; + } +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx); + if ( ret < 0 ) { + Error("Could not initialize stream parameteres"); + return false; + } +#else + avcodec_copy_context(video_out_stream->codec, video_out_ctx); +#endif + if ( audio_in_stream ) { audio_in_stream_index = audio_in_stream->index; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) audio_in_ctx = avcodec_alloc_context3(NULL); ret = avcodec_parameters_to_context(audio_in_ctx, - audio_in_stream->codecpar); + audio_in_stream->codecpar); #else audio_in_ctx = audio_in_stream->codec; #endif @@ -398,7 +348,7 @@ Debug(2,"Using mjpeg"); if ( audio_in_ctx->codec_id != AV_CODEC_ID_AAC ) { static char error_buffer[256]; avcodec_string(error_buffer, sizeof(error_buffer), audio_in_ctx, - 0); + 0); Debug(2, "Got something other than AAC (%s)", error_buffer); if ( !setup_resampler() ) { @@ -409,9 +359,9 @@ Debug(2,"Using mjpeg"); audio_out_stream = #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - avformat_new_stream(oc, (const AVCodec *)(audio_in_ctx->codec)); + avformat_new_stream(oc, (const AVCodec *)(audio_in_ctx->codec)); #else - avformat_new_stream(oc, (AVCodec *)audio_in_ctx->codec); + avformat_new_stream(oc, (AVCodec *)audio_in_ctx->codec); #endif if ( !audio_out_stream ) { Error("Unable to create audio out stream\n"); @@ -424,13 +374,13 @@ Debug(2,"Using mjpeg"); // Copy params from instream to ctx if ( (ret = avcodec_parameters_to_context(audio_out_ctx, audio_in_stream->codecpar) ) < 0 ) { Error("Unable to copy audio params to ctx %s\n", - av_make_error_string(ret).c_str()); + av_make_error_string(ret).c_str()); } // Then from ctx to out_stream ret = avcodec_parameters_from_context(audio_out_stream->codecpar, audio_out_ctx); if ( ret < 0 ) { Error("Unable to copy audio params to stream %s\n", - av_make_error_string(ret).c_str()); + av_make_error_string(ret).c_str()); } if ( !audio_out_ctx->codec_tag ) { @@ -445,7 +395,7 @@ Debug(2,"Using mjpeg"); #endif if ( ret < 0 ) { Error("Unable to copy audio ctx %s\n", - av_make_error_string(ret).c_str()); + av_make_error_string(ret).c_str()); audio_out_stream = NULL; } else { if ( audio_out_ctx->channels > 1 ) { @@ -456,26 +406,24 @@ Debug(2,"Using mjpeg"); } } } // end if audio_out_stream - } // end if is AAC + } // end if is AAC if ( audio_out_stream ) { if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else - audio_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; + audio_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif } } } // end if audio_in_stream /* open the out file, if needed */ - if (!(out_format->flags & AVFMT_NOFILE)) { - ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL); - if (ret < 0) { + if ( !(out_format->flags & AVFMT_NOFILE) ) { + if ( (ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL) ) < 0 ) { Error("Could not open out file '%s': %s\n", filename, - av_make_error_string(ret).c_str()); - + av_make_error_string(ret).c_str()); return false; } } @@ -495,7 +443,7 @@ Debug(2,"Using mjpeg"); } if ( ret < 0 ) { Error("Error occurred when writing out file header to %s: %s\n", - filename, av_make_error_string(ret).c_str()); + filename, av_make_error_string(ret).c_str()); return false; } if ( opts ) av_dict_free(&opts); @@ -503,83 +451,32 @@ Debug(2,"Using mjpeg"); zm_dump_stream_format(oc, 0, 0, 1); if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1); return true; -} // end bool VideoStore::open() + } // end bool VideoStore::open() -void VideoStore::write_audio_packet( AVPacket &pkt ) { -//Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, - //pkt.dts, pkt.duration); - pkt.pts = audio_next_pts; - pkt.dts = audio_next_dts; + void VideoStore::write_audio_packet( AVPacket &pkt ) { + //Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, + //pkt.dts, pkt.duration); + pkt.pts = audio_next_pts; + pkt.dts = audio_next_dts; - Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); - if ( pkt.duration > 0 ) { - pkt.duration = - av_rescale_q(pkt.duration, audio_out_ctx->time_base, - audio_out_stream->time_base); - } - audio_next_pts += pkt.duration; - audio_next_dts += pkt.duration; + Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); + if ( pkt.duration > 0 ) { + pkt.duration = + av_rescale_q(pkt.duration, audio_out_ctx->time_base, + audio_out_stream->time_base); + } + audio_next_pts += pkt.duration; + audio_next_dts += pkt.duration; - Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); - pkt.stream_index = audio_out_stream->index; - av_interleaved_write_frame(oc, &pkt); -} // end void VideoStore::Write_audio_packet( AVPacket &pkt ) + Debug(2, "writing audio packet pts(%d) dts(%d) duration(%d)", pkt.pts, pkt.dts, pkt.duration); + pkt.stream_index = audio_out_stream->index; + av_interleaved_write_frame(oc, &pkt); + } // end void VideoStore::Write_audio_packet( AVPacket &pkt ) -VideoStore::~VideoStore() { - if ( oc->pb ) { - if ( ( video_out_ctx->codec_id != video_in_ctx->codec_id ) || audio_out_codec ) { - Debug(2,"Different codecs between in and out"); - // The codec queues data. We need to send a flush command and out - // whatever we get. Failures are not fatal. - AVPacket pkt; - // WIthout these we seg fault I don't know why. - pkt.data = NULL; - pkt.size = 0; - av_init_packet(&pkt); - - // I got crashes if the codec didn't do DELAY, so let's test for it. - if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - AV_CODEC_CAP_DELAY -#else - CODEC_CAP_DELAY -#endif - ) ) { - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Put encoder into flushing mode - avcodec_send_frame(video_out_ctx, NULL); - while (1) { - ret = avcodec_receive_packet(video_out_ctx, &pkt); - if (ret < 0) { - if (AVERROR_EOF != ret) { - Error("ERror encoding audio while flushing (%d) (%s)", ret, - av_err2str(ret)); - } - break; - } -#else - while (1) { - // WIthout these we seg fault I don't know why. - pkt.data = NULL; - pkt.size = 0; - av_init_packet(&pkt); - int got_packet = 0; - ret = avcodec_encode_video2(video_out_ctx, &pkt, NULL, &got_packet); - if ( ret < 0 ) { - Error("ERror encoding video while flushing (%d) (%s)", ret, av_err2str(ret)); - break; - } - if (!got_packet) { - break; - } -#endif - write_video_packet(pkt); - zm_av_packet_unref(&pkt); - } // while have buffered frames - } // end if have delay capability - - if ( audio_out_codec ) { + VideoStore::~VideoStore() { + if ( oc->pb ) { + if ( ( video_out_ctx->codec_id != video_in_ctx->codec_id ) || audio_out_codec ) { + Debug(2,"Different codecs between in and out"); // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. AVPacket pkt; @@ -588,845 +485,896 @@ VideoStore::~VideoStore() { pkt.size = 0; av_init_packet(&pkt); + // I got crashes if the codec didn't do DELAY, so let's test for it. + if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Put encoder into flushing mode - avcodec_send_frame(audio_out_ctx, NULL); - while (1) { - if ( (ret = avcodec_receive_packet(audio_out_ctx, &pkt) ) < 0 ) { - if ( AVERROR_EOF != ret ) { - Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); + AV_CODEC_CAP_DELAY +#else + CODEC_CAP_DELAY +#endif + ) ) { + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // Put encoder into flushing mode + avcodec_send_frame(video_out_ctx, NULL); + while (1) { + ret = avcodec_receive_packet(video_out_ctx, &pkt); + if (ret < 0) { + if (AVERROR_EOF != ret) { + Error("ERror encoding audio while flushing (%d) (%s)", ret, + av_err2str(ret)); + } + break; } - break; - } #else - while (1) { - pkt.data = NULL; - pkt.size = 0; - av_init_packet(&pkt); - int got_packet = 0; - if ( (ret = avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet)) < 0 ) { - Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); - break; + while (1) { + // WIthout these we seg fault I don't know why. + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pkt); + int got_packet = 0; + ret = avcodec_encode_video2(video_out_ctx, &pkt, NULL, &got_packet); + if ( ret < 0 ) { + Error("ERror encoding video while flushing (%d) (%s)", ret, av_err2str(ret)); + break; + } + if (!got_packet) { + break; + } +#endif + write_video_packet(pkt); + zm_av_packet_unref(&pkt); + } // while have buffered frames + } // end if have delay capability + + if ( audio_out_codec ) { + // The codec queues data. We need to send a flush command and out + // whatever we get. Failures are not fatal. + AVPacket pkt; + // WIthout these we seg fault I don't know why. + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pkt); + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // Put encoder into flushing mode + avcodec_send_frame(audio_out_ctx, NULL); + while (1) { + if ( (ret = avcodec_receive_packet(audio_out_ctx, &pkt) ) < 0 ) { + if ( AVERROR_EOF != ret ) { + Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); + } + break; + } +#else + while (1) { + pkt.data = NULL; + pkt.size = 0; + av_init_packet(&pkt); + int got_packet = 0; + if ( (ret = avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet)) < 0 ) { + Error("ERror encoding audio while flushing (%d) (%s)", ret, av_err2str(ret)); + break; + } + Debug(1, "Have audio encoder, need to flush it's out"); + if (!got_packet) { + break; + } +#endif + write_audio_packet(pkt); + zm_av_packet_unref(&pkt); + } // while have buffered frames + } // end if audio_out_codec + } // end if buffers + + // Flush Queues + av_interleaved_write_frame(oc, NULL); + + /* Write the trailer before close */ + if ( int rc = av_write_trailer(oc) ) { + Error("Error writing trailer %s", av_err2str(rc)); + } else { + Debug(3, "Sucess Writing trailer"); } - Debug(1, "Have audio encoder, need to flush it's out"); - if (!got_packet) { - break; + + // WHen will be not using a file ? + if ( !(out_format->flags & AVFMT_NOFILE) ) { + /* Close the out file. */ + Debug(2, "Closing"); + if (int rc = avio_close(oc->pb)) { + oc->pb = NULL; + Error("Error closing avio %s", av_err2str(rc)); + } + } else { + Debug(3, "Not closing avio because we are not writing to a file."); } -#endif - write_audio_packet(pkt); - zm_av_packet_unref(&pkt); - } // while have buffered frames - } // end if audio_out_codec - } // end if buffers + } // end if oc->pb - // Flush Queues - av_interleaved_write_frame(oc, NULL); + // I wonder if we should be closing the file first. + // I also wonder if we really need to be doing all the ctx + // allocation/de-allocation constantly, or whether we can just re-use it. + // Just do a file open/close/writeheader/etc. + // What if we were only doing audio recording? - /* Write the trailer before close */ - if ( int rc = av_write_trailer(oc) ) { - Error("Error writing trailer %s", av_err2str(rc)); - } else { - Debug(3, "Sucess Writing trailer"); - } - - // WHen will be not using a file ? - if ( !(out_format->flags & AVFMT_NOFILE) ) { - /* Close the out file. */ - Debug(2, "Closing"); - if (int rc = avio_close(oc->pb)) { - oc->pb = NULL; - Error("Error closing avio %s", av_err2str(rc)); - } - } else { - Debug(3, "Not closing avio because we are not writing to a file."); - } - } // end if oc->pb - - // I wonder if we should be closing the file first. - // I also wonder if we really need to be doing all the ctx - // allocation/de-allocation constantly, or whether we can just re-use it. - // Just do a file open/close/writeheader/etc. - // What if we were only doing audio recording? - - if ( video_out_stream ) { + if ( video_out_stream ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_in_ctx); + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&video_in_ctx); #endif - video_in_ctx=NULL; + video_in_ctx=NULL; - avcodec_close(video_out_ctx); + avcodec_close(video_out_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - avcodec_free_context(&video_out_ctx); + avcodec_free_context(&video_out_ctx); #endif - video_out_ctx = NULL; - Debug(4, "Success freeing video_out_ctx"); - } - if ( audio_out_stream ) { - if ( audio_in_codec ) { - avcodec_close(audio_in_ctx); + video_out_ctx = NULL; + Debug(4, "Success freeing video_out_ctx"); + } + if ( audio_out_stream ) { + if ( audio_in_codec ) { + avcodec_close(audio_in_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&audio_in_ctx); + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&audio_in_ctx); #endif - audio_in_ctx = NULL; - audio_in_codec = NULL; - } // end if audio_in_codec + audio_in_ctx = NULL; + audio_in_codec = NULL; + } // end if audio_in_codec - avcodec_close(audio_out_ctx); + avcodec_close(audio_out_ctx); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - avcodec_free_context(&audio_out_ctx); + avcodec_free_context(&audio_out_ctx); #endif - audio_out_ctx = NULL; + audio_out_ctx = NULL; #ifdef HAVE_LIBAVRESAMPLE - if ( resample_ctx ) { - avresample_close(resample_ctx); - avresample_free(&resample_ctx); - } - if ( out_frame ) { - av_frame_free(&out_frame); - out_frame = NULL; - } - if ( converted_in_samples ) { - av_free(converted_in_samples); - converted_in_samples = NULL; - } + if ( resample_ctx ) { + avresample_close(resample_ctx); + avresample_free(&resample_ctx); + } + if ( out_frame ) { + av_frame_free(&out_frame); + out_frame = NULL; + } + if ( converted_in_samples ) { + av_free(converted_in_samples); + converted_in_samples = NULL; + } #endif - } + } #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( video_in_ctx ) { - avcodec_free_context(&video_in_ctx); - video_in_ctx = NULL; - } - if ( video_out_ctx ) { - avcodec_close(video_out_ctx); - avcodec_free_context(&video_out_ctx); - video_out_ctx = NULL; - } + if ( video_in_ctx ) { + avcodec_free_context(&video_in_ctx); + video_in_ctx = NULL; + } + if ( video_out_ctx ) { + avcodec_close(video_out_ctx); + avcodec_free_context(&video_out_ctx); + video_out_ctx = NULL; + } #endif - /* free the stream */ - avformat_free_context(oc); -} - -bool VideoStore::setup_resampler() { - //I think this is unneccessary, we should be able to just pass in the decoder from the input. -#ifdef HAVE_LIBAVRESAMPLE - // Newer ffmpeg wants to keep everything separate... so have to lookup our own - // decoder, can't reuse the one from the camera. - AVCodec *audio_in_codec = avcodec_find_decoder( -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_in_stream->codecpar->codec_id -#else - audio_in_ctx->codec_id -#endif - ); - if ( (ret = avcodec_open2(audio_in_ctx, audio_in_codec, NULL)) < 0 ) { - Error("Can't open in codec!"); - return false; - } - - audio_out_codec = avcodec_find_encoder(AV_CODEC_ID_AAC); - if ( !audio_out_codec ) { - Error("Could not find codec for AAC"); - return false; - } - - // Now copy them to the out stream - audio_out_stream = avformat_new_stream(oc, audio_out_codec); - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if ( !audio_out_ctx ) { - Error("could not allocate codec ctx for AAC\n"); - audio_out_stream = NULL; - return false; - } -#else - audio_out_ctx = audio_out_stream->codec; -#endif - - /* put sample parameters */ - audio_out_ctx->bit_rate = audio_in_ctx->bit_rate; - audio_out_ctx->sample_rate = audio_in_ctx->sample_rate; - audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; - audio_out_ctx->channels = audio_in_ctx->channels; - audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; - - if ( audio_out_codec->supported_samplerates ) { - int found = 0; - for ( int i=0; audio_out_codec->supported_samplerates[i]; i++ ) { - if ( audio_out_ctx->sample_rate == - audio_out_codec->supported_samplerates[i]) { - found = 1; - break; + /* free the stream */ + avformat_free_context(oc); } - } - if ( found ) { - Debug(4, "Sample rate is good"); - } else { - audio_out_ctx->sample_rate = - audio_out_codec->supported_samplerates[0]; - Debug(1, "Sampel rate is no good, setting to (%d)", - audio_out_codec->supported_samplerates[0]); - } - } - /* check that the encoder supports s16 pcm in */ - if ( !check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt) ) { - Debug(3, "Encoder does not support sample format %s, setting to FLTP", - av_get_sample_fmt_name(audio_out_ctx->sample_fmt)); - audio_out_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; - } - - // Example code doesn't set the codec tb. I think it just uses whatever defaults - //audio_out_ctx->time_base = (AVRational){1, audio_out_ctx->sample_rate}; - - - AVDictionary *opts = NULL; - // Needed to allow AAC - if ( (ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0 ) { - Error("Couldn't set experimental"); - } - ret = avcodec_open2(audio_out_ctx, audio_out_codec, &opts); - av_dict_free(&opts); - if ( ret < 0 ) { - Fatal("could not open codec (%d) (%s)\n", ret, av_make_error_string(ret).c_str()); - audio_out_codec = NULL; - audio_out_ctx = NULL; - audio_out_stream = NULL; - return false; - } - - audio_out_stream->time_base = (AVRational){1, audio_out_ctx->sample_rate}; + bool VideoStore::setup_resampler() { + //I think this is unneccessary, we should be able to just pass in the decoder from the input. +#ifdef HAVE_LIBAVRESAMPLE + // Newer ffmpeg wants to keep everything separate... so have to lookup our own + // decoder, can't reuse the one from the camera. + AVCodec *audio_in_codec = avcodec_find_decoder( #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( (ret = avcodec_parameters_from_context( - audio_out_stream->codecpar, - audio_out_ctx)) < 0 ) { - Error("Could not initialize stream parameteres"); - return false; - } - //audio_out_stream->codecpar->frame_size = audio_out_ctx->frame_size; - //audio_out_stream->codecpar->bit_rate = audio_out_ctx->bit_rate; + audio_in_stream->codecpar->codec_id #else - avcodec_copy_context( audio_out_stream->codec, audio_out_ctx ); + audio_in_ctx->codec_id #endif + ); + if ( (ret = avcodec_open2(audio_in_ctx, audio_in_codec, NULL)) < 0 ) { + Error("Can't open in codec!"); + return false; + } - Debug(1, - "Audio out context bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " - "layout(%d) frame_size(%d)", - audio_out_ctx->bit_rate, audio_out_ctx->sample_rate, - audio_out_ctx->channels, audio_out_ctx->sample_fmt, - audio_out_ctx->channel_layout, audio_out_ctx->frame_size); + audio_out_codec = avcodec_find_encoder(AV_CODEC_ID_AAC); + if ( !audio_out_codec ) { + Error("Could not find codec for AAC"); + return false; + } + + // Now copy them to the out stream + audio_out_stream = avformat_new_stream(oc, audio_out_codec); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - Debug(1, - "Audio out stream bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " - "layout(%d) frame_size(%d)", - audio_out_stream->codecpar->bit_rate, audio_out_stream->codecpar->sample_rate, - audio_out_stream->codecpar->channels, audio_out_stream->codecpar->format, - audio_out_stream->codecpar->channel_layout, audio_out_stream->codecpar->frame_size); + audio_out_ctx = avcodec_alloc_context3(audio_out_codec); + if ( !audio_out_ctx ) { + Error("could not allocate codec ctx for AAC\n"); + audio_out_stream = NULL; + return false; + } #else - Debug(1, - "Audio out bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " - "layout(%d) frame_size(%d)", - audio_out_stream->codec->bit_rate, audio_out_stream->codec->sample_rate, - audio_out_stream->codec->channels, audio_out_stream->codec->sample_fmt, - audio_out_stream->codec->channel_layout, audio_out_stream->codec->frame_size); + audio_out_ctx = audio_out_stream->codec; #endif - /** Create a new frame to store the audio samples. */ - if ( ! in_frame ) { - if (!(in_frame = zm_av_frame_alloc())) { - Error("Could not allocate in frame"); - return false; - } - } + /* put sample parameters */ + audio_out_ctx->bit_rate = audio_in_ctx->bit_rate; + audio_out_ctx->sample_rate = audio_in_ctx->sample_rate; + audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; + audio_out_ctx->channels = audio_in_ctx->channels; + audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; - /** Create a new frame to store the audio samples. */ - if ( !(out_frame = zm_av_frame_alloc()) ) { - Error("Could not allocate out frame"); - av_frame_free(&in_frame); - return false; - } - out_frame->sample_rate = audio_out_ctx->sample_rate; + if ( audio_out_codec->supported_samplerates ) { + int found = 0; + for ( int i=0; audio_out_codec->supported_samplerates[i]; i++ ) { + if ( audio_out_ctx->sample_rate == + audio_out_codec->supported_samplerates[i]) { + found = 1; + break; + } + } + if ( found ) { + Debug(4, "Sample rate is good"); + } else { + audio_out_ctx->sample_rate = + audio_out_codec->supported_samplerates[0]; + Debug(1, "Sampel rate is no good, setting to (%d)", + audio_out_codec->supported_samplerates[0]); + } + } - // Setup the audio resampler - resample_ctx = avresample_alloc_context(); - if ( !resample_ctx ) { - Error("Could not allocate resample ctx\n"); - return false; - } + /* check that the encoder supports s16 pcm in */ + if ( !check_sample_fmt(audio_out_codec, audio_out_ctx->sample_fmt) ) { + Debug(3, "Encoder does not support sample format %s, setting to FLTP", + av_get_sample_fmt_name(audio_out_ctx->sample_fmt)); + audio_out_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP; + } - uint64_t mono_layout = av_get_channel_layout("mono"); - // Some formats (i.e. WAV) do not produce the proper channel layout - if ( audio_in_ctx->channel_layout == 0 ) { - av_opt_set_int(resample_ctx, "in_channel_layout", mono_layout, 0); - Debug(1, "Bad channel layout. Need to set it to mono (%d).", mono_layout); - } else { - Debug(1, "channel layout. set it to mono (%d).", audio_in_ctx->channel_layout); - av_opt_set_int(resample_ctx, "in_channel_layout", - audio_in_ctx->channel_layout, 0); - } + // Example code doesn't set the codec tb. I think it just uses whatever defaults + //audio_out_ctx->time_base = (AVRational){1, audio_out_ctx->sample_rate}; - av_opt_set_int(resample_ctx, "in_sample_fmt", audio_in_ctx->sample_fmt, 0); - av_opt_set_int(resample_ctx, "in_sample_rate", audio_in_ctx->sample_rate, 0); - av_opt_set_int(resample_ctx, "in_channels", audio_in_ctx->channels, 0); - // av_opt_set_int( resample_ctx, "out_channel_layout", - // audio_out_ctx->channel_layout, 0); - av_opt_set_int(resample_ctx, "out_channel_layout", mono_layout, 0); - av_opt_set_int(resample_ctx, "out_sample_fmt", - audio_out_ctx->sample_fmt, 0); - av_opt_set_int(resample_ctx, "out_sample_rate", - audio_out_ctx->sample_rate, 0); - av_opt_set_int(resample_ctx, "out_channels", - audio_out_ctx->channels, 0); - if ( (ret = avresample_open(resample_ctx)) < 0 ) { - Error("Could not open resample ctx\n"); - return false; - } + AVDictionary *opts = NULL; + // Needed to allow AAC + if ( (ret = av_dict_set(&opts, "strict", "experimental", 0)) < 0 ) { + Error("Couldn't set experimental"); + } + ret = avcodec_open2(audio_out_ctx, audio_out_codec, &opts); + av_dict_free(&opts); + if ( ret < 0 ) { + Fatal("could not open codec (%d) (%s)\n", ret, av_make_error_string(ret).c_str()); + audio_out_codec = NULL; + audio_out_ctx = NULL; + audio_out_stream = NULL; + return false; + } - out_frame->nb_samples = audio_out_ctx->frame_size; - out_frame->format = audio_out_ctx->sample_fmt; - out_frame->channel_layout = audio_out_ctx->channel_layout; - - // The codec gives us the frame size, in samples, we calculate the size of the - // samples buffer in bytes - unsigned int audioSampleBuffer_size = av_samples_get_buffer_size( - NULL, audio_out_ctx->channels, audio_out_ctx->frame_size, - audio_out_ctx->sample_fmt, 0); - converted_in_samples = (uint8_t *)av_malloc(audioSampleBuffer_size); - - if ( !converted_in_samples ) { - Error("Could not allocate converted in sample pointers\n"); - return false; - } - - // Setup the data pointers in the AVFrame - if ( avcodec_fill_audio_frame(out_frame, audio_out_ctx->channels, - audio_out_ctx->sample_fmt, - (const uint8_t *)converted_in_samples, - audioSampleBuffer_size, 0) < 0 ) { - Error("Could not allocate converted in sample pointers\n"); - return false; - } - - return true; + audio_out_stream->time_base = (AVRational){1, audio_out_ctx->sample_rate}; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + if ( (ret = avcodec_parameters_from_context( + audio_out_stream->codecpar, + audio_out_ctx)) < 0 ) { + Error("Could not initialize stream parameteres"); + return false; + } + //audio_out_stream->codecpar->frame_size = audio_out_ctx->frame_size; + //audio_out_stream->codecpar->bit_rate = audio_out_ctx->bit_rate; #else - Error( - "Not built with libavresample library. Cannot do audio conversion to " - "AAC"); - return false; + avcodec_copy_context( audio_out_stream->codec, audio_out_ctx ); #endif -} // end bool VideoStore::setup_resampler() + + Debug(1, + "Audio out context bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " + "layout(%d) frame_size(%d)", + audio_out_ctx->bit_rate, audio_out_ctx->sample_rate, + audio_out_ctx->channels, audio_out_ctx->sample_fmt, + audio_out_ctx->channel_layout, audio_out_ctx->frame_size); + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + Debug(1, + "Audio out stream bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " + "layout(%d) frame_size(%d)", + audio_out_stream->codecpar->bit_rate, audio_out_stream->codecpar->sample_rate, + audio_out_stream->codecpar->channels, audio_out_stream->codecpar->format, + audio_out_stream->codecpar->channel_layout, audio_out_stream->codecpar->frame_size); +#else + Debug(1, + "Audio out bit_rate (%d) sample_rate(%d) channels(%d) fmt(%d) " + "layout(%d) frame_size(%d)", + audio_out_stream->codec->bit_rate, audio_out_stream->codec->sample_rate, + audio_out_stream->codec->channels, audio_out_stream->codec->sample_fmt, + audio_out_stream->codec->channel_layout, audio_out_stream->codec->frame_size); +#endif + + /** Create a new frame to store the audio samples. */ + if ( ! in_frame ) { + if (!(in_frame = zm_av_frame_alloc())) { + Error("Could not allocate in frame"); + return false; + } + } + + /** Create a new frame to store the audio samples. */ + if ( !(out_frame = zm_av_frame_alloc()) ) { + Error("Could not allocate out frame"); + av_frame_free(&in_frame); + return false; + } + out_frame->sample_rate = audio_out_ctx->sample_rate; + + // Setup the audio resampler + resample_ctx = avresample_alloc_context(); + if ( !resample_ctx ) { + Error("Could not allocate resample ctx\n"); + return false; + } + + uint64_t mono_layout = av_get_channel_layout("mono"); + // Some formats (i.e. WAV) do not produce the proper channel layout + if ( audio_in_ctx->channel_layout == 0 ) { + av_opt_set_int(resample_ctx, "in_channel_layout", mono_layout, 0); + Debug(1, "Bad channel layout. Need to set it to mono (%d).", mono_layout); + } else { + Debug(1, "channel layout. set it to mono (%d).", audio_in_ctx->channel_layout); + av_opt_set_int(resample_ctx, "in_channel_layout", + audio_in_ctx->channel_layout, 0); + } + + av_opt_set_int(resample_ctx, "in_sample_fmt", audio_in_ctx->sample_fmt, 0); + av_opt_set_int(resample_ctx, "in_sample_rate", audio_in_ctx->sample_rate, 0); + av_opt_set_int(resample_ctx, "in_channels", audio_in_ctx->channels, 0); + // av_opt_set_int( resample_ctx, "out_channel_layout", + // audio_out_ctx->channel_layout, 0); + av_opt_set_int(resample_ctx, "out_channel_layout", mono_layout, 0); + av_opt_set_int(resample_ctx, "out_sample_fmt", + audio_out_ctx->sample_fmt, 0); + av_opt_set_int(resample_ctx, "out_sample_rate", + audio_out_ctx->sample_rate, 0); + av_opt_set_int(resample_ctx, "out_channels", + audio_out_ctx->channels, 0); + + if ( (ret = avresample_open(resample_ctx)) < 0 ) { + Error("Could not open resample ctx\n"); + return false; + } + + out_frame->nb_samples = audio_out_ctx->frame_size; + out_frame->format = audio_out_ctx->sample_fmt; + out_frame->channel_layout = audio_out_ctx->channel_layout; + + // The codec gives us the frame size, in samples, we calculate the size of the + // samples buffer in bytes + unsigned int audioSampleBuffer_size = av_samples_get_buffer_size( + NULL, audio_out_ctx->channels, audio_out_ctx->frame_size, + audio_out_ctx->sample_fmt, 0); + converted_in_samples = (uint8_t *)av_malloc(audioSampleBuffer_size); + + if ( !converted_in_samples ) { + Error("Could not allocate converted in sample pointers\n"); + return false; + } + + // Setup the data pointers in the AVFrame + if ( avcodec_fill_audio_frame(out_frame, audio_out_ctx->channels, + audio_out_ctx->sample_fmt, + (const uint8_t *)converted_in_samples, + audioSampleBuffer_size, 0) < 0 ) { + Error("Could not allocate converted in sample pointers\n"); + return false; + } + + return true; +#else + Error( + "Not built with libavresample library. Cannot do audio conversion to " + "AAC"); + return false; +#endif + } // end bool VideoStore::setup_resampler() -int VideoStore::writePacket( ZMPacket *ipkt ) { - if ( ipkt->packet.stream_index == video_in_stream_index ) { - return writeVideoFramePacket( ipkt ); - } else if ( ipkt->packet.stream_index == audio_in_stream_index ) { - return writeAudioFramePacket( ipkt ); - } - Error("Unknown stream type in packet (%d) out input video stream is (%d) and audio is (%d)", - ipkt->packet.stream_index, video_in_stream_index, ( audio_in_stream ? audio_in_stream_index : -1 ) - ); - return 0; -} - -int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) { - frame_count += 1; - - // if we have to transcode - if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) { - //Debug(3, "Have encoding video frame count (%d)", frame_count); - - if ( ! zm_packet->out_frame ) { - //Debug(3, "Have no out frame"); - AVFrame *out_frame = zm_packet->out_frame = zm_av_frame_alloc(); - if ( ! out_frame ) { - Error("Unable to allocate a frame"); + int VideoStore::writePacket( ZMPacket *ipkt ) { + if ( ipkt->packet.stream_index == video_in_stream_index ) { + return writeVideoFramePacket( ipkt ); + } else if ( ipkt->packet.stream_index == audio_in_stream_index ) { + return writeAudioFramePacket( ipkt ); + } + Error("Unknown stream type in packet (%d) out input video stream is (%d) and audio is (%d)", + ipkt->packet.stream_index, video_in_stream_index, ( audio_in_stream ? audio_in_stream_index : -1 ) + ); return 0; } + + int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) { + frame_count += 1; + + // if we have to transcode + if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) { + //Debug(3, "Have encoding video frame count (%d)", frame_count); + + if ( ! zm_packet->out_frame ) { + //Debug(3, "Have no out frame"); + AVFrame *out_frame = zm_packet->out_frame = zm_av_frame_alloc(); + if ( ! out_frame ) { + Error("Unable to allocate a frame"); + return 0; + } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - int codec_imgsize = av_image_get_buffer_size( - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height, 1); - zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); - av_image_fill_arrays( - out_frame->data, - out_frame->linesize, - zm_packet->buffer, - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height, - 1); + int codec_imgsize = av_image_get_buffer_size( + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height, 1); + zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); + av_image_fill_arrays( + out_frame->data, + out_frame->linesize, + zm_packet->buffer, + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height, + 1); #else - int codec_imgsize = avpicture_get_size( - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height); - zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); - avpicture_fill( - (AVPicture *)out_frame, - zm_packet->buffer, - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height - ); + int codec_imgsize = avpicture_get_size( + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height); + zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize); + avpicture_fill( + (AVPicture *)out_frame, + zm_packet->buffer, + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height + ); #endif - out_frame->width = video_out_ctx->width; - out_frame->height = video_out_ctx->height; - out_frame->format = video_out_ctx->pix_fmt; - //out_frame->pkt_duration = 0; + out_frame->width = video_out_ctx->width; + out_frame->height = video_out_ctx->height; + out_frame->format = video_out_ctx->pix_fmt; + //out_frame->pkt_duration = 0; - if ( ! zm_packet->in_frame ) { - //Debug(2,"Have no in_frame"); - if ( zm_packet->packet.size ) { - //Debug(2,"Decoding"); - if ( ! zm_packet->decode( video_in_ctx ) ) { - Debug(2, "unable to decode yet."); + if ( ! zm_packet->in_frame ) { + //Debug(2,"Have no in_frame"); + if ( zm_packet->packet.size ) { + //Debug(2,"Decoding"); + if ( ! zm_packet->decode( video_in_ctx ) ) { + Debug(2, "unable to decode yet."); + return 0; + } + //Go straight to out frame + swscale.Convert( zm_packet->in_frame, out_frame ); + } else if ( zm_packet->image ) { + //Debug(2,"Have an image, convert it"); + //Go straight to out frame + swscale.Convert( + zm_packet->image, + zm_packet->buffer, + codec_imgsize, + (AVPixelFormat)zm_packet->image->AVPixFormat(), + video_out_ctx->pix_fmt, + video_out_ctx->width, + video_out_ctx->height + ); + + } else { + Error("Have neither in_frame or image!"); + return 0; + } // end if has packet or image + } else { + // Have in_frame.... may need to convert it to out_frame + swscale.Convert(zm_packet->in_frame, zm_packet->out_frame); + } // end if no in_frame + } // end if no out_frame + + zm_packet->out_frame->coded_picture_number = frame_count; + zm_packet->out_frame->display_picture_number = frame_count; + zm_packet->out_frame->sample_aspect_ratio = (AVRational){ 0, 1 }; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + zm_packet->out_frame->pkt_duration = 0; +#endif + + if ( ! video_start_pts ) { + uint64_t temp = zm_packet->timestamp->tv_sec*(uint64_t)1000000; + video_start_pts = temp + zm_packet->timestamp->tv_usec; + Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d=>%" PRId64 ") usecs(%d)", + video_start_pts, zm_packet->timestamp->tv_sec, temp, zm_packet->timestamp->tv_usec ); + Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d) usecs(%d)", + video_start_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); + zm_packet->out_frame->pts = 0; + zm_packet->out_frame->coded_picture_number = 0; + } else { + uint64_t seconds = ( zm_packet->timestamp->tv_sec*(uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_start_pts; + zm_packet->out_frame->pts = av_rescale_q( seconds, video_in_stream->time_base, video_out_ctx->time_base); + + //zm_packet->out_frame->pkt_duration = zm_packet->out_frame->pts - video_start_pts; + Debug(2, " Setting pts for frame(%d), set to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", + frame_count, zm_packet->out_frame->pts, video_start_pts, seconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); + } + if ( zm_packet->keyframe ) { + //Debug(2, "Setting keyframe was (%d)", zm_packet->out_frame->key_frame ); + zm_packet->out_frame->key_frame = 1; + //Debug(2, "Setting keyframe (%d)", zm_packet->out_frame->key_frame ); + } else { + Debug(2, "Not Setting keyframe"); + } + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + // Do this to allow the encoder to choose whether to use I/P/B frame + zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE; + Debug(4, "Sending frame"); + if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->out_frame)) < 0 ) { + Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); + return -1; + } + + av_init_packet(&opkt); + opkt.data = NULL; + opkt.size = 0; + if ( (ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0 ) { + zm_av_packet_unref(&opkt); + if ( AVERROR(EAGAIN) == ret ) { + // THe codec may need more samples than it has, perfectly valid + Debug(3, "Could not recieve packet (error '%s')", + av_make_error_string(ret).c_str()); + return 0; + } else { + Error("Could not recieve packet (error %d = '%s')", ret, + av_make_error_string(ret).c_str()); + } + return -1; + } + //Debug(2, "Got packet using receive_packet, dts:%" PRId64 ", pts:%" PRId64 ", keyframe:%d", opkt.dts, opkt.pts, opkt.flags & AV_PKT_FLAG_KEY ); +#else + av_init_packet(&opkt); + opkt.data = NULL; + opkt.size = 0; + int data_present; + if ( (ret = avcodec_encode_video2( + video_out_ctx, &opkt, zm_packet->out_frame, &data_present)) < 0) { + Error("Could not encode frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&opkt); return 0; } - //Go straight to out frame - swscale.Convert( zm_packet->in_frame, out_frame ); - } else if ( zm_packet->image ) { - //Debug(2,"Have an image, convert it"); - //Go straight to out frame - swscale.Convert( - zm_packet->image, - zm_packet->buffer, - codec_imgsize, - (AVPixelFormat)zm_packet->image->AVPixFormat(), - video_out_ctx->pix_fmt, - video_out_ctx->width, - video_out_ctx->height - ); + if ( !data_present ) { + Debug(2, "Not ready to out a frame yet."); + zm_av_packet_unref(&opkt); + return 0; + } +#endif + // Need to adjust pts/dts values from codec time to stream time + if ( opkt.pts != AV_NOPTS_VALUE) + opkt.pts = av_rescale_q(opkt.pts, video_out_ctx->time_base, video_out_stream->time_base); + if ( opkt.dts != AV_NOPTS_VALUE) + opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); } else { - Error("Have neither in_frame or image!"); - return 0; - } // end if has packet or image - } else { - // Have in_frame.... may need to convert it to out_frame - swscale.Convert(zm_packet->in_frame, zm_packet->out_frame); - } // end if no in_frame - } // end if no out_frame + AVPacket *ipkt = &zm_packet->packet; + Debug(3, "Doing passthrough, just copy packet"); + // Just copy it because the codec is the same + av_init_packet(&opkt); + opkt.data = ipkt->data; + opkt.size = ipkt->size; + opkt.flags = ipkt->flags; + if ( ! video_start_pts ) { + // If video has b-frames, pts can be AV_NOPTS_VALUE so use dts intead + video_start_pts = ipkt->dts; + Debug(2, "No video_lsat_pts, set to (%" PRId64 ")", video_start_pts ); + opkt.dts = opkt.pts = 0; + } else { + dumpPacket(ipkt); + if ( ipkt->pts != AV_NOPTS_VALUE ) + opkt.pts = av_rescale_q( ipkt->pts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); + else + opkt.pts = 0; - zm_packet->out_frame->coded_picture_number = frame_count; - zm_packet->out_frame->display_picture_number = frame_count; - zm_packet->out_frame->sample_aspect_ratio = (AVRational){ 0, 1 }; + if ( ipkt->dts != AV_NOPTS_VALUE ) + opkt.dts = av_rescale_q( ipkt->dts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); + else + opkt.dts = 0; + Debug(2, "out_stream_time_base(%d/%d) in_stream_time_base(%d/%d) video_start_pts(%" PRId64 ")", + video_out_stream->time_base.num, video_out_stream->time_base.den, + video_in_stream->time_base.num, video_in_stream->time_base.den, + video_start_pts + ); + + dumpPacket(&opkt); + + opkt.duration = av_rescale_q( opkt.duration, video_in_stream->time_base, video_out_stream->time_base); + } + } + + //opkt.duration = 0; + + write_video_packet( opkt ); + zm_av_packet_unref(&opkt); + + return 1; + } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) + + void VideoStore::write_video_packet( AVPacket &opkt ) { + + if ( opkt.dts > opkt.pts ) { + Debug(1, + "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " + "before presentation.", + opkt.dts, opkt.pts); + opkt.dts = opkt.pts; + } + + opkt.stream_index = video_out_stream->index; + //av_packet_rescale_ts( &opkt, video_out_ctx->time_base, video_out_stream->time_base ); + + dumpPacket(&opkt, "writing video packet"); + + if ( (opkt.data == NULL) || (opkt.size < 1) ) { + Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__); + //dumpPacket(&opkt); + + //} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) { + //Warning("%s:%d: DTS out of order: next:%lld \u226E opkt.dts %lld; discarding frame", + //__FILE__, __LINE__, video_next_dts, opkt.dts); + //video_next_dts = opkt.dts; + //dumpPacket(&opkt); + + } else { + if ( (ret = av_interleaved_write_frame(oc, &opkt)) < 0 ) { + // There's nothing we can really do if the frame is rejected, just drop it + // and get on with the next + Warning( + "%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) " + " ", + __FILE__, __LINE__, av_make_error_string(ret).c_str(), ret); + + //dumpPacket(&safepkt); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - zm_packet->out_frame->pkt_duration = 0; + if ( video_in_stream ) + zm_dump_codecpar(video_in_stream->codecpar); + zm_dump_codecpar(video_out_stream->codecpar); #endif - - if ( ! video_start_pts ) { - uint64_t temp = zm_packet->timestamp->tv_sec*(uint64_t)1000000; - video_start_pts = temp + zm_packet->timestamp->tv_usec; - Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d=>%" PRId64 ") usecs(%d)", - video_start_pts, zm_packet->timestamp->tv_sec, temp, zm_packet->timestamp->tv_usec ); - Debug(2, "No video_lsat_pts, set to (%" PRId64 ") secs(%d) usecs(%d)", - video_start_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); - zm_packet->out_frame->pts = 0; - zm_packet->out_frame->coded_picture_number = 0; - } else { - uint64_t seconds = ( zm_packet->timestamp->tv_sec*(uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_start_pts; - zm_packet->out_frame->pts = av_rescale_q( seconds, video_in_stream->time_base, video_out_ctx->time_base); - - //zm_packet->out_frame->pkt_duration = zm_packet->out_frame->pts - video_start_pts; - Debug(2, " Setting pts for frame(%d), set to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", - frame_count, zm_packet->out_frame->pts, video_start_pts, seconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec ); - } - if ( zm_packet->keyframe ) { - //Debug(2, "Setting keyframe was (%d)", zm_packet->out_frame->key_frame ); - zm_packet->out_frame->key_frame = 1; - //Debug(2, "Setting keyframe (%d)", zm_packet->out_frame->key_frame ); - } else { - Debug(2, "Not Setting keyframe"); - } - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // Do this to allow the encoder to choose whether to use I/P/B frame - zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE; - Debug(4, "Sending frame"); - if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->out_frame)) < 0 ) { - Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); - return -1; - } - - av_init_packet(&opkt); - opkt.data = NULL; - opkt.size = 0; - if ( (ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0 ) { - zm_av_packet_unref(&opkt); - if ( AVERROR(EAGAIN) == ret ) { - // THe codec may need more samples than it has, perfectly valid - Debug(3, "Could not recieve packet (error '%s')", - av_make_error_string(ret).c_str()); - return 0; - } else { - Error("Could not recieve packet (error %d = '%s')", ret, - av_make_error_string(ret).c_str()); + } else { + packets_written += 1; + } } - return -1; - } -//Debug(2, "Got packet using receive_packet, dts:%" PRId64 ", pts:%" PRId64 ", keyframe:%d", opkt.dts, opkt.pts, opkt.flags & AV_PKT_FLAG_KEY ); -#else - av_init_packet(&opkt); - opkt.data = NULL; - opkt.size = 0; - int data_present; - if ( (ret = avcodec_encode_video2( - video_out_ctx, &opkt, zm_packet->out_frame, &data_present)) < 0) { - Error("Could not encode frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to out a frame yet."); - zm_av_packet_unref(&opkt); - return 0; - } -#endif - // Need to adjust pts/dts values from codec time to stream time - if ( opkt.pts != AV_NOPTS_VALUE) - opkt.pts = av_rescale_q(opkt.pts, video_out_ctx->time_base, video_out_stream->time_base); - if ( opkt.dts != AV_NOPTS_VALUE) - opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); - } else { - AVPacket *ipkt = &zm_packet->packet; - Debug(3, "Doing passthrough, just copy packet"); - // Just copy it because the codec is the same - av_init_packet(&opkt); - opkt.data = ipkt->data; - opkt.size = ipkt->size; - opkt.flags = ipkt->flags; - if ( ! video_start_pts ) { - // If video has b-frames, pts can be AV_NOPTS_VALUE so use dts intead - video_start_pts = ipkt->dts; - Debug(2, "No video_lsat_pts, set to (%" PRId64 ")", video_start_pts ); - opkt.dts = opkt.pts = 0; - } else { - dumpPacket(ipkt); - if ( ipkt->pts != AV_NOPTS_VALUE ) - opkt.pts = av_rescale_q( ipkt->pts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); - else - opkt.pts = 0; + } // end void VideoStore::write_video_packet - if ( ipkt->dts != AV_NOPTS_VALUE ) - opkt.dts = av_rescale_q( ipkt->dts - video_start_pts, video_in_stream->time_base, video_out_stream->time_base ); - else - opkt.dts = 0; - Debug(2, "out_stream_time_base(%d/%d) in_stream_time_base(%d/%d) video_start_pts(%" PRId64 ")", - video_out_stream->time_base.num, video_out_stream->time_base.den, - video_in_stream->time_base.num, video_in_stream->time_base.den, - video_start_pts - ); + int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) { + Debug(4, "writeAudioFrame"); - dumpPacket(&opkt); + AVPacket *ipkt = &zm_packet->packet; - opkt.duration = av_rescale_q( opkt.duration, video_in_stream->time_base, video_out_stream->time_base); - } - } + if ( !audio_out_stream ) { + Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); + return 0; // FIXME -ve return codes do not free packet in ffmpeg_camera at + // the moment + } - //opkt.duration = 0; - - write_video_packet( opkt ); - zm_av_packet_unref(&opkt); - - return 1; -} // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) - -void VideoStore::write_video_packet( AVPacket &opkt ) { - - if ( opkt.dts > opkt.pts ) { - Debug(1, - "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " - "before presentation.", - opkt.dts, opkt.pts); - opkt.dts = opkt.pts; - } - - opkt.stream_index = video_out_stream->index; - //av_packet_rescale_ts( &opkt, video_out_ctx->time_base, video_out_stream->time_base ); - - dumpPacket(&opkt, "writing video packet"); - - if ( (opkt.data == NULL) || (opkt.size < 1) ) { - Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__); - //dumpPacket(&opkt); - - //} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) { - //Warning("%s:%d: DTS out of order: next:%lld \u226E opkt.dts %lld; discarding frame", - //__FILE__, __LINE__, video_next_dts, opkt.dts); - //video_next_dts = opkt.dts; - //dumpPacket(&opkt); - - } else { - if ( (ret = av_interleaved_write_frame(oc, &opkt)) < 0 ) { - // There's nothing we can really do if the frame is rejected, just drop it - // and get on with the next - Warning( - "%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) " - " ", - __FILE__, __LINE__, av_make_error_string(ret).c_str(), ret); - - //dumpPacket(&safepkt); -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( video_in_stream ) - zm_dump_codecpar(video_in_stream->codecpar); - zm_dump_codecpar(video_out_stream->codecpar); -#endif - } else { - packets_written += 1; - } - } - -} // end void VideoStore::write_video_packet - -int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) { - Debug(4, "writeAudioFrame"); - - AVPacket *ipkt = &zm_packet->packet; - - if ( !audio_out_stream ) { - Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); - return 0; // FIXME -ve return codes do not free packet in ffmpeg_camera at - // the moment - } - - if ( audio_out_codec ) { - Debug(3, "Have audio codec"); + if ( audio_out_codec ) { + Debug(3, "Have audio codec"); #ifdef HAVE_LIBAVRESAMPLE #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( (ret = avcodec_send_packet(audio_in_ctx, ipkt)) < 0 ) { - Error("avcodec_send_packet fail %s", av_make_error_string(ret).c_str()); - return 0; - } - if ( (ret = avcodec_receive_frame(audio_in_ctx, in_frame)) < 0 ) { - Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str()); - return 0; - } - Debug(2, - "Input Frame: samples(%d), format(%d), sample_rate(%d), channel " - "layout(%d)", - in_frame->nb_samples, in_frame->format, - in_frame->sample_rate, in_frame->channel_layout); + if ( (ret = avcodec_send_packet(audio_in_ctx, ipkt)) < 0 ) { + Error("avcodec_send_packet fail %s", av_make_error_string(ret).c_str()); + return 0; + } + if ( (ret = avcodec_receive_frame(audio_in_ctx, in_frame)) < 0 ) { + Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str()); + return 0; + } + Debug(2, + "Input Frame: samples(%d), format(%d), sample_rate(%d), channel " + "layout(%d)", + in_frame->nb_samples, in_frame->format, + in_frame->sample_rate, in_frame->channel_layout); #else - /** - * Decode the audio frame stored in the packet. - * The in audio stream decoder is used to do this. - * If we are at the end of the file, pass an empty packet to the decoder - * to flush it. - */ - int data_present; - if ( (ret = avcodec_decode_audio4(audio_in_ctx, in_frame, - &data_present, ipkt)) < 0 ) { - Error("Could not decode frame (error '%s')\n", - av_make_error_string(ret).c_str()); - dumpPacket(ipkt); - av_frame_free(&in_frame); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to transcode a frame yet."); - return 0; - } + /** + * Decode the audio frame stored in the packet. + * The in audio stream decoder is used to do this. + * If we are at the end of the file, pass an empty packet to the decoder + * to flush it. + */ + int data_present; + if ( (ret = avcodec_decode_audio4(audio_in_ctx, in_frame, + &data_present, ipkt)) < 0 ) { + Error("Could not decode frame (error '%s')\n", + av_make_error_string(ret).c_str()); + dumpPacket(ipkt); + av_frame_free(&in_frame); + return 0; + } + if ( !data_present ) { + Debug(2, "Not ready to transcode a frame yet."); + return 0; + } #endif - int frame_size = out_frame->nb_samples; - in_frame->pts = audio_next_pts; + int frame_size = out_frame->nb_samples; + in_frame->pts = audio_next_pts; - // Resample the in into the audioSampleBuffer until we proceed the whole - // decoded data - if ((ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, - 0, in_frame->nb_samples)) < 0) { - Error("Could not resample frame (error '%s')\n", - av_make_error_string(ret).c_str()); - av_frame_unref(in_frame); - return 0; - } - av_frame_unref(in_frame); + // Resample the in into the audioSampleBuffer until we proceed the whole + // decoded data + if ((ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, + 0, in_frame->nb_samples)) < 0) { + Error("Could not resample frame (error '%s')\n", + av_make_error_string(ret).c_str()); + av_frame_unref(in_frame); + return 0; + } + av_frame_unref(in_frame); - int samples_available = avresample_available(resample_ctx); - if ( samples_available < frame_size ) { - Debug(1, "Not enough samples yet (%d)", samples_available); - return 0; - } + int samples_available = avresample_available(resample_ctx); + if ( samples_available < frame_size ) { + Debug(1, "Not enough samples yet (%d)", samples_available); + return 0; + } - Debug(3, "Output_frame samples (%d)", out_frame->nb_samples); - // Read a frame audio data from the resample fifo - if ( avresample_read(resample_ctx, out_frame->data, frame_size) != frame_size) { - Warning("Error reading resampled audio: "); - return 0; - } - Debug(2, - "Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d)", - out_frame->nb_samples, out_frame->format, - out_frame->sample_rate, out_frame->channel_layout); + Debug(3, "Output_frame samples (%d)", out_frame->nb_samples); + // Read a frame audio data from the resample fifo + if ( avresample_read(resample_ctx, out_frame->data, frame_size) != frame_size) { + Warning("Error reading resampled audio: "); + return 0; + } + Debug(2, + "Frame: samples(%d), format(%d), sample_rate(%d), channel layout(%d)", + out_frame->nb_samples, out_frame->format, + out_frame->sample_rate, out_frame->channel_layout); - av_init_packet(&opkt); - Debug(5, "after init packet"); + av_init_packet(&opkt); + Debug(5, "after init packet"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ((ret = avcodec_send_frame(audio_out_ctx, out_frame)) < 0) { - Error("Could not send frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } + if ((ret = avcodec_send_frame(audio_out_ctx, out_frame)) < 0) { + Error("Could not send frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&opkt); + return 0; + } - // av_frame_unref( out_frame ); + // av_frame_unref( out_frame ); - if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { - if ( AVERROR(EAGAIN) == ret ) { - // THe codec may need more samples than it has, perfectly valid - Debug(3, "Could not recieve packet (error '%s')", - av_make_error_string(ret).c_str()); - } else { - Error("Could not recieve packet (error %d = '%s')", ret, - av_make_error_string(ret).c_str()); - } - //zm_av_packet_unref(&opkt); - av_frame_unref(in_frame); - // av_frame_unref( out_frame ); - return 0; - } + if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { + if ( AVERROR(EAGAIN) == ret ) { + // THe codec may need more samples than it has, perfectly valid + Debug(3, "Could not recieve packet (error '%s')", + av_make_error_string(ret).c_str()); + } else { + Error("Could not recieve packet (error %d = '%s')", ret, + av_make_error_string(ret).c_str()); + } + //zm_av_packet_unref(&opkt); + av_frame_unref(in_frame); + // av_frame_unref( out_frame ); + return 0; + } #else - if ( (ret = avcodec_encode_audio2(audio_out_ctx, &opkt, out_frame, - &data_present)) < 0) { - Error("Could not encode frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to out a frame yet."); - zm_av_packet_unref(&opkt); - return 0; - } + if ( (ret = avcodec_encode_audio2(audio_out_ctx, &opkt, out_frame, + &data_present)) < 0) { + Error("Could not encode frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&opkt); + return 0; + } + if ( !data_present ) { + Debug(2, "Not ready to out a frame yet."); + zm_av_packet_unref(&opkt); + return 0; + } #endif #endif - opkt.duration = out_frame->nb_samples; - } else { - av_init_packet(&opkt); - Debug(5, "after init packet"); - opkt.data = ipkt->data; - opkt.size = ipkt->size; - opkt.duration = ipkt->duration; - } + opkt.duration = out_frame->nb_samples; + } else { + av_init_packet(&opkt); + Debug(5, "after init packet"); + opkt.data = ipkt->data; + opkt.size = ipkt->size; + opkt.duration = ipkt->duration; + } -// PTS is difficult, because of the buffering of the audio packets in the -// resampler. So we have to do it once we actually have a packet... -// audio_last_pts is the pts of ipkt, audio_next_pts is the last pts of the -// out + // PTS is difficult, because of the buffering of the audio packets in the + // resampler. So we have to do it once we actually have a packet... + // audio_last_pts is the pts of ipkt, audio_next_pts is the last pts of the + // out -// Scale the PTS of the outgoing packet to be the correct time base + // Scale the PTS of the outgoing packet to be the correct time base #if 0 - if ( ipkt->pts != AV_NOPTS_VALUE ) { - if ( !audio_last_pts ) { - opkt.pts = 0; - Debug(1, "No audio_last_pts"); - } else { - if ( audio_last_pts > ipkt->pts ) { - Debug(1, "Resetting audio_start_pts from (%d) to (%d)", audio_last_pts, ipkt->pts); - opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts, audio_in_stream->time_base, audio_out_stream->time_base); - } else { - opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts - audio_last_pts, audio_in_stream->time_base, audio_out_stream->time_base); - } - Debug(2, "audio opkt.pts = %d from ipkt->pts(%d) - last_pts(%d)", opkt.pts, ipkt->pts, audio_last_pts); - } - audio_last_pts = ipkt->pts; - } else { - Debug(2, "opkt.pts = undef"); - opkt.pts = AV_NOPTS_VALUE; - } + if ( ipkt->pts != AV_NOPTS_VALUE ) { + if ( !audio_last_pts ) { + opkt.pts = 0; + Debug(1, "No audio_last_pts"); + } else { + if ( audio_last_pts > ipkt->pts ) { + Debug(1, "Resetting audio_start_pts from (%d) to (%d)", audio_last_pts, ipkt->pts); + opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts, audio_in_stream->time_base, audio_out_stream->time_base); + } else { + opkt.pts = audio_next_pts + av_rescale_q(ipkt->pts - audio_last_pts, audio_in_stream->time_base, audio_out_stream->time_base); + } + Debug(2, "audio opkt.pts = %d from ipkt->pts(%d) - last_pts(%d)", opkt.pts, ipkt->pts, audio_last_pts); + } + audio_last_pts = ipkt->pts; + } else { + Debug(2, "opkt.pts = undef"); + opkt.pts = AV_NOPTS_VALUE; + } #else - opkt.pts = audio_next_pts; - opkt.dts = audio_next_dts; + opkt.pts = audio_next_pts; + opkt.dts = audio_next_dts; #endif #if 0 - if ( ipkt->dts == AV_NOPTS_VALUE ) { - // So if the in has no dts assigned... still need an out dts... so we use cur_dts? + if ( ipkt->dts == AV_NOPTS_VALUE ) { + // So if the in has no dts assigned... still need an out dts... so we use cur_dts? - if ( audio_last_dts >= audio_in_stream->cur_dts ) { - Debug(1, "Resetting audio_last_dts from (%d) to cur_dts (%d)", audio_last_dts, audio_in_stream->cur_dts); - opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); - } else { - opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts - audio_last_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); + if ( audio_last_dts >= audio_in_stream->cur_dts ) { + Debug(1, "Resetting audio_last_dts from (%d) to cur_dts (%d)", audio_last_dts, audio_in_stream->cur_dts); + opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); + } else { + opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts - audio_last_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); + } + audio_last_dts = audio_in_stream->cur_dts; + Debug(2, "opkt.dts = %d from video_in_stream->cur_dts(%d) - last_dts(%d)", opkt.dts, audio_in_stream->cur_dts, audio_last_dts); + } else { + if ( audio_last_dts >= ipkt->dts ) { + Debug(1, "Resetting audio_last_dts from (%d) to (%d)", audio_last_dts, ipkt->dts ); + opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts, audio_in_stream->time_base, audio_out_stream->time_base); + } else { + opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts - audio_last_dts, audio_in_stream->time_base, audio_out_stream->time_base); + Debug(2, "opkt.dts = %d from previous(%d) + ( ipkt->dts(%d) - last_dts(%d) )", opkt.dts, audio_next_dts, ipkt->dts, audio_last_dts ); + } + } } - audio_last_dts = audio_in_stream->cur_dts; - Debug(2, "opkt.dts = %d from video_in_stream->cur_dts(%d) - last_dts(%d)", opkt.dts, audio_in_stream->cur_dts, audio_last_dts); - } else { - if ( audio_last_dts >= ipkt->dts ) { - Debug(1, "Resetting audio_last_dts from (%d) to (%d)", audio_last_dts, ipkt->dts ); - opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts, audio_in_stream->time_base, audio_out_stream->time_base); - } else { - opkt.dts = audio_next_dts + av_rescale_q(ipkt->dts - audio_last_dts, audio_in_stream->time_base, audio_out_stream->time_base); - Debug(2, "opkt.dts = %d from previous(%d) + ( ipkt->dts(%d) - last_dts(%d) )", opkt.dts, audio_next_dts, ipkt->dts, audio_last_dts ); - } - } - } #endif - // audio_last_dts = ipkt->dts; - if ( opkt.dts > opkt.pts ) { - Debug(1, - "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " - "before presentation.", - opkt.dts, opkt.pts); - opkt.dts = opkt.pts; - } + // audio_last_dts = ipkt->dts; + if ( opkt.dts > opkt.pts ) { + Debug(1, + "opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 "). Decompression must happen " + "before presentation.", + opkt.dts, opkt.pts); + opkt.dts = opkt.pts; + } - //opkt.duration = out_frame ? out_frame->nb_samples : ipkt->duration; - // opkt.duration = av_rescale_q(ipkt->duration, audio_in_stream->time_base, - // audio_out_stream->time_base); - dumpPacket(&opkt); + //opkt.duration = out_frame ? out_frame->nb_samples : ipkt->duration; + // opkt.duration = av_rescale_q(ipkt->duration, audio_in_stream->time_base, + // audio_out_stream->time_base); + dumpPacket(&opkt); - // pkt.pos: byte position in stream, -1 if unknown - opkt.pos = -1; - opkt.stream_index = audio_out_stream->index; - audio_next_dts = opkt.dts + opkt.duration; - audio_next_pts = opkt.pts + opkt.duration; + // pkt.pos: byte position in stream, -1 if unknown + opkt.pos = -1; + opkt.stream_index = audio_out_stream->index; + audio_next_dts = opkt.dts + opkt.duration; + audio_next_pts = opkt.pts + opkt.duration; - AVPacket safepkt; - memcpy(&safepkt, &opkt, sizeof(AVPacket)); - ret = av_interleaved_write_frame(oc, &opkt); - if ( ret != 0 ) { - Error("Error writing audio frame packet: %s\n", - av_make_error_string(ret).c_str()); - dumpPacket(&safepkt); - } else { - Debug(2, "Success writing audio frame"); - } - zm_av_packet_unref(&opkt); - return 1; -} // end int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) + AVPacket safepkt; + memcpy(&safepkt, &opkt, sizeof(AVPacket)); + ret = av_interleaved_write_frame(oc, &opkt); + if ( ret != 0 ) { + Error("Error writing audio frame packet: %s\n", + av_make_error_string(ret).c_str()); + dumpPacket(&safepkt); + } else { + Debug(2, "Success writing audio frame"); + } + zm_av_packet_unref(&opkt); + return 1; + } // end int VideoStore::writeAudioFramePacket( AVPacket *ipkt ) -int VideoStore::write_packets( zm_packetqueue &queue ) { - // Need to write out all the frames from the last keyframe? - // No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. - unsigned int packet_count = 0; - ZMPacket *queued_packet; + int VideoStore::write_packets( zm_packetqueue &queue ) { + // Need to write out all the frames from the last keyframe? + // No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. + unsigned int packet_count = 0; + ZMPacket *queued_packet; - while ( ( queued_packet = queue.popPacket() ) ) { - AVPacket *avp = queued_packet->av_packet(); + while ( ( queued_packet = queue.popPacket() ) ) { + AVPacket *avp = queued_packet->av_packet(); - packet_count += 1; - //Write the packet to our video store - Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", - avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, queue.size() ); - int ret = this->writePacket( queued_packet ); - if ( ret < 0 ) { - //Less than zero and we skipped a frame - } - delete queued_packet; - } // end while packets in the packetqueue - Debug(2, "Wrote %d queued packets", packet_count ); - return packet_count; -} // end int VideoStore::write_packets( PacketQueue &queue ) { + packet_count += 1; + //Write the packet to our video store + Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", + avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, queue.size() ); + int ret = this->writePacket( queued_packet ); + if ( ret < 0 ) { + //Less than zero and we skipped a frame + } + delete queued_packet; + } // end while packets in the packetqueue + Debug(2, "Wrote %d queued packets", packet_count ); + return packet_count; + } // end int VideoStore::write_packets( PacketQueue &queue ) { diff --git a/src/zm_videostore.h b/src/zm_videostore.h index d2b14ae2e..cf8168c06 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -19,6 +19,15 @@ class VideoStore; class VideoStore { private: +struct CodecData { + const int codec_id; + const char *codec_codec; + const char *codec_name; + const enum AVPixelFormat pix_fmt; + +}; +static struct CodecData codec_data[]; + Monitor *monitor; AVOutputFormat *out_format; AVFormatContext *oc; diff --git a/version b/version index 6367ac8f2..0be5d1204 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.39 +1.31.40 diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index a4f5f03db..942f25c87 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -15,7 +15,8 @@ private $defaults = array( 'Height' => null, 'Orientation' => null, 'AnalysisFPSLimit' => null, - 'OutputCodec' => 'h264', + 'OutputCodec' => '0', + 'Encoder' => 'auto', 'OutputContainer' => 'auto', 'ZoneCount' => 0, 'Triggers' => null, diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 50aaad267..d00496f93 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -470,13 +470,21 @@ $videowriteropts = array( 'X264 Encode' => 1, 'H264 Camera Passthrough' => 2 ); -$videowriter_codecs = array( - '' => translate('Disabled'), +$videowriter_encoders = array( + '' => translate('Auto'), + 'h264_omx' => 'h264_omx', 'h264' => 'h264', 'mjpeg' => 'mjpeg', 'mpeg1' => 'mpeg1', 'mpeg2' => 'mpeg2', ); +$videowriter_codecs = array( + '0' => translate('Disabled'), + '220' => 'h264', + '8' => 'mjpeg', + '1' => 'mpeg1', + '2' => 'mpeg2', +); $videowriter_containers = array( '' => translate('Auto'), 'mp4' => 'mp4', @@ -609,6 +617,7 @@ if ( $tab != 'storage' ) { + @@ -912,6 +921,7 @@ if ( $monitor->Type() == 'Local' ) { OutputCodec() );?> + Encoder() );?> OutputContainer() );?> RecordAudio() ) { ?> checked="checked"/> From a19e6b619c293580b0639909196f33ca5e0134fd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Feb 2018 07:17:52 -0800 Subject: [PATCH 055/538] add Encoder to Monitors --- db/zm_update-1.31.40.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 db/zm_update-1.31.40.sql diff --git a/db/zm_update-1.31.40.sql b/db/zm_update-1.31.40.sql new file mode 100644 index 000000000..66b98eb66 --- /dev/null +++ b/db/zm_update-1.31.40.sql @@ -0,0 +1,14 @@ +ALTER TABLE `Monitors` MODIFY `OutputCodec` int(10) UNSIGNED NOT NULL default 0; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Monitors' + AND column_name = 'Encoder' + ) > 0, +"SELECT 'Column Encoder already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2') AFTER `OutputCodec`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + From dc074bd2c00c5fc1e23722d2747c5a177c68b0c4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Feb 2018 07:18:38 -0800 Subject: [PATCH 056/538] fix path to triggers.sql --- db/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt index fda03b463..c68285e5b 100644 --- a/db/CMakeLists.txt +++ b/db/CMakeLists.txt @@ -13,4 +13,4 @@ install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") # install triggers.sql -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") From 0077f2e17bbd6f34e6822c5ac4c1e42fa9e868db Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 28 Feb 2018 11:06:28 -0500 Subject: [PATCH 057/538] Update faq.rst I see no reason to mention bits. No one cares about bits. Our base unit should be bytes. Also, lets be consistent and use 1024 as the divisor when converting to Kb and Mb and Gb. --- docs/faq.rst | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 87421ddb9..d6694b153 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -71,22 +71,22 @@ The 1.2 at the start is basically adding 20% on top of the calculation to accoun The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space: :: - 1280*960 = 1,228,800 (bits) - 1,228,800 * 24 = 2,359,296,000 (bits) - 2,359,296,000 * 50 = 5,898,240,000 (bits) - 5,898,240,000 * 4 = 7,077,888,000 (bits) - 7,077,888,000 / 8 = 884,736,000 (bytes) - 884,736,000 / 1000 = 884,736 (Kilobytes) - 884,736 / 1000 = 864 (Megabytes) - 864 / 1000 = 0.9 (Gigabyte) + 1280*960 = 1,228,800 (bytes) + 1,228,800 * (3 bytes for 24 bit) = 3,686,400 (bytes) + 3,686,400 * 50 = 184,320,000 (bytes) + 184,320,000 * 4 = 737,280,000 (bytes) + 737,280,000 / 1024 = 720,000 (Kilobytes) + 720,000 / 1024 = 703.125 (Megabytes) + 703.125 / 1024 = 0.686 (Gigabytes) -Around 900MB of memory. +Around 700MB of memory. So if you have 2GB of memory, you should be all set. Right? **Not, really**: * This is just the base memory required to capture the streams. Remember ZM is always capturing streams irrespective of whether you are actually recording or not - to make sure its image ring buffer is there with pre images when an alarm kicks in. * You also need to account for other processes not related to ZM running in your box * You also need to account for other ZM processes - for example, I noticed the audit daemon takes up a good amount of memory when it runs, DB updates also take up memory + * If you are using H264 encoding, that buffers a lot of frames in memory as well. So a good rule of thumb is to make sure you have twice the memory as the calculation above (and if you are using the ZM server for other purposes, please factor in those memory requirements as well) @@ -128,15 +128,14 @@ So, for example: :: 384x288 capture resolution, that makes: 110 592 pixels - in 24 bit color that's x24 = 2 654 208 bits per frame - by 80 frames ring buffer x80 = 212 336 640 bits per camera - by 4 cameras x4 = 849 346 560 bits. - Plus 10% overhead = 934 281 216 bits - That's 116 785 152 bytes, and - = 114 048 kB, respectively 111.38 MB. - If my shared memory is set to 134 217 728, which is exactly 128MB, + in 24 bit color that's x 3 = 331,776 bytes per frame + by 80 frames ring buffer x80 = 26,542,080 bytes per camera + by 4 cameras x4 = 106,168,320 bytes. + Plus 10% overhead = 116,785,152 bytes + Thats 114,048 kB, respectively 111.38 MB. + If my shared memory is set to 134,217,728, which is exactly 128MB, that means I shouldn't have any problem. - (Note that 1 byte = 8 bits and 1kbyte = 1024bytes, 1MB = 1024 kB) + (Note that 1kbyte = 1024bytes, 1MB = 1024 kB) If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here `__ From 865649d785378d68c78b639505f2419b1fae38ac Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:20:00 -0800 Subject: [PATCH 058/538] Turn on c++11 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b8308afc..d39c97d4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ if(NOT HOST_OS) "ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") endif(NOT HOST_OS) +set (CMAKE_CXX_STANDARD 11) # Default CLFAGS and CXXFLAGS: set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") From b3d23ab2337c8697c7984ada47bbfaa1e97964bb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:20:29 -0800 Subject: [PATCH 059/538] merge zma_to_thread version of db functions, which include a mutex lock --- src/zm_db.cpp | 25 +++++++++++++++++-------- src/zm_db.h | 35 ++++++++++++++--------------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index fb716349e..fc5ee5fe0 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -23,29 +23,35 @@ #include "zm.h" #include "zm_db.h" +// From what I read, we need one of these per thread MYSQL dbconn; +Mutex db_mutex; -int zmDbConnected = false; +bool zmDbConnected = false; -void zmDbConnect() { +bool zmDbConnect() { // For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu //if ( zmDbConnected ) //return; if ( !mysql_init( &dbconn ) ) { Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + return false; } my_bool reconnect = 1; if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) - Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); + Error( "Can't set database auto reconnect option: %s", mysql_error( &dbconn) ); if ( !staticConfig.DB_SSL_CA_CERT.empty() ) - mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL ); + mysql_ssl_set( &dbconn, + staticConfig.DB_SSL_CLIENT_KEY.c_str(), + staticConfig.DB_SSL_CLIENT_CERT.c_str(), + staticConfig.DB_SSL_CA_CERT.c_str(), + NULL, NULL ); std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" ); if ( colonIndex == std::string::npos ) { if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) { Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + return false; } } else { std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); @@ -53,12 +59,12 @@ void zmDbConnect() { if ( dbPortOrSocket[0] == '/' ) { if ( !mysql_real_connect( &dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0 ) ) { Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + return false; } } else { if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) ) { Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + return false; } } } @@ -67,6 +73,7 @@ void zmDbConnect() { exit( mysql_errno( &dbconn ) ); } zmDbConnected = true; + return zmDbConnected; } void zmDbClose() { @@ -84,6 +91,7 @@ MYSQL_RES * zmDbFetch( const char * query ) { Error( "Not connected." ); return NULL; } + db_mutex.lock(); if ( mysql_query( &dbconn, query ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); @@ -95,6 +103,7 @@ MYSQL_RES * zmDbFetch( const char * query ) { Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query ); return NULL; } + db_mutex.unlock(); return result; } // end MYSQL_RES * zmDbFetch( const char * query ); diff --git a/src/zm_db.h b/src/zm_db.h index f9b158be8..267995e70 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -21,37 +21,30 @@ #define ZM_DB_H #include +#include "zm_thread.h" class zmDbRow { - private: - MYSQL_RES *result_set; - MYSQL_ROW row; - public: - zmDbRow() { result_set = NULL; row = NULL; }; - MYSQL_RES *fetch( const char *query ); - zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); - ~zmDbRow(); + private: + MYSQL_RES *result_set; + MYSQL_ROW row; + public: + zmDbRow() { result_set = NULL; row = NULL; }; + MYSQL_RES *fetch( const char *query ); + zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); + ~zmDbRow(); - char *operator[](unsigned int index) const { - return row[index]; - } + char *operator[](unsigned int index) const { + return row[index]; + } }; -#ifdef __cplusplus -extern "C" { -#endif extern MYSQL dbconn; +extern Mutex db_mutex; -extern int zmDbConnected; - -void zmDbConnect(); +bool zmDbConnect(); void zmDbClose(); MYSQL_RES * zmDbFetch( const char *query ); zmDbRow *zmDbFetchOne( const char *query ); -#ifdef __cplusplus -} /* extern "C" */ -#endif - #endif // ZM_DB_H From 1bc6e40fbc716d58c04fc90b37253e4467e299d5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:20:52 -0800 Subject: [PATCH 060/538] add mutex locking around mysql functions so that we can go multi-threaded --- src/zm_event.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index c37bd04ce..17c385c87 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -96,11 +96,14 @@ Event::Event( monitor->GetOptSaveJPEGs(), storage->SchemeString().c_str() ); + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql ); - exit( mysql_errno( &dbconn ) ); + db_mutex.unlock(); + return; } id = mysql_insert_id( &dbconn ); + db_mutex.unlock(); if ( untimedEvent ) { Warning( "Event %d has zero time, setting to current", id ); } @@ -110,7 +113,6 @@ Event::Event( tot_score = 0; max_score = 0; - struct stat statbuf; char id_file[PATH_MAX]; if ( storage->Scheme() == Storage::DEEP ) { @@ -236,10 +238,11 @@ Event::~Event() { snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); } + db_mutex.unlock(); } /* Close the video file */ @@ -259,10 +262,11 @@ Event::~Event() { } snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); } + db_mutex.unlock(); } void Event::createNotes( std::string ¬es ) { @@ -427,9 +431,11 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) { mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert event: %s", mysql_error( &dbconn ) ); } + db_mutex.unlock(); #endif } } @@ -490,9 +496,11 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st if ( frameCount ) { Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); *(sql+strlen(sql)-2) = '\0'; + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frames: %s, sql was (%s)", mysql_error( &dbconn ), sql ); } + db_mutex.unlock(); last_db_frame = frames; } else { Debug( 1, "No valid pre-capture frames to add" ); @@ -542,10 +550,13 @@ Debug(3, "Writing video"); Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] ); static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + db_mutex.unlock(); + return; } + db_mutex.unlock(); last_db_frame = frames; // We are writing a Bulk frame @@ -560,10 +571,11 @@ Debug(3, "Writing video"); max_score, id ); + db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); } + db_mutex.unlock(); } } // end if db_frame From 4d81b7b66bb49b1f3e9536dfbceb627bcacbd049 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:21:41 -0800 Subject: [PATCH 061/538] Do not just reconnect to the input stream on EOF. pts/dts gets outof whack when doing passthrough. Instead treat it as a loss of signal, which will end the event and start a new one --- src/zm_ffmpeg_camera.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index c43bb3389..fae261e65 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -710,20 +710,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event ret = av_read_frame( mFormatContext, &packet ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - if ( - // Check if EOF. - (ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || - // Check for Connection failure. - (ret == -110) - ) { - Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf); - if ( ReopenFfmpeg() < 0 ) { - // OpenFfmpeg will do enough logging. - return -1; - } - continue; - } - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf ); return -1; } From 28342b907b290b5967a2b7a40bcfcb77b0815343 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:22:22 -0800 Subject: [PATCH 062/538] add a thread for deleting the event. This is because writing out the final frames of an mp4 can take a long time, during which we are not analyzing. --- src/zm_monitor.cpp | 17 +++++++++++++++-- src/zm_monitor.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 58e758577..0828b497a 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -326,7 +326,8 @@ Monitor::Monitor( zones( p_zones ), timestamps( 0 ), images( 0 ), - privacy_bitmask( NULL ) + privacy_bitmask( NULL ), + event_delete_thread(NULL) { strncpy( name, p_name, sizeof(name)-1 ); @@ -599,6 +600,11 @@ Debug(3, "Success connecting"); } Monitor::~Monitor() { + if ( event_delete_thread ) { + event_delete_thread->join(); + delete event_delete_thread; + event_delete_thread = NULL; + } if ( timestamps ) { delete[] timestamps; timestamps = 0; @@ -3100,7 +3106,14 @@ bool Monitor::closeEvent() { if ( function == RECORD || function == MOCORD ) { gettimeofday( &(event->EndTime()), NULL ); } - delete event; + if ( event_delete_thread ) { + event_delete_thread->join(); + delete event_delete_thread; + event_delete_thread = NULL; + } + event_delete_thread = new std::thread([](Event *event) { + delete event; + }, event ); video_store_data->recording = (struct timeval){0}; event = 0; return( true ); diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 0de71c424..ff6ceca50 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -22,6 +22,7 @@ #include #include +#include #include "zm.h" #include "zm_coord.h" @@ -325,6 +326,7 @@ protected: Image **images; const unsigned char *privacy_bitmask; + std::thread *event_delete_thread; // Used to close events, but continue processing. int n_linked_monitors; MonitorLink **linked_monitors; From 9af6dc02bcb19f47ee0b79a5ee7eb68f0494319e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:22:47 -0800 Subject: [PATCH 063/538] better debug of packet pts/dts/duration --- src/zm_videostore.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index e074b1d9d..0e72a7c7e 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -721,8 +721,10 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { duration = av_rescale_q(ipkt->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base); - Debug(1, "duration calc: pts(%d) - last_pts(%d) = (%d)", ipkt->pts, - video_last_pts, duration); + Debug(1, "duration calc: pts(%" PRId64 ") - last_pts(% " PRId64 ") = (%" PRId64 ")", + ipkt->pts, + video_last_pts, + duration); if (duration < 0) { duration = ipkt->duration; } From debe4607bc71ad3160ccc3bdb27853071b3d78cd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:23:03 -0800 Subject: [PATCH 064/538] add Parent and Parents functions --- web/includes/Group.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/web/includes/Group.php b/web/includes/Group.php index f8836ebca..d6edba7ca 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -288,6 +288,23 @@ $group_options[$Group->Id()] = str_repeat( ' ', $depth ) . $Group->Name(); return $monitor_id; } +public function Parent( ) { + if ( $this->{'ParentId'} ) { + return new Group($this->{'ParentId'}); + } + return null; +} + +public function Parents() { + $Parents = array(); + $Parent = $this->Parent(); + while( $Parent ) { + array_unshift($Parents, $Parent); + $Parent = $Parent->Parent(); + } + return $Parents; +} + } # end class Group From 9a157bf4cabdfaafcf3d5ea07aa7db36cd3dc62d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:23:27 -0800 Subject: [PATCH 065/538] add Type, MaxFPS, AlarmMaxFPS to field defaults --- web/includes/Monitor.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 46de04aaa..52031824c 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -17,6 +17,9 @@ private $defaults = array( 'AnalysisFPSLimit' => null, 'ZoneCount' => 0, 'Triggers' => null, + 'Type' => 'Ffmpeg', + 'MaxFPS' => null, + 'AlarmMaxFPS' => null, ); private $status_fields = array( 'AnalysisFPS' => null, From 7e628bc3fdf0aabe9f5aeba4d2dacc3a720681e7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 1 Mar 2018 19:23:46 -0800 Subject: [PATCH 066/538] add showing groups under Monitor Name on console --- web/skins/classic/views/console.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 846404b60..3afc05820 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -183,6 +183,7 @@ xhtmlHeaders( __FILE__, translate('Console') ); - ' : '>') . $monitor['Name'] ?>
+ + ' : '>') . $monitor['Name'] ?>
+
+ ', + array_map(function($group_id){ + $Group = new Group($group_id); + return implode(' > ', array_map(function($Group){ return $Group->Name(); }, $Group->Parents())); + }, $Monitor->GroupIds() ) ); +?> + '.translate('Fn'.$monitor['Function']).( empty($monitor['Enabled']) ? ', disabled' : '' ) .'', canEdit( 'Monitors' ) ) ?>
Date: Fri, 2 Mar 2018 17:19:10 -0600 Subject: [PATCH 067/538] url to css/spinner.css should be relative fixes http 404 not found errors for this css file --- web/skins/classic/views/js/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index a7a8aa2c6..678cf900d 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -286,7 +286,7 @@ sortReverse: true warningPrefix: "", errorPrefix: "" }); - new Asset.css( "/css/spinner.css" ); + new Asset.css( "css/spinner.css" ); fetchNextLogs(); } From a6827c97abadb2910024b3965d1773da97d1dfb1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:24:39 -0800 Subject: [PATCH 068/538] Cleanup, add some sanity checks --- src/zm_eventstream.cpp | 55 ++++++++++++++++++++++++++++-------------- src/zm_eventstream.h | 16 ++---------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 821750216..69c840343 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -88,43 +88,48 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { } bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) { - loadEventData( init_event_id ); + loadEventData(init_event_id); if ( init_frame_id ) { - curr_stream_time = event_data->frames[init_frame_id-1].timestamp; - curr_frame_id = init_frame_id; + if ( init_frame_id >= event_data->frame_count ) { + Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count ); + curr_stream_time = event_data->start_time; + } else { + curr_stream_time = event_data->frames[init_frame_id-1].timestamp; + curr_frame_id = init_frame_id; + } } else { curr_stream_time = event_data->start_time; } - return( true ); + return true; } -bool EventStream::loadEventData( int event_id ) { +bool EventStream::loadEventData(int event_id) { static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %d", event_id ); + snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %d", event_id); - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } - MYSQL_RES *result = mysql_store_result( &dbconn ); + MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Error("Can't use query result: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } - if ( !mysql_num_rows( result ) ) { - Fatal( "Unable to load event %d, not found in DB", event_id ); + if ( !mysql_num_rows(result) ) { + Fatal("Unable to load event %d, not found in DB", event_id); } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_ROW dbrow = mysql_fetch_row(result); - if ( mysql_errno( &dbconn ) ) { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + if ( mysql_errno(&dbconn) ) { + Error("Can't fetch row: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } delete event_data; @@ -886,3 +891,17 @@ void EventStream::runStream() { closeComms(); } +void EventStream::setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) { + loadInitialEventData( init_event_id, init_frame_id ); + if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) { + Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id ); + return; + } +} +void EventStream::setStreamStart( int monitor_id, time_t event_time ) { + loadInitialEventData( monitor_id, event_time ); + if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) { + Fatal( "Unable to load monitor id %d for streaming", monitor_id ); + return; + } +} diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 02e90f70a..3a9168eac 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -110,20 +110,8 @@ class EventStream : public StreamBase { ffmpeg_input = NULL; } - void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) { - loadInitialEventData( init_event_id, init_frame_id ); - if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) { - Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id ); - return; - } - } - void setStreamStart( int monitor_id, time_t event_time ) { - loadInitialEventData( monitor_id, event_time ); - if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) { - Fatal( "Unable to load monitor id %d for streaming", monitor_id ); - return; - } - } + void setStreamStart( int init_event_id, unsigned int init_frame_id ); + void setStreamStart( int monitor_id, time_t event_time ); void setStreamMode( StreamMode p_mode ) { mode = p_mode; } From ede13ac41b659837e33be20fbb1b57f38696ce4f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:25:20 -0800 Subject: [PATCH 069/538] put all db access after finishing encoding, because we don 't care when they happen, and we need the encoding done. --- src/zm_event.cpp | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 17c385c87..7b5ca2c38 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -227,23 +227,8 @@ Event::Event( } // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent ) Event::~Event() { - static char sql[ZM_SQL_MED_BUFSIZ]; - struct DeltaTimeval delta_time; - DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2); - Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec ); - if ( frames > last_db_frame ) { - - Debug( 1, "Adding closing frame %d to DB", frames ); - snprintf( sql, sizeof(sql), - "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", - id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - db_mutex.lock(); - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - } - db_mutex.unlock(); - } + // We cose the videowriter first, because it might take some time, and we don't want to lock the db if we can avoid it /* Close the video file */ if ( videowriter != NULL ) { @@ -261,10 +246,32 @@ Event::~Event() { } } - snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); + + static char sql[ZM_SQL_MED_BUFSIZ]; + struct DeltaTimeval delta_time; + DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2); + Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec ); + + if ( frames > last_db_frame ) { + Debug( 1, "Adding closing frame %d to DB", frames ); + snprintf( sql, sizeof(sql), + "INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", + id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); + db_mutex.lock(); + if ( mysql_query( &dbconn, sql ) ) { + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + } else { + Debug(1,"Success writing last frame"); + } + db_mutex.unlock(); + } + + snprintf( sql, sizeof(sql), "UPDATE Events SET Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); db_mutex.lock(); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't update event: %s", mysql_error( &dbconn ) ); + } else { + Debug(1,"Success updating event"); } db_mutex.unlock(); } From 77ded1f5beebab71f565cb77c05c89ec18555d02 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:25:46 -0800 Subject: [PATCH 070/538] spacing and increase debug --- src/zm_video.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/zm_video.cpp b/src/zm_video.cpp index 369819098..51f529082 100644 --- a/src/zm_video.cpp +++ b/src/zm_video.cpp @@ -211,6 +211,7 @@ int X264MP4Writer::Open() { int X264MP4Writer::Close() { /* Flush all pending frames */ for ( int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) { +Debug(1,"Encoding delayed frame"); x264encodeloop(true); } @@ -220,6 +221,7 @@ int X264MP4Writer::Close() { /* Close MP4 handle */ MP4Close(mp4h); + Debug(1,"Optimising"); /* Required for proper HTTP streaming */ MP4Optimize((path + ".incomplete").c_str(), path.c_str()); @@ -228,7 +230,7 @@ int X264MP4Writer::Close() { bOpen = false; - Debug(7, "Video closed. Total frames: %d", frame_count); + Debug(1, "Video closed. Total frames: %d", frame_count); return 0; } @@ -413,7 +415,7 @@ void X264MP4Writer::x264encodeloop(bool bFlush) { } if ( frame_size > 0 || bFlush ) { - Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n", + Debug(1, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n", frame_count, x264picout.i_pts, x264picout.i_dts, frame_size); /* Handle the previous frame */ @@ -490,7 +492,7 @@ void X264MP4Writer::x264encodeloop(bool bFlush) { } } else if ( frame_size == 0 ) { - Debug(7, "x264 encode returned zero. Delayed frames: %d", + Debug(1, "x264 encode returned zero. Delayed frames: %d", x264_encoder_delayed_frames(x264enc)); } else { Error("x264 encode failed: %d", frame_size); From d980def64fcf7e78f721a8dea563b0057f74b9c9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:26:07 -0800 Subject: [PATCH 071/538] spacing, increase debug --- src/zm_ffmpeg_input.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 70dafb76c..c7b60cd7b 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -121,7 +121,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) { } if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { - Debug(1,"Packet is for our stream (%d)", packet.stream_index ); + Debug(3,"Packet is for our stream (%d)", packet.stream_index ); AVCodecContext *context = streams[packet.stream_index].context; @@ -169,9 +169,9 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) { frameComplete = 1; # else - ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet ); + ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet); if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); zm_av_packet_unref( &packet ); continue; @@ -179,7 +179,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) { #endif } // end if it's the right stream - zm_av_packet_unref( &packet ); + zm_av_packet_unref( &packet ); } // end while ! frameComplete return frame; From 91c9714e43ecbe864fc083d93dd57d275e86200c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:26:21 -0800 Subject: [PATCH 072/538] cleanup --- src/zm_monitor.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 0828b497a..164376100 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -3112,13 +3112,16 @@ bool Monitor::closeEvent() { event_delete_thread = NULL; } event_delete_thread = new std::thread([](Event *event) { - delete event; + Event * e = event; + event = NULL; + delete e; + e = NULL; }, event ); video_store_data->recording = (struct timeval){0}; - event = 0; - return( true ); + event = NULL; + return true; } - return( false ); + return false; } unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) { From c53b6ffbb00f8a5cc09c1a8ae2c8cc2f8a1309f6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:26:50 -0800 Subject: [PATCH 073/538] rework montagereview to load data faster and leave event and frame data in json objects --- web/skins/classic/views/js/montagereview.js | 169 ++++++++++++------ .../classic/views/js/montagereview.js.php | 119 +++++------- web/skins/classic/views/montagereview.php | 31 ++-- 3 files changed, 172 insertions(+), 147 deletions(-) diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 826c814ca..148b0e50b 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -38,42 +38,95 @@ function evaluateLoadTimes() { else if (avgFrac >= 0.2) currentDisplayInterval = (currentDisplayInterval * 1.50).toFixed(1); else if (avgFrac >= 0.1) currentDisplayInterval = (currentDisplayInterval * 2.00).toFixed(1); else currentDisplayInterval = (currentDisplayInterval * 2.50).toFixed(1); - currentDisplayInterval = Math.min(Math.max(currentDisplayInterval, 30), 10000); // limit this from about 30fps to .1 fps + // limit this from about 40fps to .1 fps + currentDisplayInterval = Math.min(Math.max(currentDisplayInterval, 40), 10000); imageLoadTimesEvaluated=0; setSpeed(speedIndex); $('fps').innerHTML="Display refresh rate is " + (1000 / currentDisplayInterval).toFixed(1) + " per second, avgFrac=" + avgFrac.toFixed(3) + "."; } // end evaluateLoadTimes() +function getFrame( monId, time ) { + var Frame = null; + for ( var event_id in events ) { + // Search for the event matching this time. Would be more efficient if we had events indexed by monitor + Event = events[event_id]; + if ( Event.MonitorId != monId || Event.StartTimeSecs > time || Event.EndTimeSecs < time ) + continue; + + var duration = Event.EndTimeSecs - Event.StartTimeSecs; + var frame = parseInt((time - Event.StartTimeSecs)/(duration)*Object.keys(Event.FramesById).length)+1; + // Need to get frame by time, not some fun calc that assumes frames have the same mlength. + // Frames are not sorted. + for ( var frame_id in Event.FramesById ) { +if ( 0 ) { + if ( frame == 0 ) { +console.log("Found frame for time " + time ); +console.log(Frame); + Frame = Event.FramesById[frame_id]; + break; + } + frame --; + continue; +} + if ( + Event.FramesById[frame_id].TimeStampSecs == time + || ( + Event.FramesById[frame_id].TimeStampSecs < time + && ( + (!Event.FramesById[frame_id].NextTimeStampSecs) + || + (Event.FramesById[frame_id].NextTimeStampSecs > time) + ) + ) + ) { + Frame = Event.FramesById[frame_id]; + break; + } + } // end foreach frame in the event. + if ( ! Frame ) { +console.log("Difn't find frame for " + time ); + return null; + } + } // end foreach event + return Frame; +} + // time is seconds since epoch function getImageSource( monId, time ) { if ( liveMode == 1 ) { - var new_url = monitorImageObject[monId].src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) )); + var new_url = monitorImageObject[monId].src.replace( + /rand=\d+/i, + 'rand='+Math.floor((Math.random() * 1000000) ) + ); if ( auth_hash ) { // update auth hash new_url = new_url.replace(/auth=[a-z0-9]+/i, 'auth='+auth_hash); } return new_url; } + var Frame = getFrame(monId, time); + if ( Frame ) { + Event = events[Frame.EventId]; - for ( var i=0, eIdlength = eId.length; i < eIdlength; i++ ) { - // Search for the event matching this time. Would be more efficient if we had events indexed by monitor - if ( eMonId[i] == monId && time >= eStartSecs[i] && time <= eEndSecs[i] ) { - var duration = eEndSecs[i]-eStartSecs[i]; - var frame = parseInt((time - eStartSecs[i])/(duration)*eventFrames[i])+1; - var storage = Storage[eStorageId[i]]; - if ( storage.ServerId ) { - var server = Servers[storage.ServerId]; - if ( server ) { -//console.log( server.Hostname + " for event " + eId[i] ); - return location.protocol + '//' + server.Hostname + '/index.php?view=image&eid=' + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; - } else { - console.log("No server found for " + storage.ServerId ); - } + var storage = Storage[Event.StorageId]; + if ( storage.ServerId ) { + var server = Servers[storage.ServerId]; + if ( server ) { + //console.log( server.Hostname + " for event " + eId[i] ); + return location.protocol + '//' + server.Hostname + + //'/cgi-bin/zms?mode=jpeg&replay=single&event=' + event_id + + //'&frame='+Frame.FrameId + +'/index.php?view=image&eid=' + event_id + '&fid='+Frame.FrameId + + "&width=" + monitorCanvasObj[monId].width + + "&height=" + monitorCanvasObj[monId].height; + } else { + console.log("No server found for " + storage.ServerId ); } - //console.log("No storage found for " + eStorageId[i] ); - return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; } - } // end for + //console.log("No storage found for " + eStorageId[i] ); + return '/index.php?view=image&eid=' + Frame.EventId + '&fid='+Frame.FrameId + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; + return "/cgi-bin/zms?mode=jpeg&replay=single&event=" + Frame.EventId + '&frame='+Frame.FrameId + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height; + } // end found Frame return "no data"; } @@ -294,7 +347,7 @@ function drawGraph() { canvas.height = cHeight; - if ( eId.length == 0 ) { + if ( Object.keys(events).length == 0 ) { ctx.globalAlpha=1; ctx.font= "40px Georgia"; ctx.fillStyle="white"; @@ -308,24 +361,32 @@ function drawGraph() { // first fill in the bars for the events (not alarms) - for(var i=0; i0); i++) { - // Now put in scored frames (if any) - var x1=parseInt( (fTimeFromSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down - var x2=parseInt( (fTimeToSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up - if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide - ctx.fillStyle=monitorColour[fMonId[i]]; - ctx.globalAlpha = 0.4 + 0.6 * (1 - fScore[i]/maxScore); // Background is scaled but even lowest is twice as dark as the background - ctx.fillRect(x1,monitorIndex[fMonId[i]]*rowHeight,x2-x1,rowHeight); - } + ctx.clearRect(x1,monitorIndex[Event.MonitorId]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker + ctx.fillRect(x1,monitorIndex[Event.MonitorId]*rowHeight,x2-x1,rowHeight); + + for ( var frame_id in Event.FramesById ) { + var Frame = Event.FramesById[frame_id]; + if ( ! Frame.Score ) + continue; + + // Now put in scored frames (if any) + var x1=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down + var x2=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up + if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide + ctx.fillStyle=monitorColour[Event.MonitorId]; + ctx.globalAlpha = 0.4 + 0.6 * (1 - Frame.Score/maxScore); // Background is scaled but even lowest is twice as dark as the background + ctx.fillRect(x1,monitorIndex[Event.MonitorId]*rowHeight,x2-x1,rowHeight); + } // end foreach frame + } // end foreach Event + for(var i=0; i= eStartSecs[i] && currentTimeSecs <= eEndSecs[i] ) { - url="?view=event&eid=" + eId[i] + '&fid=' + parseInt(Math.max(1, Math.min(eventFrames[i], eventFrames[i] * (currentTimeSecs - eStartSecs[i]) / (eEndSecs[i] - eStartSecs[i] + 1) ) )); - break; - } else if ( currentTimeSecs <= eStartSecs[i] ) { - if ( i ) { - // Didn't find an exact event, so go with the one before. - url="?view=event&eid=" + eId[i] + '&fid=' + parseInt(Math.max(1, Math.min(eventFrames[i], eventFrames[i] * (currentTimeSecs - eStartSecs[i]) / (eEndSecs[i] - eStartSecs[i] + 1) ) )); - } - break; - } - } - if ( url ) { - createPopup(url, 'zmEvent', 'event', monitorWidth[monId], monitorHeight[monId]); + var Frame = getFrame( monId, currentTimeSecs ); + if ( Frame ) { + url="?view=event&eid=" + Frame.EventId + '&fid=' +Frame.FrameId; + createPopup(url, 'zmEvent', 'event', monitorWidth[monId], monitorHeight[monId]); } else { url="?view=watch&mid=" + monId.toString(); createPopup(url, 'zmWatch', 'watch', monitorWidth[monId], monitorHeight[monId] ); } - } + } // end if live/events } function zoom(monId,scale) { @@ -787,8 +836,6 @@ function changeDateTime(e) { // >>>>>>>>> Initialization that runs on window load by being at the bottom function initPage() { - canvas = $("timeline"); - ctx = canvas.getContext('2d'); for ( var i = 0, len = monitorPtr.length; i < len; i += 1 ) { var monId = monitorPtr[i]; if ( ! monId ) continue; @@ -804,7 +851,11 @@ function initPage() { loadImage2Monitor( monId, monitorImageURL[monId] ); } } - drawGraph(); + if ( !liveMode ) { + canvas = $("timeline"); + ctx = canvas.getContext('2d'); + drawGraph(); + } setSpeed(speedIndex); //setFit(fitMode); // will redraw //setLive(liveMode); // will redraw diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 029eb92ff..1349ee512 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -9,10 +9,14 @@ echo $offset . '; // ' . floor($offset / 3600) . ' hours '; var currentScale=; var liveMode=; var fitMode=; -var currentSpeed=; // slider scale, which is only for replay and relative to real time + +// slider scale, which is only for replay and relative to real time +var currentSpeed=; var speedIndex=; -// will be set based on performance, this is the display interval in milliseconds for history, and fps for live, and dynamically determined (in ms) +// will be set based on performance, this is the display interval in milliseconds +// for history, and fps for live, and dynamically determined (in ms) + var currentDisplayInterval=; var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live) var timerInterval; // milliseconds between interrupts @@ -21,12 +25,6 @@ var freeTimeLastIntervals=[]; // Percentage of current interval used in loadi var imageLoadTimesEvaluated=0; // running count var imageLoadTimesNeeded=15; // and how many we need var timeLabelsFractOfRow = 0.9; -var eMonId = []; -var eId = []; -var eStorageId = []; -var eStartSecs = []; -var eEndSecs = []; -var eventFrames = []; // this is going to presume all frames equal durationlength fetch( PDO::FETCH_ASSOC ) ) { + $event_id = $event['Id']; + $EventsById[$event_id] = $event; + } - $StartTimeSecs = strtotime($event['StartTime']); - $EndTimeSecs = strtotime($event['EndTime']); + $next_frames = array(); - if ( $minTimeSecs > $StartTimeSecs ) $minTimeSecs = $StartTimeSecs; + if ( $result = dbQuery($frameSql) ) { + $next_frame = null; + while( $frame = $result->fetch(PDO::FETCH_ASSOC) ) { + $event_id = $frame['EventId']; + $event = &$EventsById[$event_id]; + + $frame['TimeStampSecs'] = $event['StartTimeSecs'] + $frame['Delta']; + if ( !isset($event['FramesById']) ) { + $event['FramesById'] = array(); + $frame['NextTimeStampSecs'] = 0; + } else { + $frame['NextTimeStampSecs'] = $next_frames[$frame['EventId']]['TimeStampSecs'];; + } + $event['FramesById'] += array( $frame['Id']=>$frame ); + $next_frames[$frame['EventId']] = $frame; + } + } + + echo "var events = {\n"; + foreach ( $EventsById as $event_id=>$event ) { + + $StartTimeSecs = $event['StartTimeSecs']; + $EndTimeSecs = $event['EndTimeSecs']; + + if ( $minTimeSecs > $StartTimeSecs ) $minTimeSecs = $StartTimeSecs; if ( $maxTimeSecs < $EndTimeSecs ) $maxTimeSecs = $EndTimeSecs; - echo " -eMonId[$index]=" . $event['MonitorId'] . "; -eStorageId[$index]=".$event['StorageId'] . "; -eId[$index]=" . $event['Id'] . "; -eStartSecs[$index]=" . $StartTimeSecs . "; -eEndSecs[$index]=" . $EndTimeSecs . "; -eventFrames[$index]=" . $event['Frames'] . "; - "; + $event_json = json_encode($event, JSON_PRETTY_PRINT); + echo " $event_id : $event_json,\n"; $index = $index + 1; - if ( $event['MaxScore'] > 0 ) + if ( $event['MaxScore'] > 0 ) { + if ( $event['MaxScore'] > $maxScore ) + $maxScore = $event['MaxScore']; $anyAlarms = true; + } } +echo " };\n"; // if there is no data set the min/max to the passed in values if ( $index == 0 ) { @@ -93,60 +118,6 @@ eventFrames[$index]=" . $event['Frames'] . "; $maxTimeSecs = strtotime($maxTime); } - // If we had any alarms in those events, this builds the list of all alarm frames, but consolidated down to (nearly) contiguous segments - // comparison in else governs how aggressively it consolidates - - echo " -var fMonId = []; -var fTimeFromSecs = []; -var fTimeToSecs = []; -var fScore = []; -"; -$maxScore=0; -$index=0; -$mId=-1; -$fromSecs=-1; -$toSecs=-1; -$maxScore=-1; - -if ( $anyAlarms && $result = dbQuery( $frameSql ) ) { - - while( $frame = $result->fetch( PDO::FETCH_ASSOC ) ) { - if ( $mId < 0 ) { - $mId = $frame['MonitorId']; - $fromSecs = $frame['TimeStampSecs']; - $toSecs = $frame['TimeStampSecs']; - $maxScore = $frame['Score']; - } else if ( $mId != $frame['MonitorId'] || $frame['TimeStampSecs'] - $toSecs > 10 ) { - // dump this one start a new - $index++; - echo " - -fMonId[$index]= $mId; -fTimeFromSecs[$index]= $fromSecs; -fTimeToSecs[$index]= $toSecs; -fScore[$index]= $maxScore; -"; - $mId = $frame['MonitorId']; - $fromSecs = $frame['TimeStampSecs']; - $toSecs = $frame['TimeStampSecs']; - $maxScore = $frame['Score']; - } else { - // just add this one on - $toSecs = $frame['TimeStampSecs']; - if ( $maxScore < $frame['Score'] ) $maxScore = $frame['Score']; - } - } -} -if ( $mId > 0 ) { - echo " - fMonId[$index]= $mId; - fTimeFromSecs[$index]= $fromSecs; - fTimeToSecs[$index]= $toSecs; - fScore[$index]= $maxScore; -"; -} - echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms. } // end if initialmodeislive diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index d2d60b8e7..c5e5e3913 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -94,14 +94,14 @@ if (isset($_REQUEST['minTime']) && isset($_REQUEST['maxTime']) && count($display // if the bulk record has not been written - to be able to include more current frames reduce bulk frame sizes (event size can be large) // Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly. -$eventsSql = ' - SELECT E.Id,E.Name,E.StorageId,E.StartTime AS StartTime, - CASE WHEN E.EndTime IS NULL THEN (SELECT NOW()) ELSE E.EndTime END AS EndTime, - E.Length, - CASE WHEN E.Frames IS NULL THEN (SELECT COUNT(*) FROM Frames F WHERE F.EventId=E.Id) ELSE E.Frames END AS Frames, - E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId +$eventsSql = 'SELECT + E.Id,E.Name,E.StorageId, + E.StartTime AS StartTime,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs, + CASE WHEN E.EndTime IS NULL THEN (SELECT NOW()) ELSE E.EndTime END AS EndTime, + UNIX_TIMESTAMP(EndTime) AS EndTimeSecs, + E.Length, E.Frames, E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId FROM Events AS E - WHERE NOT isnull(E.Frames) + WHERE 1 > 0 '; // select E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) as StartTimeSecs,UNIX_TIMESTAMP(max(DATE_ADD(E.StartTime, Interval Delta+0.5 Second))) as CalcEndTimeSecs, E.Length,max(F.FrameId) as Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId @@ -112,10 +112,9 @@ $eventsSql = ' // Note that the delta value seems more accurate than the time stamp for some reason. $frameSql = ' - SELECT E.Id AS eId, E.MonitorId, UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval Delta Second)) AS TimeStampSecs, max(F.Score) AS Score - FROM Events AS E - INNER JOIN Frames AS F ON (F.EventId = E.Id) - WHERE F.Score>0 + SELECT Id, FrameId, EventId, TimeStamp, UNIX_TIMESTAMP(TimeStamp) AS TimeStampSecs, Score, Delta + FROM Frames + WHERE EventId IN (SELECT E.Id FROM Events AS E WHERE 1>0 '; // This program only calls itself with the time range involved -- it does all monitors (the user can see, in the called group) all the time @@ -183,7 +182,7 @@ if ( isset($_REQUEST['current']) ) $liveMode = 1; // default to live if ( isset($_REQUEST['live']) && $_REQUEST['live']=='0' ) - $liveMode=0; + $liveMode = 0; $initialDisplayInterval = 1000; if ( isset($_REQUEST['displayinterval']) ) @@ -196,10 +195,14 @@ if ( isset($minTime) && isset($maxTime) ) { $maxTimeSecs = strtotime($maxTime); Logger::Debug("Min/max time secs: $minTimeSecs $maxTimeSecs"); $eventsSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'"; - $frameSql .= " AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'"; + $frameSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'"; + $frameSql .= ") AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'"; } -$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC'; +#$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC'; +#$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC'; $eventsSql .= ' ORDER BY E.Id ASC'; +// DESC is intentional. We process them in reverse order so that we can point each frame to the next one in time. +$frameSql .= ' ORDER BY Id DESC'; $monitors = array(); foreach( $displayMonitors as $row ) { From 47ec0abf117b4eb0f69f5fc823b1bcc492a66e67 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Mar 2018 18:27:03 -0800 Subject: [PATCH 074/538] spacing --- src/zms.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zms.cpp b/src/zms.cpp index 66b3dc683..d3947c99e 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -283,10 +283,10 @@ int main( int argc, const char *argv[] ) { stream.setStreamMode( replay ); stream.setStreamQueue( connkey ); if ( monitor_id && event_time ) { - stream.setStreamStart( monitor_id, event_time ); + stream.setStreamStart(monitor_id, event_time); } else { Debug(3, "Setting stream start to frame (%d)", frame_id); - stream.setStreamStart( event_id, frame_id ); + stream.setStreamStart(event_id, frame_id); } if ( mode == ZMS_JPEG ) { stream.setStreamType( EventStream::STREAM_JPEG ); From 3dc157732a6ad1df4021ab1801aa7530f79c44ec Mon Sep 17 00:00:00 2001 From: Mike R Date: Sat, 3 Mar 2018 00:58:35 -0500 Subject: [PATCH 075/538] Issue 1963: Remove spaces from example api calls. --- docs/api.rst | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 36467525a..706748880 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -98,15 +98,15 @@ This command will add a new http monitor. :: - curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton \ - &Monitor[Function]=Modect \ - &Monitor[Protocol]=http \ - &Monitor[Method]=simple \ - &Monitor[Host]=usr:pass@192.168.11.20 \ - &Monitor[Port]=80 \ - &Monitor[Path]=/mjpg/video.mjpg \ - &Monitor[Width]=704 \ - &Monitor[Height]=480 \ + curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton\ + &Monitor[Function]=Modect\ + &Monitor[Protocol]=http\ + &Monitor[Method]=simple\ + &Monitor[Host]=usr:pass@192.168.11.20\ + &Monitor[Port]=80\ + &Monitor[Path]=/mjpg/video.mjpg\ + &Monitor[Width]=704\ + &Monitor[Height]=480\ &Monitor[Colours]=4" Edit monitor 1 @@ -304,26 +304,26 @@ Create a Zone :: - curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted \ - &Zone[MonitorId]=3 \ - &Zone[Type]=Active \ - &Zone[Units]=Percent \ - &Zone[NumCoords]=4 \ - &Zone[Coords]=0,0 639,0 639,479 0,479 \ - &Zone[AlarmRGB]=16711680 \ - &Zone[CheckMethod]=Blobs \ - &Zone[MinPixelThreshold]=25 \ - &Zone[MaxPixelThreshold]= \ - &Zone[MinAlarmPixels]=9216 \ - &Zone[MaxAlarmPixels]= \ - &Zone[FilterX]=3 \ - &Zone[FilterY]=3 \ - &Zone[MinFilterPixels]=9216 \ - &Zone[MaxFilterPixels]=230400 \ - &Zone[MinBlobPixels]=6144 \ - &Zone[MaxBlobPixels]= \ - &Zone[MinBlobs]=1 \ - &Zone[MaxBlobs]= \ + curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted\ + &Zone[MonitorId]=3\ + &Zone[Type]=Active\ + &Zone[Units]=Percent\ + &Zone[NumCoords]=4\ + &Zone[Coords]=0,0 639,0 639,479 0,479\ + &Zone[AlarmRGB]=16711680\ + &Zone[CheckMethod]=Blobs\ + &Zone[MinPixelThreshold]=25\ + &Zone[MaxPixelThreshold]=\ + &Zone[MinAlarmPixels]=9216\ + &Zone[MaxAlarmPixels]=\ + &Zone[FilterX]=3\ + &Zone[FilterY]=3\ + &Zone[MinFilterPixels]=9216\ + &Zone[MaxFilterPixels]=230400\ + &Zone[MinBlobPixels]=6144\ + &Zone[MaxBlobPixels]=\ + &Zone[MinBlobs]=1\ + &Zone[MaxBlobs]=\ &Zone[OverloadFrames]=0" PTZ Control APIs From 2864876d28c6cde03d84f11359d3ccbb62ac1479 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 3 Mar 2018 09:25:04 -0800 Subject: [PATCH 076/538] fix spacing, add code comment --- web/includes/Control.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/web/includes/Control.php b/web/includes/Control.php index 343a7b31a..de5f221ff 100644 --- a/web/includes/Control.php +++ b/web/includes/Control.php @@ -204,6 +204,7 @@ private $defaults = array( $this->{$k} = $v; } } + // Set default values foreach ( $this->defaults as $k=>$v ) { if ( ( ! array_key_exists( $k, $this ) ) or ( $this->{$k} == '' ) ) { $this->{$k} = $v; @@ -212,16 +213,17 @@ private $defaults = array( $fields = array_keys( $this->defaults ); -if ( array_key_exists( 'Id', $this ) ) { - $sql = 'UPDATE Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?'; - $values = array_map( function($field){return $this->{$field};}, $fields ); - $values[] = $this->{'Id'}; - dbQuery( $sql, $values ); -} else { - $sql = 'INSERT INTO Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ''; - $values = array_map( function($field){return $this->{$field};}, $fields ); - dbQuery( $sql, $values ); -} + if ( array_key_exists( 'Id', $this ) ) { + $sql = 'UPDATE Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?'; + $values = array_map( function($field){return $this->{$field};}, $fields ); + $values[] = $this->{'Id'}; + dbQuery( $sql, $values ); + } else { + $sql = 'INSERT INTO Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ''; + $values = array_map( function($field){return $this->{$field};}, $fields ); + dbQuery( $sql, $values ); + $this->{'Id'} = dbInsertId(); + } } // end function save } // end class Control From 128c4c73e92395c051096624ec9c39cb50ccc652 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 3 Mar 2018 13:29:39 -0800 Subject: [PATCH 077/538] Add official sql for loading a Monitor --- src/zm_monitor.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zm_monitor.h b/src/zm_monitor.h index ff6ceca50..d18cd1c13 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -330,6 +330,8 @@ protected: int n_linked_monitors; MonitorLink **linked_monitors; + // This is the official SQL (and ordering of the fields) to load a Monitor. It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended + std::string load_monitor_sql = "SELECT Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, Encoder, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors"; public: explicit Monitor( int p_id ); From 646f0dd65ecbd4a553cfa67aaaf10b86fdd77c44 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 3 Mar 2018 13:31:55 -0800 Subject: [PATCH 078/538] fix merge --- src/zm_ffmpeg_camera.cpp | 303 --------------------------------------- src/zm_ffmpeg_input.cpp | 70 +-------- 2 files changed, 6 insertions(+), 367 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 58fd115b1..154e6fb37 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -131,15 +131,7 @@ int FfmpegCamera::PrimeCapture() { } int FfmpegCamera::PreCapture() { -<<<<<<< HEAD return 0; -======= - // If Reopen was called, then ffmpeg is closed and we need to reopen it. - if ( ! mCanCapture ) - return OpenFfmpeg(); - // Nothing to do here - return( 0 ); ->>>>>>> storageareas } int FfmpegCamera::Capture( ZMPacket &zm_packet ) { @@ -483,301 +475,6 @@ int FfmpegCamera::CloseFfmpeg() { } return 0; -<<<<<<< HEAD } // end int FfmpegCamera::CloseFfmpeg() -======= -} // end FfmpegCamera::Close - -//Function to handle capture and store -int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) { - if ( ! mCanCapture ) { - return -1; - } - int ret; - static char errbuf[AV_ERROR_MAX_STRING_SIZE]; - - int frameComplete = false; - while ( ! frameComplete ) { - av_init_packet( &packet ); - - ret = av_read_frame( mFormatContext, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf ); - return -1; - } - - int keyframe = packet.flags & AV_PKT_FLAG_KEY; - dumpPacket(&packet); - - //Video recording - if ( recording.tv_sec ) { - - uint32_t last_event_id = monitor->GetLastEventId() ; - uint32_t video_writer_event_id = monitor->GetVideoWriterEventId(); - - if ( last_event_id != video_writer_event_id ) { - Debug(2, "Have change of event. last_event(%d), our current (%d)", last_event_id, video_writer_event_id ); - - if ( videoStore ) { - Info("Re-starting video storage module"); - - // I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it. - // Also don't know how much it matters for audio. - if ( packet.stream_index == mVideoStreamId ) { - //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket( &packet ); - if ( ret < 0 ) { //Less than zero and we skipped a frame - Warning("Error writing last packet to videostore."); - } - } // end if video - - delete videoStore; - videoStore = NULL; - have_video_keyframe = false; - - monitor->SetVideoWriterEventId( 0 ); - } // end if videoStore - } // end if end of recording - - if ( last_event_id and ! videoStore ) { - //Instantiate the video storage module - - if ( record_audio ) { - if ( mAudioStreamId == -1 ) { - Debug(3, "Record Audio on but no audio stream found"); - videoStore = new VideoStore((const char *) event_file, "mp4", - mFormatContext->streams[mVideoStreamId], - NULL, - startTime, - this->getMonitor()); - - } else { - Debug(3, "Video module initiated with audio stream"); - videoStore = new VideoStore((const char *) event_file, "mp4", - mFormatContext->streams[mVideoStreamId], - mFormatContext->streams[mAudioStreamId], - startTime, - this->getMonitor()); - } - } else { - if ( mAudioStreamId >= 0 ) { - Debug(3, "Record_audio is false so exclude audio stream"); - } - videoStore = new VideoStore((const char *) event_file, "mp4", - mFormatContext->streams[mVideoStreamId], - NULL, - startTime, - this->getMonitor()); - } // end if record_audio - - if ( ! videoStore->open() ) { - delete videoStore; - videoStore = NULL; - - } else { - monitor->SetVideoWriterEventId( last_event_id ); - - // Need to write out all the frames from the last keyframe? - // No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. - unsigned int packet_count = 0; - ZMPacket *queued_packet; - - // Clear all packets that predate the moment when the recording began - packetqueue.clear_unwanted_packets( &recording, mVideoStreamId ); - - while ( ( queued_packet = packetqueue.popPacket() ) ) { - AVPacket *avp = queued_packet->av_packet(); - - packet_count += 1; - //Write the packet to our video store - Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() ); - if ( avp->stream_index == mVideoStreamId ) { - ret = videoStore->writeVideoFramePacket( avp ); - have_video_keyframe = true; - } else if ( avp->stream_index == mAudioStreamId ) { - ret = videoStore->writeAudioFramePacket( avp ); - } else { - Warning("Unknown stream id in queued packet (%d)", avp->stream_index ); - ret = -1; - } - if ( ret < 0 ) { - //Less than zero and we skipped a frame - } - delete queued_packet; - } // end while packets in the packetqueue - Debug(2, "Wrote %d queued packets", packet_count ); - } - } // end if ! was recording - - } else { - // Not recording - if ( videoStore ) { - Info("Deleting videoStore instance"); - delete videoStore; - videoStore = NULL; - have_video_keyframe = false; - monitor->SetVideoWriterEventId( 0 ); - } - - // Buffer video packets, since we are not recording. - // All audio packets are keyframes, so only if it's a video keyframe - if ( packet.stream_index == mVideoStreamId ) { - if ( keyframe ) { - Debug(3, "Clearing queue"); - packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId ); - packetqueue.queuePacket( &packet ); - } else if ( packetqueue.size() ) { - // it's a keyframe or we already have something in the queue - packetqueue.queuePacket( &packet ); - } - } else if ( packet.stream_index == mAudioStreamId ) { - // The following lines should ensure that the queue always begins with a video keyframe -//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() ); - if ( record_audio && packetqueue.size() ) { - // if it's audio, and we are doing audio, and there is already something in the queue - packetqueue.queuePacket( &packet ); - } - } - } // end if recording or not - - if ( packet.stream_index == mVideoStreamId ) { - // only do decode if we have had a keyframe, should save a few cycles. - if ( have_video_keyframe || keyframe ) { - - if ( videoStore ) { - - //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket( &packet ); - if ( ret < 0 ) { //Less than zero and we skipped a frame - zm_av_packet_unref( &packet ); - return 0; - } - have_video_keyframe = true; - } - } // end if keyframe or have_video_keyframe - - Debug(4, "about to decode video" ); - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_send_packet( mVideoCodecContext, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } -#if HAVE_AVUTIL_HWCONTEXT_H - if ( hwaccel ) { - ret = avcodec_receive_frame( mVideoCodecContext, hwFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0); - if (ret < 0) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - } else { -#endif - ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Warning( "Unable to receive frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - -#if HAVE_AVUTIL_HWCONTEXT_H - } -#endif - - frameComplete = 1; -# else - ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } -#endif - - if ( frameComplete ) { - Debug( 4, "Got frame %d", frameCount ); - - uint8_t* directbuffer; - - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if ( directbuffer == NULL ) { - Error("Failed requesting writeable buffer for the captured image."); - zm_av_packet_unref( &packet ); - return (-1); - } -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1); -#else - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); -#endif - - - if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, - 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) { - Error("Unable to convert raw format %u to target format %u at frame %d", - mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); - return -1; - } - - frameCount++; - } else { - Debug( 3, "Not framecomplete after av_read_frame" ); - } // end if frameComplete - } else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams - frameComplete = 1; - if ( videoStore ) { - if ( record_audio ) { - if ( have_video_keyframe ) { - Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index ); - //Write the packet to our video store - //FIXME no relevance of last key frame - int ret = videoStore->writeAudioFramePacket( &packet ); - if ( ret < 0 ) {//Less than zero and we skipped a frame - Warning("Failure to write audio packet."); - zm_av_packet_unref( &packet ); - return 0; - } - } else { - Debug(3, "Not recording audio yet because we don't have a video keyframe yet"); - } - } else { - Debug(4, "Not doing recording of audio packet" ); - } - } else { - Debug(4, "Have audio packet, but not recording atm" ); - } - zm_av_packet_unref( &packet ); - return 0; - } else { -#if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0) - Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codecpar->codec_type) ); -#else - Debug( 3, "Some other stream index %d", packet.stream_index ); -#endif - } // end if is video or audio or something else - - // the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version. - zm_av_packet_unref( &packet ); - } // end while ! frameComplete - return frameCount; -} // end FfmpegCamera::CaptureAndRecord - - ->>>>>>> storageareas #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index ed47cbd41..063e2eb46 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -135,77 +135,21 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) { // Check for Connection failure. (ret == -110) ) { - Info( "av_read_frame returned %s.", av_make_error_string(ret).c_str() ); + Info("av_read_frame returned %s.", av_make_error_string(ret).c_str()); } else { - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, - av_make_error_string(ret).c_str() ); + Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, + av_make_error_string(ret).c_str()); } return NULL; } -<<<<<<< HEAD if ( ( stream_id < 0 ) || ( packet.stream_index != stream_id ) ) { Warning("Packet is not for our stream (%d)", packet.stream_index); return NULL; -======= - if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { - Debug(3,"Packet is for our stream (%d)", packet.stream_index ); - - AVCodecContext *context = streams[packet.stream_index].context; - -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_send_packet( context, &packet ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } else { - Debug(1, "Success getting a packet"); } -#if HAVE_AVUTIL_HWCONTEXT_H - if ( hwaccel ) { - ret = avcodec_receive_frame( context, hwFrame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - ret = av_hwframe_transfer_data(frame, hwFrame, 0); - if (ret < 0) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - } else { -#endif - Debug(1,"Getting a frame?"); - ret = avcodec_receive_frame( context, frame ); - if ( ret < 0 ) { - av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); - continue; - } - -#if HAVE_AVUTIL_HWCONTEXT_H ->>>>>>> storageareas - } - -<<<<<<< HEAD - if ( ! zm_receive_frame( streams[packet.stream_index].context, frame, packet ) ) { + if ( ! zm_receive_frame(streams[packet.stream_index].context, frame, packet) ) { Error("Unable to get frame %d, continuing", streams[packet.stream_index].frame_count); -======= - frameComplete = 1; -# else - ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet); - if ( ret < 0 ) { - av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); - Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); ->>>>>>> storageareas zm_av_packet_unref( &packet ); continue; } else { @@ -213,12 +157,10 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) { streams[packet.stream_index].frame_count += 1; } - zm_av_packet_unref( &packet ); + zm_av_packet_unref(&packet); if ( frame_number == -1 ) break; } // end while frame_number > streams.frame_count return frame; -} // end AVFrame *FFmpeg_Input::get_frame - - +} // end AVFrame *FFmpeg_Input::get_frame From db8ab28845e19df1e842d8dacb8e2fa45ef6c2cc Mon Sep 17 00:00:00 2001 From: Steve Gilvarry Date: Sun, 4 Mar 2018 08:37:21 +1100 Subject: [PATCH 079/538] Add return false to window.history.back Adding this makes it work in Safari --- web/skins/classic/views/events.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 96cd704c4..d9ec1daf3 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -119,7 +119,7 @@ if ( $pages > 1 ) { ?>
- +
From 1c740de5dcf60e9e25e6d07e306c697042d7760a Mon Sep 17 00:00:00 2001 From: Steve Gilvarry Date: Sun, 4 Mar 2018 13:37:30 +1100 Subject: [PATCH 080/538] Add Return False to Event Back --- web/skins/classic/views/event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index ee0c212f3..38118a9f2 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -114,7 +114,7 @@ if ( ! $Event->Id() ) { ">Frames() ?>/AlarmFrames() ?> ">TotScore() ?>/AvgScore() ?>/MaxScore() ?> DiskSpace(null)) . ' on ' . $Event->Storage()->Name() ?> -
+