wip
This commit is contained in:
commit
ef0379dd18
|
@ -232,6 +232,7 @@ CREATE TABLE `Filters` (
|
|||
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
|
||||
`AutoExecuteCmd` tinytext,
|
||||
`AutoDelete` tinyint(3) unsigned NOT NULL default '0',
|
||||
`UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0',
|
||||
`Background` tinyint(1) unsigned NOT NULL default '0',
|
||||
`Concurrent` tinyint(1) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`Id`),
|
||||
|
@ -289,6 +290,7 @@ CREATE TABLE `Logs` (
|
|||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
CREATE INDEX `Logs_TimeKey_idx` ON `Logs` (`TimeKey`);
|
||||
CREATE INDEX `Logs_Level_idx` ON `Logs` (`Level`);
|
||||
--
|
||||
-- Table structure for table `Manufacturers`
|
||||
--
|
||||
|
@ -382,6 +384,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'),
|
||||
`OutputContainer` enum('mp4','mkv'),
|
||||
`EncoderParameters` TEXT,
|
||||
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
|
||||
`RTSPDescribe` tinyint(1) unsigned,
|
||||
|
|
|
@ -125,7 +125,7 @@ sub Execute {
|
|||
push @results, $event;
|
||||
}
|
||||
$sth->finish();
|
||||
Debug("Loaded " . @results . " events for filter $_[0]{Name} using query ($sql)");
|
||||
Debug('Loaded ' . @results . " events for filter $_[0]{Name} using query ($sql)");
|
||||
return @results;
|
||||
}
|
||||
|
||||
|
@ -147,38 +147,59 @@ sub Sql {
|
|||
foreach my $term ( @{$filter_expr->{terms}} ) {
|
||||
|
||||
if ( exists($term->{cnj}) ) {
|
||||
$self->{Sql} .= " ".$term->{cnj}." ";
|
||||
$self->{Sql} .= ' '.$term->{cnj}." ";
|
||||
}
|
||||
if ( exists($term->{obr}) ) {
|
||||
$self->{Sql} .= " ".str_repeat( "(", $term->{obr} )." ";
|
||||
$self->{Sql} .= ' '.str_repeat( "(", $term->{obr} )." ";
|
||||
}
|
||||
my $value = $term->{val};
|
||||
my @value_list;
|
||||
if ( $term->{attr} ) {
|
||||
if ( $term->{attr} =~ /^Monitor/ ) {
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= "M.".$temp_attr_name;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} =~ /^Server/ ) {
|
||||
$self->{Sql} .= "M.".$term->{attr};
|
||||
$self->{Sql} .= 'M.'.$term->{attr};
|
||||
|
||||
# StartTime options
|
||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||
$self->{Sql} .= "E.StartTime";
|
||||
$self->{Sql} .= 'E.StartTime';
|
||||
} elsif ( $term->{attr} eq 'StartDateTime' ) {
|
||||
$self->{Sql} .= 'E.StartTime';
|
||||
} elsif ( $term->{attr} eq 'Date' ) {
|
||||
$self->{Sql} .= "to_days( E.StartTime )";
|
||||
$self->{Sql} .= 'to_days( E.StartTime )';
|
||||
} elsif ( $term->{attr} eq 'StartDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartTime )';
|
||||
} elsif ( $term->{attr} eq 'Time' ) {
|
||||
$self->{Sql} .= "extract( hour_second from E.StartTime )";
|
||||
} elsif ( $term->{attr} eq 'Weekday' ) {
|
||||
$self->{Sql} .= "weekday( E.StartTime )";
|
||||
|
||||
# EndTIme options
|
||||
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||
$self->{Sql} .= 'E.EndTime';
|
||||
} elsif ( $term->{attr} eq 'EndDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.EndTime )';
|
||||
} elsif ( $term->{attr} eq 'EndTime' ) {
|
||||
$self->{Sql} .= "extract( hour_second from E.EndTime )";
|
||||
} elsif ( $term->{attr} eq 'EndWeekday' ) {
|
||||
$self->{Sql} .= "weekday( E.EndTime )";
|
||||
|
||||
#
|
||||
} elsif ( $term->{attr} eq 'DiskSpace' ) {
|
||||
$self->{Sql} .= 'E.DiskSpace';
|
||||
$self->{HasDiskPercent} = !undef;
|
||||
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
||||
$self->{Sql} .= "zmDiskPercent";
|
||||
$self->{Sql} .= 'zmDiskPercent';
|
||||
$self->{HasDiskPercent} = !undef;
|
||||
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
||||
$self->{Sql} .= "zmDiskBlocks";
|
||||
$self->{Sql} .= 'zmDiskBlocks';
|
||||
$self->{HasDiskBlocks} = !undef;
|
||||
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
||||
$self->{Sql} .= "zmSystemLoad";
|
||||
$self->{Sql} .= 'zmSystemLoad';
|
||||
$self->{HasSystemLoad} = !undef;
|
||||
} else {
|
||||
$self->{Sql} .= "E.".$term->{attr};
|
||||
$self->{Sql} .= 'E.'.$term->{attr};
|
||||
}
|
||||
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
|
@ -243,11 +264,11 @@ sub Sql {
|
|||
} elsif ( $term->{op} eq '!~' ) {
|
||||
$self->{Sql} .= " not in (".join( ",", @value_list ).")";
|
||||
} else {
|
||||
$self->{Sql} .= " ".$term->{op}." $value";
|
||||
$self->{Sql} .= ' '.$term->{op}." $value";
|
||||
}
|
||||
} # end if has an operator
|
||||
if ( exists($term->{cbr}) ) {
|
||||
$self->{Sql} .= " ".str_repeat( ")", $term->{cbr} )." ";
|
||||
$self->{Sql} .= ' '.str_repeat( ")", $term->{cbr} )." ";
|
||||
}
|
||||
} # end foreach term
|
||||
} # end if terms
|
||||
|
@ -284,7 +305,7 @@ sub Sql {
|
|||
push @auto_terms, "E.Executed = 0";
|
||||
}
|
||||
if ( @auto_terms ) {
|
||||
$sql .= " and ( ".join( " or ", @auto_terms )." )";
|
||||
$sql .= " and ( ".join( ' or ', @auto_terms )." )";
|
||||
}
|
||||
if ( !$filter_expr->{sort_field} ) {
|
||||
$filter_expr->{sort_field} = 'StartTime';
|
||||
|
@ -292,30 +313,34 @@ sub Sql {
|
|||
}
|
||||
my $sort_column = '';
|
||||
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
||||
$sort_column = "E.Id";
|
||||
$sort_column = 'E.Id';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) {
|
||||
$sort_column = "M.Name";
|
||||
$sort_column = 'M.Name';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'Name' ) {
|
||||
$sort_column = "E.Name";
|
||||
$sort_column = 'E.Name';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'StartTime' ) {
|
||||
$sort_column = "E.StartTime";
|
||||
$sort_column = 'E.StartTime';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'EndTime' ) {
|
||||
$sort_column = 'E.EndTime';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'Secs' ) {
|
||||
$sort_column = "E.Length";
|
||||
$sort_column = 'E.Length';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'Frames' ) {
|
||||
$sort_column = "E.Frames";
|
||||
$sort_column = 'E.Frames';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) {
|
||||
$sort_column = "E.AlarmFrames";
|
||||
$sort_column = 'E.AlarmFrames';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'TotScore' ) {
|
||||
$sort_column = "E.TotScore";
|
||||
$sort_column = 'E.TotScore';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) {
|
||||
$sort_column = "E.AvgScore";
|
||||
$sort_column = 'E.AvgScore';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) {
|
||||
$sort_column = "E.MaxScore";
|
||||
$sort_column = 'E.MaxScore';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
|
||||
$sort_column = 'E.DiskSpace';
|
||||
} else {
|
||||
$sort_column = "E.StartTime";
|
||||
$sort_column = 'E.StartTime';
|
||||
}
|
||||
my $sort_order = $filter_expr->{sort_asc}?"asc":"desc";
|
||||
$sql .= " order by ".$sort_column." ".$sort_order;
|
||||
my $sort_order = $filter_expr->{sort_asc}?'asc':'desc';
|
||||
$sql .= ' order by '.$sort_column." ".$sort_order;
|
||||
if ( $filter_expr->{limit} ) {
|
||||
$sql .= " limit 0,".$filter_expr->{limit};
|
||||
}
|
||||
|
@ -325,7 +350,7 @@ sub Sql {
|
|||
} # end sub Sql
|
||||
|
||||
sub getDiskPercent {
|
||||
my $command = "df " . ($_[0] ? $_[0] : '.');
|
||||
my $command = 'df ' . ($_[0] ? $_[0] : '.');
|
||||
my $df = qx( $command );
|
||||
my $space = -1;
|
||||
if ( $df =~ /\s(\d+)%/ms ) {
|
||||
|
@ -335,7 +360,7 @@ sub getDiskPercent {
|
|||
}
|
||||
|
||||
sub getDiskBlocks {
|
||||
my $command = "df .";
|
||||
my $command = 'df .';
|
||||
my $df = qx( $command );
|
||||
my $space = -1;
|
||||
if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) {
|
||||
|
@ -345,7 +370,7 @@ sub getDiskBlocks {
|
|||
}
|
||||
|
||||
sub getLoad {
|
||||
my $command = "uptime .";
|
||||
my $command = 'uptime .';
|
||||
my $uptime = qx( $command );
|
||||
my $load = -1;
|
||||
if ( $uptime =~ /load average:\s+([\d.]+)/ms ) {
|
||||
|
|
|
@ -678,6 +678,8 @@ sub Dump {
|
|||
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) );
|
||||
}
|
||||
|
||||
sub debug { fetch()->logPrint( DEBUG, @_ ); }
|
||||
|
||||
sub Debug( @ ) {
|
||||
fetch()->logPrint( DEBUG, @_ );
|
||||
}
|
||||
|
@ -685,14 +687,24 @@ sub Debug( @ ) {
|
|||
sub Info( @ ) {
|
||||
fetch()->logPrint( INFO, @_ );
|
||||
}
|
||||
sub info {
|
||||
fetch()->logPrint( INFO, @_ );
|
||||
}
|
||||
|
||||
|
||||
sub Warning( @ ) {
|
||||
fetch()->logPrint( WARNING, @_ );
|
||||
}
|
||||
sub warn {
|
||||
fetch()->logPrint( WARNING, @_ );
|
||||
}
|
||||
|
||||
sub Error( @ ) {
|
||||
fetch()->logPrint( ERROR, @_ );
|
||||
}
|
||||
sub error {
|
||||
fetch()->logPrint( ERROR, @_ );
|
||||
}
|
||||
|
||||
sub Fatal( @ ) {
|
||||
fetch()->logPrint( FATAL, @_ );
|
||||
|
|
|
@ -42,7 +42,13 @@ use ZoneMinder::Config qw(:all);
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Database qw(:all);
|
||||
|
||||
use vars qw/ $AUTOLOAD /;
|
||||
use vars qw/ $AUTOLOAD $log $dbh/;
|
||||
|
||||
*log = \$ZoneMinder::Logger::logger;
|
||||
*dbh = \$ZoneMinder::Database::dbh;
|
||||
|
||||
my $debug = 1;
|
||||
use constant DEBUG_ALL=>0;
|
||||
|
||||
sub new {
|
||||
my ( $parent, $id, $data ) = @_;
|
||||
|
@ -110,7 +116,269 @@ sub AUTOLOAD {
|
|||
return $_[0]{$name};
|
||||
}
|
||||
|
||||
sub save {
|
||||
my ( $self, $data, $force_insert ) = @_;
|
||||
|
||||
my $type = ref $self;
|
||||
if ( ! $type ) {
|
||||
my ( $caller, undef, $line ) = caller;
|
||||
$log->error("No type in Object::save. self:$self from $caller:$line");
|
||||
}
|
||||
my $local_dbh = eval '$'.$type.'::dbh';
|
||||
$local_dbh = $ZoneMinder::Database::dbh if ! $local_dbh;
|
||||
$self->set( $data ? $data : {} );
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
if ( $data ) {
|
||||
foreach my $k ( keys %$data ) {
|
||||
$log->debug("Object::save after set $k => $$data{$k} $$self{$k}");
|
||||
}
|
||||
} else {
|
||||
$log->debug("No data after set");
|
||||
}
|
||||
}
|
||||
#$debug = 0;
|
||||
|
||||
my $table = eval '$'.$type.'::table';
|
||||
my $fields = eval '\%'.$type.'::fields';
|
||||
my $debug = eval '$'.$type.'::debug';
|
||||
#$debug = DEBUG_ALL if ! $debug;
|
||||
|
||||
my %sql;
|
||||
foreach my $k ( keys %$fields ) {
|
||||
$sql{$$fields{$k}} = $$self{$k} if defined $$fields{$k};
|
||||
} # end foreach
|
||||
if ( ! $force_insert ) {
|
||||
$sql{$$fields{updated_on}} = 'NOW()' if exists $$fields{updated_on};
|
||||
} # end if
|
||||
my $serial = eval '$'.$type.'::serial';
|
||||
my @identified_by = eval '@'.$type.'::identified_by';
|
||||
|
||||
my $ac = sql::start_transaction( $local_dbh );
|
||||
if ( ! $serial ) {
|
||||
my $insert = $force_insert;
|
||||
my %serial = eval '%'.$type.'::serial';
|
||||
if ( ! %serial ) {
|
||||
$log->debug("No serial") if $debug;
|
||||
# No serial columns defined, which means that we will do saving by delete/insert instead of insert/update
|
||||
if ( @identified_by ) {
|
||||
my $where = join(' AND ', map { $$fields{$_}.'=?' } @identified_by );
|
||||
if ( $debug ) {
|
||||
$log->debug("DELETE FROM $table WHERE $where");
|
||||
} # end if
|
||||
|
||||
if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM $table WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
|
||||
$where =~ s/\?/\%s/g;
|
||||
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
sql::end_transaction( $local_dbh, $ac );
|
||||
return $local_dbh->errstr;
|
||||
} elsif ( $debug ) {
|
||||
$log->debug("SQL succesful DELETE FROM $table WHERE $where");
|
||||
} # end if
|
||||
} # end if
|
||||
$insert = 1;
|
||||
} else {
|
||||
foreach my $id ( @identified_by ) {
|
||||
if ( ! $serial{$id} ) {
|
||||
my ( $caller, undef, $line ) = caller;
|
||||
$log->error("$id nor in serial for $type from $caller:$line") if $debug;
|
||||
next;
|
||||
}
|
||||
if ( ! $$self{$id} ) {
|
||||
($$self{$id}) = ($sql{$$fields{$id}}) = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial{$id} . q{')} );
|
||||
$log->debug("SQL statement execution SELECT nextval('$serial{$id}') returned $$self{$id}") if $debug or DEBUG_ALL;
|
||||
$insert = 1;
|
||||
} # end if
|
||||
} # end foreach
|
||||
} # end if ! %serial
|
||||
|
||||
if ( $insert ) {
|
||||
my @keys = keys %sql;
|
||||
my $command = "INSERT INTO $table (" . join(',', @keys ) . ') VALUES (' . join(',', map { '?' } @sql{@keys} ) . ')';
|
||||
if ( ! ( ( $_ = $local_dbh->prepare($command) ) and $_->execute( @sql{@keys} ) ) ) {
|
||||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
sql::end_transaction( $local_dbh, $ac );
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->debug('SQL statement execution: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys} ) ).'):' );
|
||||
} # end if
|
||||
} else {
|
||||
my @keys = keys %sql;
|
||||
my $command = "UPDATE $table SET " . join(',', map { $_ . ' = ?' } @keys ) . ' WHERE ' . join(' AND ', map { $_ . ' = ?' } @$fields{@identified_by} );
|
||||
if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys,@$fields{@identified_by}} ) ) ) {
|
||||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
sql::end_transaction( $local_dbh, $ac );
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys,@$fields{@identified_by}} ) ).'):' );
|
||||
} # end if
|
||||
} # end if
|
||||
} else { # not identified_by
|
||||
@identified_by = ('id') if ! @identified_by;
|
||||
my $need_serial = ! ( @identified_by == map { $$self{$_} ? $_ : () } @identified_by );
|
||||
|
||||
if ( $force_insert or $need_serial ) {
|
||||
|
||||
if ( $need_serial ) {
|
||||
if ( $serial ) {
|
||||
@$self{@identified_by} = @sql{@$fields{@identified_by}} = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial . q{')} );
|
||||
if ( $local_dbh->errstr() ) {
|
||||
$log->error("Error getting next id. " . $local_dbh->errstr() );
|
||||
$log->error("SQL statement execution SELECT nextval('$serial') returned ".join(',',@$self{@identified_by}));
|
||||
} elsif ( $debug or DEBUG_ALL ) {
|
||||
$log->debug("SQL statement execution SELECT nextval('$serial') returned ".join(',',@$self{@identified_by}));
|
||||
} # end if
|
||||
} # end if
|
||||
} # end if
|
||||
my @keys = keys %sql;
|
||||
my $command = "INSERT INTO $table (" . join(',', @keys ) . ') VALUES (' . join(',', map { '?' } @sql{@keys} ) . ')';
|
||||
if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys} ) ) ) {
|
||||
$command =~ s/\?/\%s/g;
|
||||
my $error = $local_dbh->errstr;
|
||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
|
||||
$local_dbh->rollback();
|
||||
sql::end_transaction( $local_dbh, $ac );
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys} ) ).'):' );
|
||||
} # end if
|
||||
} else {
|
||||
delete $sql{created_on};
|
||||
my @keys = keys %sql;
|
||||
@keys = sets::exclude( [ @$fields{@identified_by} ], \@keys );
|
||||
my $command = "UPDATE $table SET " . join(',', map { $_ . ' = ?' } @keys ) . ' WHERE ' . join(' AND ', map { $$fields{$_} .'= ?' } @identified_by );
|
||||
if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys}, @sql{@$fields{@identified_by}} ) ) ) {
|
||||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
|
||||
$local_dbh->rollback();
|
||||
sql::end_transaction( $local_dbh, $ac );
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? ( ref $_ eq 'ARRAY' ? join(',',@{$_}) : $_ ) : 'undef' } ( @sql{@keys}, @$self{@identified_by} ) ).'):' );
|
||||
} # end if
|
||||
} # end if
|
||||
} # end if
|
||||
sql::end_transaction( $local_dbh, $ac );
|
||||
$self->load();
|
||||
#if ( $$fields{id} ) {
|
||||
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
||||
#$ZoneMinder::Object::cache{$type}{$$self{id}} = $self;
|
||||
#} # end if
|
||||
#delete $ZoneMinder::Object::cache{$config{db_name}}{$type}{$$self{id}};
|
||||
#} # end if
|
||||
#$log->debug("after delete");
|
||||
#eval 'if ( %'.$type.'::find_cache ) { %'.$type.'::find_cache = (); }';
|
||||
#$log->debug("after clear cache");
|
||||
return '';
|
||||
} # end sub save
|
||||
|
||||
sub set {
|
||||
my ( $self, $params ) = @_;
|
||||
my @set_fields = ();
|
||||
|
||||
my $type = ref $self;
|
||||
my %fields = eval ('%'.$type.'::fields');
|
||||
if ( ! %fields ) {
|
||||
$log->warn('ZoneMinder::Object::set called on an object with no fields');
|
||||
} # end if
|
||||
my %defaults = eval('%'.$type.'::defaults');
|
||||
if ( ref $params ne 'HASH' ) {
|
||||
my ( $caller, undef, $line ) = caller;
|
||||
$openprint::log->error("$type -> set called with non-hash params from $caller $line");
|
||||
}
|
||||
|
||||
foreach my $field ( keys %fields ) {
|
||||
$log->debug("field: $field, param: ".$$params{$field}) if $debug;
|
||||
if ( exists $$params{$field} ) {
|
||||
$openprint::log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug;
|
||||
if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) {
|
||||
# Only make changes to fields that have changed
|
||||
if ( defined $fields{$field} ) {
|
||||
$$self{$field} = $$params{$field} if defined $fields{$field};
|
||||
push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating
|
||||
} # end if
|
||||
$openprint::log->debug("Running $field with $$params{$field}") if $debug;
|
||||
if ( my $func = $self->can( $field ) ) {
|
||||
$func->( $self, $$params{$field} );
|
||||
} # end if
|
||||
} # end if
|
||||
} # end if
|
||||
|
||||
if ( defined $fields{$field} ) {
|
||||
if ( $$self{$field} ) {
|
||||
$$self{$field} = transform( $type, $field, $$self{$field} );
|
||||
} # end if $$self{field}
|
||||
}
|
||||
} # end foreach field
|
||||
|
||||
foreach my $field ( keys %defaults ) {
|
||||
|
||||
if ( ( ! exists $$self{$field} ) or (!defined $$self{$field}) or ( $$self{$field} eq '' ) ) {
|
||||
$log->debug("Setting default ($field) ($$self{$field}) ($defaults{$field}) ") if $debug;
|
||||
if ( defined $defaults{$field} ) {
|
||||
$log->debug("Default $field is defined: $defaults{$field}") if $debug;
|
||||
if ( $defaults{$field} eq 'NOW()' ) {
|
||||
$$self{$field} = 'NOW()';
|
||||
} else {
|
||||
$$self{$field} = eval($defaults{$field});
|
||||
$log->error( "Eval error of object default $field default ($defaults{$field}) Reason: " . $@ ) if $@;
|
||||
} # end if
|
||||
} else {
|
||||
$$self{$field} = $defaults{$field};
|
||||
} # end if
|
||||
#$$self{$field} = ( defined $defaults{$field} ) ? eval($defaults{$field}) : $defaults{$field};
|
||||
$log->debug("Setting default for ($field) using ($defaults{$field}) to ($$self{$field}) ") if $debug;
|
||||
} # end if
|
||||
} # end foreach default
|
||||
return @set_fields;
|
||||
} # end sub set
|
||||
|
||||
sub transform {
|
||||
my $type = ref $_[0];
|
||||
$type = $_[0] if ! $type;
|
||||
my $fields = eval '\%'.$type.'::fields';
|
||||
my $value = $_[2];
|
||||
|
||||
if ( defined $$fields{$_[1]} ) {
|
||||
my @transforms = eval('@{$'.$type.'::transforms{$_[1]}}');
|
||||
$openprint::log->debug("Transforms for $_[1] before $_[2]: @transforms") if $debug;
|
||||
if ( @transforms ) {
|
||||
foreach my $transform ( @transforms ) {
|
||||
if ( $transform =~ /^s\// or $transform =~ /^tr\// ) {
|
||||
eval '$value =~ ' . $transform;
|
||||
} elsif ( $transform =~ /^<(\d+)/ ) {
|
||||
if ( $value > $1 ) {
|
||||
$value = undef;
|
||||
} # end if
|
||||
} else {
|
||||
$openprint::log->debug("evalling $value ".$transform . " Now value is $value" );
|
||||
eval '$value '.$transform;
|
||||
$openprint::log->error("Eval error $@") if $@;
|
||||
}
|
||||
$openprint::log->debug("After $transform: $value") if $debug;
|
||||
} # end foreach
|
||||
} # end if
|
||||
} else {
|
||||
$openprint::log->error("Object::transform ($_[1]) not in fields for $type");
|
||||
} # end if
|
||||
return $value;
|
||||
|
||||
} # end sub transform
|
||||
1;
|
||||
__END__
|
||||
|
||||
|
|
|
@ -214,6 +214,7 @@ sub getFilters {
|
|||
or AutoMessage = 1
|
||||
or AutoExecute = 1
|
||||
or AutoDelete = 1
|
||||
or UpdateDiskSpace = 1
|
||||
) ORDER BY Name';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||
|
|
|
@ -168,7 +168,6 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
|
|||
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" );
|
||||
snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name );
|
||||
Debug(1,"Writing video file to %s", video_file );
|
||||
|
||||
#if 0
|
||||
/* X264 MP4 video writer */
|
||||
if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) {
|
||||
|
@ -200,8 +199,8 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
|
|||
} else {
|
||||
/* No video object */
|
||||
videowriter = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
|
||||
|
||||
|
@ -239,7 +238,7 @@ Event::~Event() {
|
|||
Error( "Can't update event: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
}
|
||||
} // ~Event
|
||||
|
||||
void Event::createNotes( std::string ¬es ) {
|
||||
notes.clear();
|
||||
|
|
|
@ -113,22 +113,6 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
|||
mOpenStart = 0;
|
||||
mReopenThread = 0;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
mConvertContext = NULL;
|
||||
#endif
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if ( colours == ZM_COLOUR_RGB32 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||
} else if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
} // end FFmpegCamera::FFmpegCamera
|
||||
|
||||
FfmpegCamera::~FfmpegCamera() {
|
||||
|
@ -218,94 +202,6 @@ ZMPacket * FfmpegCamera::Capture( Image &image ) {
|
|||
}
|
||||
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
|
||||
|
||||
#if 0
|
||||
// What about audio stream? Maybe someday we could do sound detection...
|
||||
if ( ( packet.stream_index == mVideoStreamId ) && ( keyframe || have_video_keyframe ) ) {
|
||||
#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 );
|
||||
Error( "Unable to send packet at 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
|
||||
|
||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||
|
||||
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.");
|
||||
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 HAVE_LIBSWSCALE
|
||||
if ( sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0 )
|
||||
Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
frameCount++;
|
||||
} // end if frameComplete
|
||||
} else {
|
||||
Debug( 4, "Different stream_index %d", packet.stream_index );
|
||||
} // end if packet.stream_index == mVideoStreamId
|
||||
#endif
|
||||
zm_packet = new ZMPacket( &packet );
|
||||
zm_av_packet_unref( &packet );
|
||||
return zm_packet;
|
||||
|
@ -517,6 +413,18 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
}
|
||||
}
|
||||
|
||||
if ( mVideoCodecContext->codec_id != AV_CODEC_ID_H264 ) {
|
||||
#ifdef AV_CODEC_ID_H265
|
||||
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H265 ) {
|
||||
Debug( 1, "Input stream appears to be h265. The stored event file may not be viewable in browser." );
|
||||
} else {
|
||||
#endif
|
||||
Warning( "Input stream is not h264. The stored event file may not be viewable in browser." );
|
||||
#ifdef AV_CODEC_ID_H265
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mVideoCodecContext->hwaccel != NULL) {
|
||||
Debug(1, "HWACCEL in use");
|
||||
} else {
|
||||
|
@ -551,51 +459,11 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
// Allocate space for the native video frame
|
||||
mRawFrame = zm_av_frame_alloc();
|
||||
|
||||
// Allocate space for the converted video frame
|
||||
mFrame = zm_av_frame_alloc();
|
||||
|
||||
if ( mRawFrame == NULL || mFrame == NULL )
|
||||
if ( mRawFrame == NULL )
|
||||
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Allocated frames" );
|
||||
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 );
|
||||
#else
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
#endif
|
||||
|
||||
if ( (unsigned int)pSize != imagesize ) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
|
||||
Debug ( 1, "Validated imagesize" );
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
||||
if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
|
||||
Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
|
||||
}
|
||||
|
||||
if ( !sws_isSupportedOutput(imagePixFormat) ) {
|
||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||
}
|
||||
|
||||
mConvertContext = sws_getContext(mVideoCodecContext->width,
|
||||
mVideoCodecContext->height,
|
||||
mVideoCodecContext->pix_fmt,
|
||||
width, height,
|
||||
imagePixFormat, SWS_BICUBIC, NULL,
|
||||
NULL, NULL);
|
||||
if ( mConvertContext == NULL )
|
||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) {
|
||||
Warning( "Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height );
|
||||
}
|
||||
|
||||
mCanCapture = true;
|
||||
|
||||
|
@ -636,12 +504,6 @@ int FfmpegCamera::CloseFfmpeg() {
|
|||
mRawFrame = NULL;
|
||||
}
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if ( mConvertContext ) {
|
||||
sws_freeContext( mConvertContext );
|
||||
mConvertContext = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( mVideoCodecContext ) {
|
||||
avcodec_close(mVideoCodecContext);
|
||||
|
@ -664,7 +526,7 @@ int FfmpegCamera::CloseFfmpeg() {
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // end int FfmpegCamera::CloseFfmpeg()
|
||||
|
||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||
Debug(3,"FfmpegInteruptCallback");
|
||||
|
@ -678,9 +540,9 @@ int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // end int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
|
||||
|
||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx) {
|
||||
Debug(3,"FfmpegReopenThreadtCallback");
|
||||
if ( ctx == NULL ) return NULL;
|
||||
|
||||
|
@ -702,6 +564,47 @@ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx)
|
||||
|
||||
/*
|
||||
Responsible for populating the decoded frame member of the zm_packet.
|
||||
*/
|
||||
Image * FfmpegCamera::decode( Image *i = NULL, _AVPIXELFORMAT imagePixFormat, struct SwsContext *mConvertContext ) {
|
||||
|
||||
if ( ! frame ) {
|
||||
Error("Can't get image without frame.. maybe need to decode first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( ! image ) {
|
||||
if ( ! i ) {
|
||||
Error("Need a pre-allocated image buffer");
|
||||
return NULL;
|
||||
}
|
||||
image = i;
|
||||
}
|
||||
/* Request a writeable buffer of the target image */
|
||||
uint8_t* directbuffer = image->WriteBuffer();
|
||||
//uint8_t* directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if ( directbuffer == NULL ) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
image = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// mFrame is an intermediate.
|
||||
AVFrame *mFrame = zm_av_frame_alloc();
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, frame->width, frame->height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, frame->width, frame->height);
|
||||
#endif
|
||||
if (sws_scale(mConvertContext, frame->data, frame->linesize,
|
||||
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
||||
Fatal("Unable to convert raw format %u to target format %u",
|
||||
mVideoCodecContext->pix_fmt, imagePixFormat);
|
||||
}
|
||||
av_frame_free( &mFrame );
|
||||
return image;
|
||||
}
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -40,6 +40,7 @@ class FfmpegCamera : public Camera {
|
|||
std::string mPath;
|
||||
std::string mMethod;
|
||||
std::string mOptions;
|
||||
std::string encoder_options;
|
||||
|
||||
int frameCount;
|
||||
|
||||
|
@ -51,7 +52,6 @@ class FfmpegCamera : public Camera {
|
|||
AVCodec *mAudioCodec;
|
||||
AVFrame *mRawFrame;
|
||||
AVFrame *mFrame;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
|
||||
bool hwaccel;
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
|
@ -82,9 +82,6 @@ class FfmpegCamera : public Camera {
|
|||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
struct SwsContext *mConvertContext;
|
||||
#endif
|
||||
|
||||
int64_t startTime;
|
||||
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
|
||||
#include "zm_ffmpeg_input.h"
|
||||
#include "zm_logger.h"
|
||||
#include "zm_ffmpeg.h"
|
||||
|
||||
FFmpeg_Output::FFmpeg_Output() {
|
||||
input_format_context = NULL;
|
||||
video_stream_id = -1;
|
||||
audio_stream_id = -1;
|
||||
av_register_all();
|
||||
avcodec_register_all();
|
||||
|
||||
}
|
||||
FFmpeg_Output::~FFmpeg_Output() {
|
||||
}
|
||||
|
||||
int FFmpeg_Output::Open( const char *filepath ) {
|
||||
|
||||
int error;
|
||||
|
||||
/** Open the input file to read from it. */
|
||||
if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) {
|
||||
|
||||
Error("Could not open input file '%s' (error '%s')\n",
|
||||
filepath, av_make_error_string(error).c_str() );
|
||||
input_format_context = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
/** Get information on the input file (number of streams etc.). */
|
||||
if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) {
|
||||
Error( "Could not open find stream info (error '%s')\n",
|
||||
av_make_error_string(error).c_str() );
|
||||
avformat_close_input(&input_format_context);
|
||||
return error;
|
||||
}
|
||||
|
||||
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
|
||||
if ( is_video_stream( input_format_context->streams[i] ) ) {
|
||||
zm_dump_stream_format(input_format_context, i, 0, 0);
|
||||
if ( video_stream_id == -1 ) {
|
||||
video_stream_id = i;
|
||||
// if we break, then we won't find the audio stream
|
||||
} else {
|
||||
Warning( "Have another video stream." );
|
||||
}
|
||||
} else if ( is_audio_stream( input_format_context->streams[i] ) ) {
|
||||
if ( audio_stream_id == -1 ) {
|
||||
audio_stream_id = i;
|
||||
} else {
|
||||
Warning( "Have another audio stream." );
|
||||
}
|
||||
}
|
||||
|
||||
streams[i].frame_count = 0;
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
streams[i].context = avcodec_alloc_context3( NULL );
|
||||
avcodec_parameters_to_context( streams[i].context, input_format_context->streams[i]->codecpar );
|
||||
#else
|
||||
streams[i].context = input_format_context->streams[i]->codec;
|
||||
#endif
|
||||
|
||||
if ( !(streams[i].codec = avcodec_find_decoder(streams[i].context->codec_id)) ) {
|
||||
Error( "Could not find input codec\n");
|
||||
avformat_close_input(&input_format_context);
|
||||
return AVERROR_EXIT;
|
||||
} else {
|
||||
Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i );
|
||||
}
|
||||
|
||||
if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) {
|
||||
Error( "Could not open input codec (error '%s')\n",
|
||||
av_make_error_string(error).c_str() );
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
avcodec_free_context( &streams[i].context );
|
||||
#endif
|
||||
avformat_close_input(&input_format_context);
|
||||
return error;
|
||||
}
|
||||
} // end foreach stream
|
||||
|
||||
if ( video_stream_id == -1 )
|
||||
Error( "Unable to locate video stream in %s", filepath );
|
||||
if ( audio_stream_id == -1 )
|
||||
Debug( 3, "Unable to locate audio stream in %s", filepath );
|
||||
|
||||
return 0;
|
||||
} // end int FFmpeg_Output::Open( const char * filepath )
|
||||
|
||||
AVFrame *FFmpeg_Output::get_frame( int stream_id ) {
|
||||
Debug(1, "Getting frame from stream %d", stream_id );
|
||||
|
||||
int frameComplete = false;
|
||||
AVPacket packet;
|
||||
av_init_packet( &packet );
|
||||
AVFrame *frame = zm_av_frame_alloc();
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
|
||||
while ( !frameComplete ) {
|
||||
int ret = av_read_frame( input_format_context, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
if (
|
||||
// Check if EOF.
|
||||
(ret == AVERROR_EOF || (input_format_context->pb && input_format_context->pb->eof_reached)) ||
|
||||
// Check for Connection failure.
|
||||
(ret == -110)
|
||||
) {
|
||||
Info( "av_read_frame returned %s.", errbuf );
|
||||
return NULL;
|
||||
}
|
||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
|
||||
Debug(1,"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
|
||||
}
|
||||
#endif
|
||||
|
||||
frameComplete = 1;
|
||||
# else
|
||||
ret = zm_avcodec_decode_video( streams[packet.stream_index].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 );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
} // end if it's the right stream
|
||||
|
||||
zm_av_packet_unref( &packet );
|
||||
|
||||
} // end while ! frameComplete
|
||||
return frame;
|
||||
|
||||
} // end AVFrame *FFmpeg_Output::get_frame
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef ZM_FFMPEG_INPUT_H
|
||||
#define ZM_FFMPEG_INPUT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libavformat/avio.h"
|
||||
#include "libavcodec/avcodec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
class FFmpeg_Output {
|
||||
|
||||
public:
|
||||
FFmpeg_Output();
|
||||
~FFmpeg_Output();
|
||||
|
||||
int Open( const char *filename );
|
||||
int Close();
|
||||
AVFrame *put_frame( int stream_id=-1 );
|
||||
AVFrame *put_packet( int stream_id=-1 );
|
||||
int get_video_stream_id() {
|
||||
return video_stream_id;
|
||||
}
|
||||
int get_audio_stream_id() {
|
||||
return audio_stream_id;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
AVCodecContext *context;
|
||||
AVCodec *codec;
|
||||
int frame_count;
|
||||
} stream;
|
||||
|
||||
stream streams[2];
|
||||
int video_stream_id;
|
||||
int audio_stream_id;
|
||||
AVFormatContext *input_format_context;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -241,6 +241,7 @@ Monitor::Monitor(
|
|||
int p_orientation,
|
||||
unsigned int p_deinterlacing,
|
||||
int p_savejpegs,
|
||||
int p_colours,
|
||||
VideoWriter p_videowriter,
|
||||
std::string p_encoderparams,
|
||||
bool p_record_audio,
|
||||
|
@ -280,6 +281,7 @@ Monitor::Monitor(
|
|||
orientation( (Orientation)p_orientation ),
|
||||
deinterlacing( p_deinterlacing ),
|
||||
savejpegspref( p_savejpegs ),
|
||||
colours( p_colours ),
|
||||
videowriter( p_videowriter ),
|
||||
encoderparams( p_encoderparams ),
|
||||
record_audio( p_record_audio ),
|
||||
|
@ -336,6 +338,49 @@ Monitor::Monitor(
|
|||
|
||||
/* Parse encoder parameters */
|
||||
ParseEncoderParameters(encoderparams.c_str(), &encoderparamsvec);
|
||||
#if HAVE_LIBSWSCALE
|
||||
mConvertContext = NULL;
|
||||
#endif
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if ( colours == ZM_COLOUR_RGB32 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||
} else if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
#if HAVE_LIBSWSCALE
|
||||
//FIXME, need to be able to query the camera input for what it is going to be getting, which needs to be called after the camera is open.
|
||||
//Debug ( 1, "Calling sws_isSupportedInput" );
|
||||
//if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
|
||||
//Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
|
||||
//}
|
||||
|
||||
if ( !sws_isSupportedOutput(imagePixFormat) ) {
|
||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||
}
|
||||
|
||||
// We don't know this yet, need to open the camera first.
|
||||
//mConvertContext = sws_getContext(mVideoCodecContext->width,
|
||||
//mVideoCodecContext->height,
|
||||
//mVideoCodecContext->pix_fmt,
|
||||
//width, height,
|
||||
//imagePixFormat, SWS_BICUBIC, NULL,
|
||||
//NULL, NULL);
|
||||
//if ( mConvertContext == NULL )
|
||||
//Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||
#else // HAVE_LIBSWSCALE
|
||||
//Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
//if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) {
|
||||
//Warning( "Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height );
|
||||
//}
|
||||
|
||||
fps = 0.0;
|
||||
event_count = 0;
|
||||
|
@ -568,6 +613,12 @@ Monitor::~Monitor() {
|
|||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
}
|
||||
#if HAVE_LIBSWSCALE
|
||||
if ( mConvertContext ) {
|
||||
sws_freeContext( mConvertContext );
|
||||
mConvertContext = NULL;
|
||||
}
|
||||
#endif
|
||||
if ( timestamps ) {
|
||||
delete[] timestamps;
|
||||
timestamps = 0;
|
||||
|
@ -1126,7 +1177,7 @@ bool Monitor::CheckSignal( const Image *image ) {
|
|||
return true;
|
||||
}
|
||||
|
||||
} else if(colours == ZM_COLOUR_RGB32) {
|
||||
} else if ( colours == ZM_COLOUR_RGB32 ) {
|
||||
if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) )
|
||||
return true;
|
||||
|
@ -1359,12 +1410,12 @@ Debug(3,"before DetectMotion");
|
|||
if ( event ) {
|
||||
//TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here??
|
||||
//snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||
Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
|
||||
//Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
|
||||
|
||||
if ( section_length ) {
|
||||
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
|
||||
int section_mod = timestamp->tv_sec % section_length;
|
||||
Debug( 3, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
|
||||
Debug( 4, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
|
||||
if ( section_mod < last_section_mod ) {
|
||||
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
|
||||
//if ( state == TAPE ) {
|
||||
|
@ -1976,6 +2027,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose
|
|||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
colours,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
record_audio,
|
||||
|
@ -2160,6 +2212,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
|
|||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
colours,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
record_audio,
|
||||
|
@ -2309,6 +2362,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
|
|||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
colours,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
record_audio,
|
||||
|
@ -2468,6 +2522,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
|||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
colours,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
record_audio,
|
||||
|
@ -2795,6 +2850,7 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
|
|||
orientation,
|
||||
deinterlacing,
|
||||
savejpegs,
|
||||
colours,
|
||||
videowriter,
|
||||
encoderparams,
|
||||
record_audio,
|
||||
|
|
|
@ -240,9 +240,15 @@ protected:
|
|||
bool videoRecording;
|
||||
|
||||
int savejpegspref;
|
||||
int colours;
|
||||
VideoWriter videowriter;
|
||||
std::string encoderparams;
|
||||
std::vector<EncoderParameter_t> encoderparamsvec;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
unsigned int subpixelorder;
|
||||
#if HAVE_LIBSWSCALE
|
||||
struct SwsContext *mConvertContext;
|
||||
#endif
|
||||
bool record_audio; // Whether to store the audio that we receive
|
||||
|
||||
int brightness; // The statically saved brightness of the camera
|
||||
|
@ -348,6 +354,7 @@ public:
|
|||
int p_orientation,
|
||||
unsigned int p_deinterlacing,
|
||||
int p_savejpegs,
|
||||
int p_colours,
|
||||
VideoWriter p_videowriter,
|
||||
std::string p_encoderparams,
|
||||
bool p_record_audio,
|
||||
|
@ -434,6 +441,7 @@ public:
|
|||
int GetOptSaveJPEGs() const { return( savejpegspref ); }
|
||||
VideoWriter GetOptVideoWriter() const { return( videowriter ); }
|
||||
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
|
||||
const std::string &GetEncoderOptions() const { return( encoderparams ); }
|
||||
uint32_t GetLastEventId() const { return shared_data->last_event_id; }
|
||||
uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; }
|
||||
void SetVideoWriterEventId( uint32_t p_event_id ) { video_store_data->current_event = p_event_id; }
|
||||
|
|
|
@ -123,41 +123,4 @@ int ZMPacket::decode( AVCodecContext *ctx ) {
|
|||
return 1;
|
||||
} // end ZMPacket::decode
|
||||
|
||||
Image * ZMPacket::get_image( Image *i = NULL ) {
|
||||
|
||||
if ( ! frame ) {
|
||||
Error("Can't get image without frame.. maybe need to decode first");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( ! image ) {
|
||||
if ( ! i ) {
|
||||
Error("Need a pre-allocated image buffer");
|
||||
return NULL;
|
||||
}
|
||||
image = i;
|
||||
}
|
||||
/* Request a writeable buffer of the target image */
|
||||
uint8_t* directbuffer = image->WriteBuffer();
|
||||
//uint8_t* directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if ( directbuffer == NULL ) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
image = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AVFrame *mFrame = zm_av_frame_alloc();
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, frame->width, frame->height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, frame->width, frame->height);
|
||||
#endif
|
||||
if (sws_scale(mConvertContext, frame->data, frame->linesize,
|
||||
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
||||
Fatal("Unable to convert raw format %u to target format %u",
|
||||
mVideoCodecContext->pix_fmt, imagePixFormat);
|
||||
}
|
||||
av_frame_free( &mFrame );
|
||||
return image;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class ZMPacket {
|
|||
public:
|
||||
AVPacket *av_packet() { return &packet; }
|
||||
AVFrame *av_frame() { return frame; }
|
||||
Image *get_image( Image * );
|
||||
Image *get_image( Image *, _AVPIXELFORMAT imagePixFormat, struct SwsContext *mConvertContext );
|
||||
int is_keyframe() { return keyframe; };
|
||||
int decode( AVCodecContext *ctx );
|
||||
ZMPacket( AVPacket *packet, struct timeval *timestamp );
|
||||
|
|
|
@ -31,10 +31,12 @@ extern "C" {
|
|||
#include "libavutil/time.h"
|
||||
}
|
||||
|
||||
VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||
AVStream *p_video_in_stream,
|
||||
AVStream *p_audio_in_stream,
|
||||
Monitor *monitor) {
|
||||
VideoStore::VideoStore(
|
||||
const char *filename_in, const char *format_in,
|
||||
AVStream *p_video_in_stream,
|
||||
AVStream *p_audio_in_stream, int64_t nStartTime,
|
||||
Monitor *monitor
|
||||
) {
|
||||
video_in_stream = p_video_in_stream;
|
||||
audio_in_stream = p_audio_in_stream;
|
||||
|
||||
|
@ -50,9 +52,12 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
// store ins in variables local to class
|
||||
filename = filename_in;
|
||||
format = format_in;
|
||||
packets_written = 0;
|
||||
frame_count = 0;
|
||||
|
||||
Info("Opening video storage stream %s format: %s", filename, format);
|
||||
|
||||
#if 0
|
||||
ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename);
|
||||
if (ret < 0) {
|
||||
Warning(
|
||||
|
@ -65,6 +70,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
|
||||
// Couldn't deduce format from filename, trying from format name
|
||||
if (!oc) {
|
||||
#endif
|
||||
avformat_alloc_output_context2(&oc, NULL, format, filename);
|
||||
if (!oc) {
|
||||
Fatal(
|
||||
|
@ -72,9 +78,11 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
" could not be assigned based on filename or format %s",
|
||||
filename, format);
|
||||
} else {
|
||||
Debug(4, "Success alocateing out ctx");
|
||||
Debug(4, "Success alocating out ctx");
|
||||
}
|
||||
#if 0
|
||||
} // end if ! oc
|
||||
#endif
|
||||
|
||||
AVDictionary *pmetadata = NULL;
|
||||
int dsr =
|
||||
|
@ -84,15 +92,14 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
oc->metadata = pmetadata;
|
||||
out_format = oc->oformat;
|
||||
|
||||
in_frame = NULL;
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
|
||||
// Since we are not re-encoding, all we have to do is copy the parameters
|
||||
video_out_ctx = avcodec_alloc_context3(NULL);
|
||||
|
||||
// Copy params from instream to ctx
|
||||
ret = avcodec_parameters_to_context(video_out_ctx,
|
||||
video_in_stream->codecpar);
|
||||
if (ret < 0) {
|
||||
if ( ret < 0 ) {
|
||||
Error("Could not initialize ctx parameteres");
|
||||
return;
|
||||
} else {
|
||||
|
@ -106,7 +113,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
Debug(2, "Success creating video out stream");
|
||||
}
|
||||
|
||||
if (!video_out_ctx->codec_tag) {
|
||||
if ( !video_out_ctx->codec_tag ) {
|
||||
video_out_ctx->codec_tag =
|
||||
av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id);
|
||||
Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
|
||||
|
@ -124,22 +131,80 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
zm_dump_codecpar(video_out_stream->codecpar);
|
||||
|
||||
#else
|
||||
video_out_stream =
|
||||
avformat_new_stream(oc,(const AVCodec *)(video_in_ctx->codec));
|
||||
if (!video_out_stream) {
|
||||
if ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) {
|
||||
// Same codec, just copy the packets, otherwise we have to decode/encode
|
||||
video_out_codec = (AVCodec *)video_in_ctx->codec;
|
||||
video_out_stream = avformat_new_stream(oc, video_out_codec);
|
||||
if ( !video_out_stream ) {
|
||||
Fatal("Unable to create video out stream\n");
|
||||
} else {
|
||||
Debug(2, "Success creating video out stream");
|
||||
}
|
||||
video_out_ctx = video_out_stream->codec;
|
||||
// Just copy them from the in, no reason to choose different
|
||||
video_out_ctx->time_base = video_in_ctx->time_base;
|
||||
video_out_stream->time_base = video_in_stream->time_base;
|
||||
} else {
|
||||
/** Create a new frame to store the audio samples. */
|
||||
if ( !(in_frame = zm_av_frame_alloc()) ) {
|
||||
Error("Could not allocate in frame");
|
||||
return;
|
||||
}
|
||||
video_out_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||
if (!video_out_codec) {
|
||||
Fatal("Could not find codec for H264");
|
||||
}
|
||||
Debug(2, "Have video out codec");
|
||||
}
|
||||
video_out_stream = avformat_new_stream(oc, video_out_codec);
|
||||
if ( !video_out_stream ) {
|
||||
Fatal("Unable to create video out stream\n");
|
||||
} else {
|
||||
Debug(2, "Success creating video out stream");
|
||||
}
|
||||
video_out_ctx = video_out_stream->codec;
|
||||
ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
||||
if (ret < 0) {
|
||||
Fatal("Unable to copy in video ctx to out video ctx %s\n",
|
||||
av_make_error_string(ret).c_str());
|
||||
|
||||
// * Copy over the useful; parameters
|
||||
video_out_ctx->height = video_in_ctx->height;
|
||||
video_out_ctx->width = video_in_ctx->width;
|
||||
video_out_ctx->sample_aspect_ratio = video_in_ctx->sample_aspect_ratio;
|
||||
/* take first format from list of supported formats */
|
||||
video_out_ctx->pix_fmt = video_out_codec->pix_fmts[0];
|
||||
/* video time_base can be set to whatever is handy and supported by encoder */
|
||||
//video_out_ctx->time_base = video_in_ctx->time_base;
|
||||
video_out_ctx->time_base = (AVRational){1, 1000}; // Milliseconds as base frame rate
|
||||
video_out_ctx->framerate = (AVRational){0,1}; // Unknown framerate
|
||||
|
||||
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 {
|
||||
Debug(3, "Success copying ctx");
|
||||
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 (!video_out_ctx->codec_tag) {
|
||||
ret = avcodec_open2(video_out_ctx, video_out_codec, &opts );
|
||||
if (ret < 0) {
|
||||
Error("Can't open video codec!");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
//ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
||||
//if ( ret < 0 ) {
|
||||
//Fatal("Unable to copy in video ctx to out video ctx %s\n",
|
||||
//av_make_error_string(ret).c_str());
|
||||
////} else {
|
||||
//Debug(3, "Success copying ctx");
|
||||
//}
|
||||
#if 0
|
||||
if ( !video_out_ctx->codec_tag ) {
|
||||
Debug(2, "No codec_tag");
|
||||
if (!oc->oformat->codec_tag ||
|
||||
av_codec_get_id(oc->oformat->codec_tag,
|
||||
|
@ -152,10 +217,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Just copy them from the in, no reason to choose different
|
||||
video_out_ctx->time_base = video_in_ctx->time_base;
|
||||
video_out_stream->time_base = video_in_stream->time_base;
|
||||
#endif
|
||||
|
||||
Debug(3,
|
||||
"Time bases: VIDEO in stream (%d/%d) in codec: (%d/%d) out "
|
||||
|
@ -192,7 +254,6 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
|||
audio_out_codec = NULL;
|
||||
audio_in_ctx = NULL;
|
||||
audio_out_stream = NULL;
|
||||
in_frame = NULL;
|
||||
out_frame = NULL;
|
||||
#ifdef HAVE_LIBAVRESAMPLE
|
||||
resample_ctx = NULL;
|
||||
|
@ -329,17 +390,86 @@ bool VideoStore::open() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void VideoStore::write_audio_packet( AVPacket &pkt ) {
|
||||
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
||||
pkt.dts, pkt.duration);
|
||||
pkt.pts = audio_next_pts;
|
||||
pkt.dts = audio_next_dts;
|
||||
|
||||
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 flushed 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);
|
||||
}
|
||||
|
||||
VideoStore::~VideoStore() {
|
||||
if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) {
|
||||
|
||||
if (video_out_ctx->codec->capabilities & AV_CODEC_CAP_DELAY) {
|
||||
// The codec queues data. We need to send a flush command and out
|
||||
// whatever we get. Failures are not fatal.
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
|
||||
#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
|
||||
Debug(3, "(%d, %d)", video_next_dts, video_next_pts );
|
||||
pkt.dts = video_next_dts;
|
||||
pkt.pts = video_next_pts;
|
||||
pkt.duration = video_last_duration;
|
||||
write_video_packet(pkt);
|
||||
zm_av_packet_unref(&pkt);
|
||||
} // while have buffered frames
|
||||
} // end if have delay capability
|
||||
} // end if have buffered video
|
||||
|
||||
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);
|
||||
|
||||
while (1) {
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
// Put encoder into flushing mode
|
||||
avcodec_send_frame(audio_out_ctx, NULL);
|
||||
// Put encoder into flushing mode
|
||||
avcodec_send_frame(audio_out_ctx, NULL);
|
||||
while (1) {
|
||||
ret = avcodec_receive_packet(audio_out_ctx, &pkt);
|
||||
if (ret < 0) {
|
||||
if (AVERROR_EOF != ret) {
|
||||
|
@ -349,6 +479,7 @@ VideoStore::~VideoStore() {
|
|||
break;
|
||||
}
|
||||
#else
|
||||
while (1) {
|
||||
int got_packet = 0;
|
||||
ret =
|
||||
avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet);
|
||||
|
@ -362,22 +493,7 @@ VideoStore::~VideoStore() {
|
|||
break;
|
||||
}
|
||||
#endif
|
||||
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
||||
pkt.dts, pkt.duration);
|
||||
pkt.pts = audio_next_pts;
|
||||
pkt.dts = audio_next_dts;
|
||||
|
||||
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 flushed 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);
|
||||
write_audio_packet(pkt);
|
||||
zm_av_packet_unref(&pkt);
|
||||
} // while have buffered frames
|
||||
} // end if audio_out_codec
|
||||
|
@ -402,6 +518,11 @@ VideoStore::~VideoStore() {
|
|||
video_out_ctx = NULL;
|
||||
Debug(4, "Success freeing video_out_ctx");
|
||||
}
|
||||
// Used by both audio and video conversions
|
||||
if (in_frame) {
|
||||
av_frame_free(&in_frame);
|
||||
in_frame = NULL;
|
||||
}
|
||||
if (audio_out_stream) {
|
||||
avcodec_close(audio_out_ctx);
|
||||
audio_out_ctx = NULL;
|
||||
|
@ -410,10 +531,6 @@ VideoStore::~VideoStore() {
|
|||
avresample_close(resample_ctx);
|
||||
avresample_free(&resample_ctx);
|
||||
}
|
||||
if (in_frame) {
|
||||
av_frame_free(&in_frame);
|
||||
in_frame = NULL;
|
||||
}
|
||||
if (out_frame) {
|
||||
av_frame_free(&out_frame);
|
||||
out_frame = NULL;
|
||||
|
@ -547,10 +664,12 @@ bool VideoStore::setup_resampler() {
|
|||
audio_out_ctx->channel_layout, audio_out_ctx->frame_size);
|
||||
|
||||
/** 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())) {
|
||||
|
@ -687,92 +806,117 @@ int VideoStore::writePacket( ZMPacket *ipkt ) {
|
|||
|
||||
int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
||||
av_init_packet(&opkt);
|
||||
frame_count += 1;
|
||||
if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) {
|
||||
Debug(3, "Have encoding video frmae count (%d)", frame_count);
|
||||
|
||||
opkt.pts = video_next_pts;
|
||||
opkt.dts = video_next_dts;
|
||||
opkt.duration = 0;
|
||||
|
||||
int duration;
|
||||
if (!video_last_pts) {
|
||||
duration = 0;
|
||||
} else {
|
||||
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);
|
||||
if (duration < 0) {
|
||||
duration = ipkt->duration;
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
ret = avcodec_send_packet(video_in_ctx, ipkt);
|
||||
if (ret < 0) {
|
||||
Error("avcodec_send_packet fail %s", av_make_error_string(ret).c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//#if ( 0 && video_last_pts && ( ipkt->duration == AV_NOPTS_VALUE || !
|
||||
//ipkt->duration ) ) {
|
||||
// Video packets don't really have a duration. Audio does.
|
||||
// opkt.duration = av_rescale_q(duration, video_in_stream->time_base,
|
||||
// video_out_stream->time_base);
|
||||
// opkt.duration = 0;
|
||||
//} else {
|
||||
// duration = opkt.duration = av_rescale_q(ipkt->duration,
|
||||
// video_in_stream->time_base, video_out_stream->time_base);
|
||||
//}
|
||||
video_last_pts = ipkt->pts;
|
||||
video_last_dts = ipkt->dts;
|
||||
ret = avcodec_receive_frame(video_in_ctx, in_frame);
|
||||
if ( ret < 0 ) {
|
||||
Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str());
|
||||
return 0;
|
||||
}
|
||||
if ((ret = avcodec_send_frame(video_out_ctx, in_frame)) < 0) {
|
||||
Error("Could not send frame (error '%s')",
|
||||
av_make_error_string(ret).c_str());
|
||||
zm_av_packet_unref(&opkt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//Scale the PTS of the outgoing packet to be the correct time base
|
||||
if ( ipkt->pts != AV_NOPTS_VALUE ) {
|
||||
|
||||
if ( ! video_last_pts ) {
|
||||
// This is the first packet.
|
||||
opkt.pts = 0;
|
||||
Debug(2, "Starting video video_last_pts will become (%d)", ipkt->pts);
|
||||
if ((ret = avcodec_receive_packet(video_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);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Decode the video frame stored in the packet.
|
||||
* The in video 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.
|
||||
*/
|
||||
if ((ret = avcodec_decode_video2(video_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;
|
||||
} else {
|
||||
if ( ipkt->pts < video_last_pts ) {
|
||||
Debug(1, "Resetting video_last_pts from (%d) to (%d)", video_last_pts, ipkt->pts);
|
||||
// wrap around, need to figure out the distance FIXME having this wrong should cause a jump, but then play ok?
|
||||
opkt.pts = video_next_pts + av_rescale_q( ipkt->pts, video_in_stream->time_base, video_out_stream->time_base);
|
||||
} else {
|
||||
opkt.pts = video_next_pts + av_rescale_q( ipkt->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base);
|
||||
}
|
||||
Debug(3, "Decoded frame data_present(%d)", data_present);
|
||||
}
|
||||
Debug(3, "opkt.pts = %d from ipkt->pts(%d) - last_pts(%d)", opkt.pts, ipkt->pts, video_last_pts);
|
||||
video_last_pts = ipkt->pts;
|
||||
} else {
|
||||
Debug(3, "opkt.pts = undef");
|
||||
opkt.pts = AV_NOPTS_VALUE;
|
||||
}
|
||||
// Just because the in stream wraps, doesn't mean the out needs to. Really, if we are limiting ourselves to 10min segments I can't imagine every wrapping in the out. So need to handle in wrap, without causing out wrap.
|
||||
if ( !video_last_dts ) {
|
||||
// This is the first packet.
|
||||
opkt.dts = 0;
|
||||
Debug(1, "Starting video video_last_dts will become (%lu)", ipkt->dts);
|
||||
video_last_dts = ipkt->dts;
|
||||
} else {
|
||||
// Scale the DTS of the outgoing packet to be the correct time base
|
||||
|
||||
if ( ipkt->dts == AV_NOPTS_VALUE ) {
|
||||
// why are we using cur_dts instead of packet.dts? I think cur_dts is in AV_TIME_BASE_Q, but ipkt.dts is in video_in_stream->time_base
|
||||
if ( video_in_stream->cur_dts < video_last_dts ) {
|
||||
Debug(1, "Resetting video_last_dts from (%d) to (%d) p.dts was (%d)", video_last_dts, video_in_stream->cur_dts, ipkt->dts);
|
||||
opkt.dts = video_next_dts + av_rescale_q(video_in_stream->cur_dts, AV_TIME_BASE_Q, video_out_stream->time_base);
|
||||
} else {
|
||||
opkt.dts = video_next_dts + av_rescale_q(video_in_stream->cur_dts - video_last_dts, AV_TIME_BASE_Q, video_out_stream->time_base);
|
||||
}
|
||||
Debug(3, "opkt.dts = %d from video_in_stream->cur_dts(%d) - previus_dts(%d)", opkt.dts, video_in_stream->cur_dts, video_last_dts);
|
||||
video_last_dts = video_in_stream->cur_dts;
|
||||
} else {
|
||||
if ( ipkt->dts < video_last_dts ) {
|
||||
Debug(1, "Resetting video_last_dts from (%d) to (%d)", video_last_dts, ipkt->dts);
|
||||
opkt.dts = video_next_dts + av_rescale_q( ipkt->dts, video_in_stream->time_base, video_out_stream->time_base);
|
||||
} else {
|
||||
opkt.dts = video_next_dts + av_rescale_q( ipkt->dts - video_last_dts, video_in_stream->time_base, video_out_stream->time_base);
|
||||
}
|
||||
Debug(3, "opkt.dts = %d from ipkt.dts(%d) - previus_dts(%d)", opkt.dts, ipkt->dts, video_last_dts);
|
||||
video_last_dts = ipkt->dts;
|
||||
if ( !data_present ) {
|
||||
Debug(2, "Not ready to transcode a frame yet.");
|
||||
return 0;
|
||||
}
|
||||
if ((ret = avcodec_encode_video2(video_out_ctx, &opkt, in_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
|
||||
} else {
|
||||
Debug(3, "Doing passthrough, just copy packet");
|
||||
// Just copy it because the codec is the same
|
||||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
opkt.flags = ipkt->flags;
|
||||
}
|
||||
|
||||
opkt.dts = video_next_dts;
|
||||
opkt.pts = video_next_pts;
|
||||
|
||||
int duration;
|
||||
if ( !video_last_pts ) {
|
||||
duration = 0;
|
||||
} else {
|
||||
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);
|
||||
if ( duration < 0 ) {
|
||||
duration = ipkt->duration;
|
||||
}
|
||||
}
|
||||
|
||||
Debug(1, "ipkt.dts(%d) ipkt.pts(%d)", ipkt->dts, ipkt->pts);
|
||||
video_last_pts = ipkt->pts;
|
||||
video_last_dts = ipkt->dts;
|
||||
video_last_duration = duration;
|
||||
opkt.duration = duration;
|
||||
|
||||
write_video_packet( opkt );
|
||||
zm_av_packet_unref(&opkt);
|
||||
|
||||
return 0;
|
||||
} // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt )
|
||||
|
||||
void VideoStore::write_video_packet( AVPacket &opkt ) {
|
||||
|
||||
if (opkt.dts > opkt.pts) {
|
||||
Debug(1,
|
||||
"opkt.dts(%d) must be <= opkt.pts(%d). Decompression must happen "
|
||||
|
@ -781,36 +925,30 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
|||
opkt.dts = opkt.pts;
|
||||
}
|
||||
|
||||
opkt.flags = ipkt->flags;
|
||||
int keyframe = opkt.flags & AV_PKT_FLAG_KEY;
|
||||
opkt.pos = -1;
|
||||
|
||||
opkt.data = ipkt->data;
|
||||
opkt.size = ipkt->size;
|
||||
|
||||
opkt.stream_index = video_out_stream->index;
|
||||
|
||||
video_next_dts += opkt.duration;
|
||||
video_next_pts += opkt.duration;
|
||||
|
||||
AVPacket safepkt;
|
||||
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
||||
|
||||
Debug(1,
|
||||
"writing video packet keyframe(%d) pts(%d) dts(%d) duration(%d) "
|
||||
"ipkt.duration(%d)",
|
||||
keyframe, opkt.pts, opkt.dts, duration, ipkt->duration);
|
||||
if ((opkt.data == NULL) || (opkt.size < 1)) {
|
||||
"writing video packet keyframe(%d) pts(%d) dts(%d) duration(%d) packet_count(%d)",
|
||||
keyframe, opkt.pts, opkt.dts, opkt.duration, packets_written );
|
||||
if ( (opkt.data == NULL) || (opkt.size < 1) ) {
|
||||
Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__);
|
||||
dumpPacket(ipkt);
|
||||
dumpPacket(&opkt);
|
||||
|
||||
} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) {
|
||||
Warning("%s:%d: DTS out of order: %lld \u226E %lld; discarding frame",
|
||||
__FILE__, __LINE__, video_next_dts, opkt.dts);
|
||||
video_next_dts = opkt.dts;
|
||||
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 {
|
||||
video_next_dts = opkt.dts + duration;
|
||||
video_next_pts = opkt.pts + duration;
|
||||
ret = av_interleaved_write_frame(oc, &opkt);
|
||||
if (ret < 0) {
|
||||
// There's nothing we can really do if the frame is rejected, just drop it
|
||||
|
@ -824,13 +962,12 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
|||
zm_dump_codecpar(video_in_stream->codecpar);
|
||||
zm_dump_codecpar(video_out_stream->codecpar);
|
||||
#endif
|
||||
} else {
|
||||
packets_written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
zm_av_packet_unref(&opkt);
|
||||
|
||||
return 0;
|
||||
} // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt )
|
||||
} // end void VideoStore::write_video_packet
|
||||
|
||||
int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
|
||||
Debug(4, "writeAudioFrame");
|
||||
|
|
|
@ -18,11 +18,14 @@ extern "C" {
|
|||
class VideoStore {
|
||||
private:
|
||||
unsigned int packets_written;
|
||||
unsigned int frame_count;
|
||||
|
||||
AVOutputFormat *out_format;
|
||||
AVFormatContext *oc;
|
||||
AVStream *video_out_stream;
|
||||
AVStream *audio_out_stream;
|
||||
|
||||
AVCodec *video_out_codec;
|
||||
AVCodecContext *video_out_ctx;
|
||||
|
||||
AVStream *video_in_stream;
|
||||
|
@ -31,6 +34,7 @@ private:
|
|||
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||
AVPacket opkt;
|
||||
// we are transcoding
|
||||
AVFrame *video_in_frame;
|
||||
AVFrame *in_frame;
|
||||
AVFrame *out_frame;
|
||||
|
||||
|
@ -58,12 +62,14 @@ AVAudioResampleContext* resample_ctx;
|
|||
// These are for in
|
||||
int64_t video_last_pts;
|
||||
int64_t video_last_dts;
|
||||
int64_t video_last_duration;
|
||||
int64_t audio_last_pts;
|
||||
int64_t audio_last_dts;
|
||||
|
||||
// These are for out, should start at zero. We assume they do not wrap because we just aren't going to save files that big.
|
||||
int64_t video_next_pts;
|
||||
int64_t video_next_dts;
|
||||
;
|
||||
int64_t audio_next_pts;
|
||||
int64_t audio_next_dts;
|
||||
|
||||
|
@ -81,6 +87,8 @@ public:
|
|||
bool open();
|
||||
~VideoStore();
|
||||
|
||||
void write_video_packet( AVPacket &pkt );
|
||||
void write_audio_packet( AVPacket &pkt );
|
||||
int writeVideoFramePacket( AVPacket *pkt );
|
||||
int writeAudioFramePacket( AVPacket *pkt );
|
||||
int writePacket( ZMPacket *pkt );
|
||||
|
|
|
@ -198,14 +198,6 @@ class Event {
|
|||
}
|
||||
|
||||
function createListThumbnail( $overwrite=false ) {
|
||||
# Load the frame with the highest score to use as a thumbnail
|
||||
if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) {
|
||||
Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} );
|
||||
// FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame.
|
||||
return( false );
|
||||
}
|
||||
|
||||
$frameId = $frame['FrameId'];
|
||||
|
||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||
$thumbWidth = ZM_WEB_LIST_THUMB_WIDTH;
|
||||
|
@ -219,12 +211,25 @@ class Event {
|
|||
Fatal( "No thumbnail width or height specified, please check in Options->Web" );
|
||||
}
|
||||
|
||||
$imageData = $this->getImageSrc( $frame, $scale, false, $overwrite );
|
||||
if ( ! $imageData ) {
|
||||
return ( false );
|
||||
$eventPath = $this->Path();
|
||||
if ( file_exists( $eventPath.'/snapshot.jpg' ) ) {
|
||||
$frame = 'snapshot';
|
||||
$humbData = array('Path'=>$eventPath.'/snapshot.jpg');
|
||||
} else {
|
||||
# Load the frame with the highest score to use as a thumbnail
|
||||
if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) {
|
||||
Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} );
|
||||
// FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame.
|
||||
return( false );
|
||||
}
|
||||
|
||||
$imageData = $this->getImageSrc( $frame, $scale, false, $overwrite );
|
||||
if ( ! $imageData ) {
|
||||
return ( false );
|
||||
}
|
||||
$thumbData = $frame;
|
||||
$thumbData['Path'] = $imageData['thumbPath'];
|
||||
}
|
||||
$thumbData = $frame;
|
||||
$thumbData['Path'] = $imageData['thumbPath'];
|
||||
$thumbData['Width'] = (int)$thumbWidth;
|
||||
$thumbData['Height'] = (int)$thumbHeight;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ public $defaults = array(
|
|||
'AutoArchive' => 0,
|
||||
'AutoVideo' => 0,
|
||||
'AutoMessage' => 0,
|
||||
'UpdateDiskSpace' => 0,
|
||||
'Background' => 0,
|
||||
'Concurrent' => 0,
|
||||
'limit' => 100,
|
||||
|
|
|
@ -148,6 +148,7 @@ Warning("Addterm");
|
|||
$sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0);
|
||||
$sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']);
|
||||
$sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0);
|
||||
$sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0);
|
||||
$sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0);
|
||||
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
|
||||
|
||||
|
|
|
@ -127,10 +127,10 @@ function dbQuery( $sql, $params=NULL ) {
|
|||
} else {
|
||||
$result = $dbConn->query( $sql );
|
||||
}
|
||||
//if ( $params )
|
||||
//Warning("SQL: $sql" . implode(',',$params));
|
||||
//else
|
||||
//Warning("SQL: $sql" );
|
||||
if ( $params )
|
||||
Warning("SQL: $sql" . implode(',',$params));
|
||||
else
|
||||
Warning("SQL: $sql" );
|
||||
} catch(PDOException $e) {
|
||||
Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . implode(',',$params) );
|
||||
}
|
||||
|
|
|
@ -1057,10 +1057,17 @@ function parseSort( $saveToSession=false, $querySep='&' ) {
|
|||
$sortColumn = 'E.Cause';
|
||||
break;
|
||||
case 'DateTime' :
|
||||
$_REQUEST['sort_field'] = 'StartTime';
|
||||
$sortColumn = 'E.StartTime';
|
||||
break;
|
||||
case 'DiskSpace' :
|
||||
$sortColumn = 'E.DiskSpace';
|
||||
break;
|
||||
case 'StartTime' :
|
||||
$sortColumn = 'E.StartTime';
|
||||
break;
|
||||
case 'EndTime' :
|
||||
$sortColumn = 'E.EndTime';
|
||||
break;
|
||||
case 'Length' :
|
||||
$sortColumn = 'E.Length';
|
||||
break;
|
||||
|
@ -1126,6 +1133,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
|
|||
case 'ServerId':
|
||||
$filter['sql'] .= 'M.ServerId';
|
||||
break;
|
||||
# Unspecified start or end, so assume start, this is to support legacy filters
|
||||
case 'DateTime':
|
||||
$filter['sql'] .= 'E.StartTime';
|
||||
break;
|
||||
|
@ -1138,8 +1146,35 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
|
|||
case 'Weekday':
|
||||
$filter['sql'] .= 'weekday( E.StartTime )';
|
||||
break;
|
||||
# Starting Time
|
||||
case 'StartDateTime':
|
||||
$filter['sql'] .= 'E.StartTime';
|
||||
break;
|
||||
case 'StartDate':
|
||||
$filter['sql'] .= 'to_days( E.StartTime )';
|
||||
break;
|
||||
case 'StartTime':
|
||||
$filter['sql'] .= 'extract( hour_second from E.StartTime )';
|
||||
break;
|
||||
case 'StartWeekday':
|
||||
$filter['sql'] .= 'weekday( E.StartTime )';
|
||||
break;
|
||||
# Ending Time
|
||||
case 'EndDateTime':
|
||||
$filter['sql'] .= 'E.EndTime';
|
||||
break;
|
||||
case 'EndDate':
|
||||
$filter['sql'] .= 'to_days( E.EndTime )';
|
||||
break;
|
||||
case 'EndTime':
|
||||
$filter['sql'] .= 'extract( hour_second from E.EndTime )';
|
||||
break;
|
||||
case 'EndWeekday':
|
||||
$filter['sql'] .= 'weekday( E.EndTime )';
|
||||
break;
|
||||
case 'Id':
|
||||
case 'Name':
|
||||
case 'DiskSpace':
|
||||
case 'MonitorId':
|
||||
case 'StorageId':
|
||||
case 'Length':
|
||||
|
@ -1853,7 +1888,8 @@ function logState() {
|
|||
Logger::WARNING => array( ZM_LOG_ALERT_WAR_COUNT, ZM_LOG_ALARM_WAR_COUNT ),
|
||||
);
|
||||
|
||||
$sql = "select Level, count(Level) as LevelCount from Logs where Level < ".Logger::INFO." and TimeKey > unix_timestamp(now() - interval ".ZM_LOG_CHECK_PERIOD." second) group by Level order by Level asc";
|
||||
# This is an expensive request, as it has to hit every row of the Logs Table
|
||||
$sql = 'SELECT Level, COUNT(Level) AS LevelCount FROM Logs WHERE Level < '.Logger::INFO.' AND TimeKey > unix_timestamp(now() - interval '.ZM_LOG_CHECK_PERIOD.' second) GROUP BY Level ORDER BY Level ASC';
|
||||
$counts = dbFetchAll( $sql );
|
||||
|
||||
foreach ( $counts as $count ) {
|
||||
|
|
|
@ -116,8 +116,11 @@ $SLANG = array(
|
|||
'AttrArchiveStatus' => 'Archive Status',
|
||||
'AttrAvgScore' => 'Avg. Score',
|
||||
'AttrCause' => 'Cause',
|
||||
'AttrDate' => 'Date',
|
||||
'AttrDateTime' => 'Date/Time',
|
||||
'AttrStartDate' => 'Start Date',
|
||||
'AttrEndDate' => 'End Date',
|
||||
'AttrStartDateTime' => 'Start Date/Time',
|
||||
'AttrEndDateTime' => 'End Date/Time',
|
||||
'AttrDiskSpace' => 'Disk Space',
|
||||
'AttrDiskBlocks' => 'Disk Blocks',
|
||||
'AttrDiskPercent' => 'Disk Percent',
|
||||
'AttrDuration' => 'Duration',
|
||||
|
@ -132,9 +135,11 @@ $SLANG = array(
|
|||
'AttrName' => 'Name',
|
||||
'AttrNotes' => 'Notes',
|
||||
'AttrSystemLoad' => 'System Load',
|
||||
'AttrTime' => 'Time',
|
||||
'AttrStartTime' => 'Start Time',
|
||||
'AttrEndTime' => 'End Time',
|
||||
'AttrTotalScore' => 'Total Score',
|
||||
'AttrWeekday' => 'Weekday',
|
||||
'AttrStartWeekday' => 'Start Weekday',
|
||||
'AttrEndWeekday' => 'End Weekday',
|
||||
'Auto' => 'Auto',
|
||||
'AutoStopTimeout' => 'Auto Stop Timeout',
|
||||
'Available' => 'Available',
|
||||
|
@ -334,6 +339,7 @@ $SLANG = array(
|
|||
'Ffmpeg' => 'Ffmpeg',
|
||||
'File' => 'File',
|
||||
'FilterArchiveEvents' => 'Archive all matches',
|
||||
'FilterUpdateDiskSpace' => 'Update used disk space',
|
||||
'FilterDeleteEvents' => 'Delete all matches',
|
||||
'FilterEmailEvents' => 'Email details of all matches',
|
||||
'FilterExecuteEvents' => 'Execute command on all matches',
|
||||
|
@ -413,6 +419,7 @@ $SLANG = array(
|
|||
'LimitResultsPre' => 'Limit to first', // This is used at the beginning of the phrase 'Limit to first N results only'
|
||||
'LinkedMonitors' => 'Linked Monitors',
|
||||
'List' => 'List',
|
||||
'ListMatches' => 'List Matches',
|
||||
'Load' => 'Load',
|
||||
'Local' => 'Local',
|
||||
'Log' => 'Log',
|
||||
|
|
|
@ -194,9 +194,19 @@ ZoneMinder requires Javascript. Please enable Javascript in your browser for thi
|
|||
<li><a href="?view=console"><?php echo translate('Console') ?></a></li>
|
||||
<?php if ( canView( 'System' ) ) { ?>
|
||||
<li><a href="?view=options"><?php echo translate('Options') ?></a></li>
|
||||
<li><?php if ( logToDatabase() > Logger::NOLOG ) { ?> <?php echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' ) ?><?php } ?></li>
|
||||
<?php } ?>
|
||||
<?php if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
|
||||
<li>
|
||||
<?php
|
||||
if ( logToDatabase() > Logger::NOLOG ) {
|
||||
if ( ! ZM_RUN_AUDIT ) {
|
||||
# zmaudit can clean the logs, but if we aren't running it, then we should clecan them regularly
|
||||
dbQuery("DELETE FROM Logs WHERE TimeKey < NOW()-to_days('".ZM_LOG_DATABASE_LIMIT."')");
|
||||
}
|
||||
echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' );
|
||||
}
|
||||
} // end if canview(System)
|
||||
?></li>
|
||||
<?php
|
||||
if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
|
||||
<li><a href="?view=devices">Devices</a></li>
|
||||
<?php } ?>
|
||||
<li><a href="?view=groups"<?php echo $view=='groups'?' class="selected"':''?>><?php echo translate('Groups') ?></a></li>
|
||||
|
|
|
@ -258,6 +258,17 @@ echo htmlSelect( 'StorageFilter', array(''=>'All')+$StorageById, (isset($_SESSIO
|
|||
<?php
|
||||
}
|
||||
?>
|
||||
<span class="StatusFilter"><label><?php echo translate('Status')?>:</label>
|
||||
<?php
|
||||
$status_options = array(
|
||||
''=>'All',
|
||||
'Unknown' => translate('Unknown'),
|
||||
'NotRunning' => translate('NotRunning'),
|
||||
'Running' => translate('Running'),
|
||||
);
|
||||
echo htmlSelect( 'StatusFilter', $status_options, ( isset($_SESSION['StatusFilter']) ? $_SESSION['StatusFilter'] : '' ), array('onchange'=>'changeFilter(this);') );
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
|
|
@ -69,10 +69,14 @@ $attrTypes = array(
|
|||
'Name' => translate('AttrName'),
|
||||
'Cause' => translate('AttrCause'),
|
||||
'Notes' => translate('AttrNotes'),
|
||||
'DateTime' => translate('AttrDateTime'),
|
||||
'Date' => translate('AttrDate'),
|
||||
'Time' => translate('AttrTime'),
|
||||
'Weekday' => translate('AttrWeekday'),
|
||||
'StartDateTime' => translate('AttrStartDateTime'),
|
||||
'StartDate' => translate('AttrStartDate'),
|
||||
'StartTime' => translate('AttrStartTime'),
|
||||
'StartWeekday' => translate('AttrStartWeekday'),
|
||||
'EndDateTime' => translate('AttrEndDateTime'),
|
||||
'EndDate' => translate('AttrEndDate'),
|
||||
'EndTime' => translate('AttrEndTime'),
|
||||
'EndWeekday' => translate('AttrEndWeekday'),
|
||||
'Length' => translate('AttrDuration'),
|
||||
'Frames' => translate('AttrFrames'),
|
||||
'AlarmFrames' => translate('AttrAlarmFrames'),
|
||||
|
@ -80,8 +84,9 @@ $attrTypes = array(
|
|||
'AvgScore' => translate('AttrAvgScore'),
|
||||
'MaxScore' => translate('AttrMaxScore'),
|
||||
'Archived' => translate('AttrArchiveStatus'),
|
||||
'DiskPercent' => translate('AttrDiskPercent'),
|
||||
'DiskBlocks' => translate('AttrDiskBlocks'),
|
||||
'DiskPercent' => translate('AttrDiskPercent'),
|
||||
'DiskSpace' => translate('AttrDiskSpace'),
|
||||
'SystemLoad' => translate('AttrSystemLoad'),
|
||||
'StorageId' => translate('AttrStorageArea'),
|
||||
'ServerId' => translate('AttrServer'),
|
||||
|
@ -297,18 +302,19 @@ if ( count($terms) == 0 ) {
|
|||
<label for="filter[Query][sort_field]"><?php echo translate('SortBy') ?></label>
|
||||
<?php
|
||||
$sort_fields = array(
|
||||
'Id' => translate('AttrId'),
|
||||
'Name' => translate('AttrName'),
|
||||
'Cause' => translate('AttrCause'),
|
||||
'Notes' => translate('AttrNotes'),
|
||||
'MonitorName' => translate('AttrMonitorName'),
|
||||
'DateTime' => translate('AttrDateTime'),
|
||||
'Length' => translate('AttrDuration'),
|
||||
'Frames' => translate('AttrFrames'),
|
||||
'AlarmFrames' => translate('AttrAlarmFrames'),
|
||||
'TotScore' => translate('AttrTotalScore'),
|
||||
'AvgScore' => translate('AttrAvgScore'),
|
||||
'MaxScore' => translate('AttrMaxScore'),
|
||||
'Id' => translate('AttrId'),
|
||||
'Name' => translate('AttrName'),
|
||||
'Cause' => translate('AttrCause'),
|
||||
'DiskSpace' => translate('AttrDiskSpace'),
|
||||
'Notes' => translate('AttrNotes'),
|
||||
'MonitorName' => translate('AttrMonitorName'),
|
||||
'StartDateTime' => translate('AttrStartDateTime'),
|
||||
'Length' => translate('AttrDuration'),
|
||||
'Frames' => translate('AttrFrames'),
|
||||
'AlarmFrames' => translate('AttrAlarmFrames'),
|
||||
'TotScore' => translate('AttrTotalScore'),
|
||||
'AvgScore' => translate('AttrAvgScore'),
|
||||
'MaxScore' => translate('AttrMaxScore'),
|
||||
);
|
||||
echo htmlSelect( 'filter[Query][sort_field]', $sort_fields, $filter->sort_field() );
|
||||
$sort_dirns = array(
|
||||
|
@ -332,6 +338,9 @@ echo htmlSelect( 'filter[Query][sort_asc]', $sort_dirns, $filter->sort_asc() );
|
|||
<label><?php echo translate('FilterArchiveEvents') ?></label>
|
||||
<input type="checkbox" name="filter[AutoArchive]" value="1"<?php if ( !empty($filter->AutoArchive()) ) { ?> checked="checked"<?php } ?> onclick="updateButtons( this )"/>
|
||||
</p>
|
||||
<p><label><?php echo translate('FilterUpdateDiskSpace') ?></label>
|
||||
<input type="checkbox" name="filter[UpdateDiskSpace]" value="1"<?php echo empty($filter->UpdateDiskSpace()) ? '' : ' checked="checked"' ?> onclick="updateButtons(this);"/>
|
||||
</p>
|
||||
<?php
|
||||
if ( ZM_OPT_FFMPEG ) {
|
||||
?>
|
||||
|
@ -387,7 +396,7 @@ if ( ZM_OPT_MESSAGE ) {
|
|||
</div>
|
||||
<hr/>
|
||||
<div id="contentButtons">
|
||||
<input type="submit" value="<?php echo translate('Submit') ?>" onclick="submitToEvents( this );"/>
|
||||
<input type="submit" value="<?php echo translate('ListMatches') ?>" onclick="submitToEvents( this );"/>
|
||||
<input type="button" name="executeButton" id="executeButton" value="<?php echo translate('Execute') ?>" onclick="executeFilter( this );"/>
|
||||
<?php
|
||||
if ( canEdit( 'Events' ) ) {
|
||||
|
|
|
@ -19,6 +19,8 @@ function updateButtons( element ) {
|
|||
canExecute = true;
|
||||
else if ( form.elements['filter[AutoDelete]'].checked )
|
||||
canExecute = true;
|
||||
else if ( form.elements['filter[UpdateDiskSpace]'].checked )
|
||||
canExecute = true;
|
||||
form.elements['executeButton'].disabled = !canExecute;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ function validateForm( form ) {
|
|||
else if ( form.elements.mid.value == 0 && monitorNames[form.elements['newMonitor[Name]'].value] )
|
||||
errors[errors.length] = "<?php echo translate('DuplicateMonitorName') ?>";
|
||||
|
||||
if ( form.elements['newMonitor[AnalysisFPS]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPS]'].value) > 0 ) )
|
||||
if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) )
|
||||
errors[errors.length] = "<?php echo translate('BadAnalysisFPS') ?>";
|
||||
if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) )
|
||||
errors[errors.length] = "<?php echo translate('BadMaxFPS') ?>";
|
||||
|
|
|
@ -84,7 +84,11 @@ function imagedone( obj, monId, success ) {
|
|||
if ( ! success ) {
|
||||
// if we had a failrue queue up the no-data image
|
||||
//loadImage2Monitor(monId,"no data"); // leave the staged URL if there is one, just ignore it here.
|
||||
loadNoData( monId );
|
||||
if ( liveMode ) {
|
||||
writeText( monId, "Camera Offline" );
|
||||
} else {
|
||||
writeText( monId, "No Data" );
|
||||
}
|
||||
} else {
|
||||
if ( monitorLoadingStageURL[monId] == "" ) {
|
||||
console.log("Not showing image for " + monId );
|
||||
|
|
|
@ -122,7 +122,7 @@ if ( ! $monitor ) {
|
|||
'FrameSkip' => 0,
|
||||
'MotionFrameSkip' => 0,
|
||||
'EventPrefix' => 'Event-',
|
||||
'AnalysisFPS' => '',
|
||||
'AnalysisFPSLimit' => '',
|
||||
'AnalysisUpdateDelay' => 0,
|
||||
'MaxFPS' => '',
|
||||
'AlarmMaxFPS' => '',
|
||||
|
@ -170,8 +170,8 @@ if ( isset( $_REQUEST['newMonitor'] ) ) {
|
|||
}
|
||||
|
||||
# What if it has less zeros? This is not robust code.
|
||||
if ( $monitor->AnalysisFPS() == '0.00' )
|
||||
$monitor->AnalysisFPS( '' );
|
||||
if ( $monitor->AnalysisFPSLimit() == '0.00' )
|
||||
$monitor->AnalysisFPSLimit( '' );
|
||||
if ( $monitor->MaxFPS() == '0.00' )
|
||||
$monitor->MaxFPS( '' );
|
||||
if ( $monitor->AlarmMaxFPS() == '0.00' )
|
||||
|
@ -538,7 +538,7 @@ if ( $tab != 'general' ) {
|
|||
<input type="hidden" name="newMonitor[Enabled]" value="<?php echo validHtmlStr($monitor->Enabled()) ?>"/>
|
||||
<input type="hidden" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($monitor->RefBlendPerc()) ?>"/>
|
||||
<input type="hidden" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($monitor->AlarmRefBlendPerc()) ?>"/>
|
||||
<input type="hidden" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>"/>
|
||||
<input type="hidden" name="newMonitor[AnalysisFPSLimit]" value="<?php echo validHtmlStr($monitor->AnalysisFPSLimit()) ?>"/>
|
||||
<input type="hidden" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($monitor->MaxFPS()) ?>"/>
|
||||
<input type="hidden" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($monitor->AlarmMaxFPS()) ?>"/>
|
||||
<?php
|
||||
|
@ -727,7 +727,7 @@ switch ( $tab ) {
|
|||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>" size="6"/></td></tr>
|
||||
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPSLimit]" value="<?php echo validHtmlStr($monitor->AnalysisFPSLimit()) ?>" size="6"/></td></tr>
|
||||
<?php
|
||||
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) {
|
||||
?>
|
||||
|
|
Loading…
Reference in New Issue