Merge branch 'zma_to_thread' into put_ffmpeg_into_event_wip

This commit is contained in:
Isaac Connor 2017-11-14 02:39:58 -05:00
commit ecc68da0fd
45 changed files with 962 additions and 604 deletions

View File

@ -195,6 +195,7 @@ CREATE TABLE `Events` (
`Frames` int(10) unsigned default NULL, `Frames` int(10) unsigned default NULL,
`AlarmFrames` int(10) unsigned default NULL, `AlarmFrames` int(10) unsigned default NULL,
`DefaultVideo` VARCHAR( 64 ) DEFAULT '' NOT NULL, `DefaultVideo` VARCHAR( 64 ) DEFAULT '' NOT NULL,
`SaveJPEGs` TINYINT,
`TotScore` int(10) unsigned NOT NULL default '0', `TotScore` int(10) unsigned NOT NULL default '0',
`AvgScore` smallint(5) unsigned default '0', `AvgScore` smallint(5) unsigned default '0',
`MaxScore` smallint(5) unsigned default '0', `MaxScore` smallint(5) unsigned default '0',
@ -384,8 +385,8 @@ CREATE TABLE `Monitors` (
`Deinterlacing` int(10) unsigned NOT NULL default '0', `Deinterlacing` int(10) unsigned NOT NULL default '0',
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' , `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
`VideoWriter` TINYINT NOT NULL DEFAULT '0', `VideoWriter` TINYINT NOT NULL DEFAULT '0',
`OutputCodec` enum('h264','mjpeg'), `OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2'),
`OutputContainer` enum('mp4','mkv'), `OutputContainer` enum('auto','mp4','mkv'),
`EncoderParameters` TEXT, `EncoderParameters` TEXT,
`RecordAudio` TINYINT NOT NULL DEFAULT '0', `RecordAudio` TINYINT NOT NULL DEFAULT '0',
`RTSPDescribe` tinyint(1) unsigned, `RTSPDescribe` tinyint(1) unsigned,

View File

@ -67,3 +67,4 @@ SET @s = (SELECT IF(
PREPARE stmt FROM @s; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;

14
db/zm_update-1.31.13.sql Normal file
View File

@ -0,0 +1,14 @@
ALTER TABLE `Monitors` MODIFY `OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2') default 'h264';
ALTER TABLE `Monitors` MODIFY `OutputContainer` enum('auto','mp4','mkv') default 'auto';
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Events'
AND column_name = 'SaveJPEGs'
) > 0,
"SELECT 'Column SaveJPEGs already exists in Events'",
"ALTER TABLE `Events` ADD `SaveJPEGs` TINYINT AFTER `DefaultVideo`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -115,7 +115,7 @@ BEGIN {
, $Config{ZM_DB_USER} , $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS} , $Config{ZM_DB_PASS}
) or croak( "Can't connect to db" ); ) or croak( "Can't connect to db" );
my $sql = 'select * from Config'; my $sql = 'SELECT Name,Value FROM Config';
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() );
while( my $config = $sth->fetchrow_hashref() ) { while( my $config = $sth->fetchrow_hashref() ) {

View File

@ -310,25 +310,27 @@ sub set {
my %defaults = eval('%'.$type.'::defaults'); my %defaults = eval('%'.$type.'::defaults');
if ( ref $params ne 'HASH' ) { if ( ref $params ne 'HASH' ) {
my ( $caller, undef, $line ) = caller; my ( $caller, undef, $line ) = caller;
$openprint::log->error("$type -> set called with non-hash params from $caller $line"); $log->error("$type -> set called with non-hash params from $caller $line");
} }
foreach my $field ( keys %fields ) { foreach my $field ( keys %fields ) {
$log->debug("field: $field, param: ".$$params{$field}) if $debug; if ( $params ) {
if ( exists $$params{$field} ) { $log->debug("field: $field, param: ".$$params{$field}) if $debug;
$openprint::log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug; if ( exists $$params{$field} ) {
if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) { $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 # Only make changes to fields that have changed
if ( defined $fields{$field} ) { if ( defined $fields{$field} ) {
$$self{$field} = $$params{$field} if defined $fields{$field}; $$self{$field} = $$params{$field} if defined $fields{$field};
push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating
} # end if } # end if
$openprint::log->debug("Running $field with $$params{$field}") if $debug; $log->debug("Running $field with $$params{$field}") if $debug;
if ( my $func = $self->can( $field ) ) { if ( my $func = $self->can( $field ) ) {
$func->( $self, $$params{$field} ); $func->( $self, $$params{$field} );
} # end if } # end if
} # end if } # end if
} # end if } # end if
} # end if $params
if ( defined $fields{$field} ) { if ( defined $fields{$field} ) {
if ( $$self{$field} ) { if ( $$self{$field} ) {

View File

@ -94,7 +94,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my @daemons = ( my @daemons = (
'zmc', 'zmc',
'zma', #'zma',
'zmfilter.pl', 'zmfilter.pl',
'zmaudit.pl', 'zmaudit.pl',
'zmtrigger.pl', 'zmtrigger.pl',

View File

@ -251,7 +251,7 @@ if ( $command =~ /^(?:start|restart)$/ )
} }
if ( $monitor->{Function} ne 'Monitor' ) if ( $monitor->{Function} ne 'Monitor' )
{ {
runCommand( "zmdc.pl start zma -m $monitor->{Id}" ); #runCommand( "zmdc.pl start zma -m $monitor->{Id}" );
} }
if ( $Config{ZM_OPT_CONTROL} ) if ( $Config{ZM_OPT_CONTROL} )
{ {

View File

@ -4,7 +4,7 @@
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp) set(ZM_BIN_SRC_FILES zm_analysis_thread.cpp zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_ffmpeg_input.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
# A fix for cmake recompiling the source files for every target. # A fix for cmake recompiling the source files for every target.
add_library(zm STATIC ${ZM_BIN_SRC_FILES}) add_library(zm STATIC ${ZM_BIN_SRC_FILES})

View File

@ -1,25 +0,0 @@
snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", staticConfig.PATH_SWAP.c_str(), monitor->Id(), connkey );
int len = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id());
int swap_path_length = strlen(staticConfig.PATH_SWAP.c_str()) + snprintf(NULL, 0, "/zmswap-m%d", monitor->Id() ) + snprintf(NULL, 0, "/zmswap-q%06d", connkey ) + 1; // +1 for NULL terminator
if ( connkey && playback_buffer > 0 ) {
if ( swap_path_length + max_swap_len_suffix > PATH_MAX ) {
Error( "Swap Path is too long. %d > %d ", swap_path_length+max_swap_len_suffix, PATH_MAX );
} else {
swap_path = (char *)malloc( swap_path_length+max_swap_len_suffix );
Debug( 3, "Checking swap image path %s", staticConfig.PATH_SWAP.c_str() );
strncpy( swap_path, staticConfig.PATH_SWAP.c_str(), swap_path_length );
if ( checkSwapPath( swap_path, false ) ) {
snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-m%d", monitor->Id() );
if ( checkSwapPath( swap_path, true ) ) {
snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-q%06d", connkey );
if ( checkSwapPath( swap_path, true ) ) {
buffered_playback = true;
}
}
}

View File

@ -0,0 +1,49 @@
#include "zm_analysis_thread.h"
AnalysisThread::AnalysisThread(Monitor *p_monitor) {
monitor = p_monitor;
terminate = false;
//sigemptyset(&block_set);
}
AnalysisThread::~AnalysisThread() {
Debug(2, "THREAD: deleteing");
}
int AnalysisThread::run() {
useconds_t analysis_rate = monitor->GetAnalysisRate();
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
time_t last_analysis_update_time, cur_time;
monitor->UpdateAdaptiveSkip();
last_analysis_update_time = time(0);
Debug(2, "THREAD: Getting ref image");
monitor->get_ref_image();
while( !terminate ) {
// Process the next image
//sigprocmask(SIG_BLOCK, &block_set, 0);
// Some periodic updates are required for variable capturing framerate
if ( analysis_update_delay ) {
cur_time = time( 0 );
if ( (unsigned int)( cur_time - last_analysis_update_time ) > analysis_update_delay ) {
analysis_rate = monitor->GetAnalysisRate();
monitor->UpdateAdaptiveSkip();
last_analysis_update_time = cur_time;
}
}
if ( !monitor->Analyse() ) {
Debug(4, "Sleeping for %d", monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE);
usleep(monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE);
} else if ( analysis_rate ) {
Debug(4, "Sleeping for %d", analysis_rate);
usleep(analysis_rate);
}
//sigprocmask(SIG_UNBLOCK, &block_set, 0);
} // end while ! terminate
return 0;
} // end in AnalysisThread::run()

29
src/zm_analysis_thread.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef ZM_ANALYSIS_THREAD_H
#define ZM_ANALYSIS_THREAD_H
#include "zm_thread.h"
#include <signal.h>
#include "zm_monitor.h"
class AnalysisThread : public Thread {
private:
bool terminate;
sigset_t block_set;
Monitor *monitor;
public:
AnalysisThread( Monitor * );
~AnalysisThread();
int run();
void stop() {
terminate = true;
}
bool stopped() const {
return( terminate );
}
};
#endif

View File

@ -107,8 +107,8 @@ public:
virtual int PreCapture()=0; virtual int PreCapture()=0;
virtual int Capture(ZMPacket &p)=0; virtual int Capture(ZMPacket &p)=0;
virtual int PostCapture()=0; virtual int PostCapture()=0;
AVStream *get_VideoStream() { return NULL; }; virtual AVStream *get_VideoStream() { return NULL; };
AVStream *get_AudioStream() { return NULL; }; virtual AVStream *get_AudioStream() { return NULL; };
int get_VideoStreamId() { return mVideoStreamId; }; int get_VideoStreamId() { return mVideoStreamId; };
int get_AudioStreamId() { return mAudioStreamId; }; int get_AudioStreamId() { return mAudioStreamId; };
}; };

View File

@ -71,7 +71,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
struct tm *stime = localtime( &start_time.tv_sec ); struct tm *stime = localtime( &start_time.tv_sec );
snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d )", snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '', %d )",
monitor->Id(), monitor->Id(),
storage->Id(), storage->Id(),
start_time.tv_sec, start_time.tv_sec,
@ -81,7 +81,8 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
notes.c_str(), notes.c_str(),
state_id, state_id,
monitor->getOrientation(), monitor->getOrientation(),
videoEvent videoEvent,
monitor->GetOptSaveJPEGs()
); );
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql ); Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql );
@ -140,6 +141,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
if ( symlink( time_path, id_file ) < 0 ) if ( symlink( time_path, id_file ) < 0 )
Error( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); Error( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno));
} else { } else {
// Shallow Storage
snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id ); snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id );
errno = 0; errno = 0;
@ -165,13 +167,23 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
/* Save as video */ /* Save as video */
if ( monitor->GetOptVideoWriter() != 0 ) { if ( monitor->GetOptVideoWriter() != 0 ) {
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" ); std::string container = monitor->OutputContainer();
if ( container == "auto" || container == "" ) {
if ( monitor->OutputCodec() == "h264" ) {
container = "mp4";
} else {
container = "mkv";
}
}
snprintf( video_name, sizeof(video_name), "%d-%s.%s", id, "video", container.c_str() );
snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name ); snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name );
Debug(1,"Writing video file to %s", video_file ); Debug(1,"Writing video file to %s", video_file );
videowriter = NULL;
Camera * camera = monitor->getCamera(); Camera * camera = monitor->getCamera();
videoStore = new VideoStore( videoStore = new VideoStore(
video_file, video_file,
"mp4", container.c_str(),
camera->get_VideoStream(), camera->get_VideoStream(),
( monitor->RecordAudio() ? camera->get_AudioStream() : NULL ), ( monitor->RecordAudio() ? camera->get_AudioStream() : NULL ),
monitor ); monitor );
@ -180,7 +192,6 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
delete videoStore; delete videoStore;
videoStore = NULL; videoStore = NULL;
} }
} else { } else {
/* No video object */ /* No video object */
videowriter = NULL; videowriter = NULL;
@ -208,7 +219,7 @@ Event::~Event() {
videoStore = NULL; videoStore = NULL;
} }
snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); snprintf( sql, sizeof(sql), "UPDATE Events SET Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) ); Error( "Can't update event: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
@ -285,7 +296,9 @@ bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp,
bool Event::WritePacket( ZMPacket &packet ) { bool Event::WritePacket( ZMPacket &packet ) {
videoStore->writePacket( &packet ); if ( videoStore->writePacket( &packet ) < 0 )
return false;
return true;
} }
void Event::updateNotes( const StringSetMap &newNoteSetMap ) { void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
@ -403,6 +416,11 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
if ( timestamps[i]->tv_sec <= 0 ) { if ( timestamps[i]->tv_sec <= 0 ) {
Debug( 1, "Not adding pre-capture frame %d, zero or less than 0 timestamp", i ); Debug( 1, "Not adding pre-capture frame %d, zero or less than 0 timestamp", i );
continue; continue;
} else if ( timestamps[i]->tv_sec < 0 ) {
Warning( "Not adding pre-capture frame %d, negative timestamp", i );
continue;
} else {
Debug( 3, "Adding pre-capture frame %d, timestamp = (%d), start_time=(%d)", i, timestamps[i]->tv_sec, start_time.tv_sec );
} }
frames++; frames++;
@ -450,6 +468,96 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
} }
} }
void Event::AddPacket( ZMPacket *packet, int score, Image *alarm_image ) {
frames++;
static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
if ( monitor->GetOptSaveJPEGs() & 4 ) {
// Only snapshots
//If this is the first frame, we should add a thumbnail to the event directory
if ( frames == 10 ) {
static char snapshot_file[PATH_MAX];
snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path );
WriteFrameImage( packet->image, packet->timestamp, snapshot_file );
}
}
if ( monitor->GetOptSaveJPEGs() & 1 ) {
Debug( 1, "Writing capture frame %d to %s", frames, event_file );
if ( ! WriteFrameImage( packet->image, packet->timestamp, event_file ) ) {
Error("Failed to write frame image");
}
}
if ( videoStore ) {
videoStore->writePacket( packet );
//FIXME if it fails, we should write a jpeg
}
struct DeltaTimeval delta_time;
DELTA_TIMEVAL( delta_time, packet->timestamp, start_time, DT_PREC_2 );
FrameType frame_type = score>0?ALARM:(score<0?BULK:NORMAL);
// < 0 means no motion detection is being done.
if ( score < 0 )
score = 0;
bool db_frame = ( frame_type != BULK ) || ((frames%config.bulk_frame_interval)==0) || !frames;
if ( db_frame ) {
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql),
"insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score )"
" values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )",
id, frames, frame_type_names[frame_type], packet->timestamp.tv_sec,
delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
last_db_frame = frames;
// We are writing a Bulk frame
if ( frame_type == BULK ) {
snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d",
( delta_time.positive?"":"-" ),
delta_time.sec, delta_time.fsec,
frames,
alarm_frames,
tot_score,
(int)(alarm_frames?(tot_score/alarm_frames):0),
max_score,
id
);
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
} // end if db_frame
end_time = packet->timestamp;
// We are writing an Alarm frame
if ( frame_type == ALARM ) {
alarm_frames++;
tot_score += score;
if ( score > (int)max_score )
max_score = score;
if ( alarm_image ) {
snprintf( event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames );
Debug( 1, "Writing analysis frame %d", frames );
if ( monitor->GetOptSaveJPEGs() & 2 ) {
WriteFrameImage(alarm_image, packet->timestamp, event_file, true);
}
}
}
}
void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) { void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) {
if ( !timestamp.tv_sec ) { if ( !timestamp.tv_sec ) {
Debug( 1, "Not adding new frame, zero timestamp" ); Debug( 1, "Not adding new frame, zero timestamp" );
@ -465,7 +573,7 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
// Only snapshots // Only snapshots
//If this is the first frame, we should add a thumbnail to the event directory //If this is the first frame, we should add a thumbnail to the event directory
if ( frames == 10 ) { if ( frames == 10 ) {
char snapshot_file[PATH_MAX]; static char snapshot_file[PATH_MAX];
snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path ); snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path );
WriteFrameImage( image, timestamp, snapshot_file ); WriteFrameImage( image, timestamp, snapshot_file );
} }
@ -539,37 +647,4 @@ Debug(3, "Writing video");
} }
} }
} }
/* This makes viewing the diagnostic images impossible because it keeps deleting them
if ( config.record_diag_images ) {
char diag_glob[PATH_MAX] = "";
snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", staticConfig.DIR_EVENTS.c_str(), monitor->Id() );
glob_t pglob;
int glob_status = glob( diag_glob, 0, 0, &pglob );
if ( glob_status != 0 ) {
if ( glob_status < 0 ) {
Error( "Can't glob '%s': %s", diag_glob, strerror(errno) );
} else {
Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status );
}
} else {
char new_diag_path[PATH_MAX] = "";
for ( int i = 0; i < pglob.gl_pathc; i++ ) {
char *diag_path = pglob.gl_pathv[i];
char *diag_file = strstr( diag_path, "diag-" );
if ( diag_file ) {
snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file );
if ( rename( diag_path, new_diag_path ) < 0 ) {
Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) );
}
}
}
}
globfree( &pglob );
}
*/
} }

View File

@ -122,6 +122,7 @@ class Event {
void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
void AddPacket( ZMPacket *p, int score=0, Image *alarm_frame=NULL );
bool WritePacket( ZMPacket &p ); bool WritePacket( ZMPacket &p );
private: private:

View File

@ -133,6 +133,7 @@ int av_dict_parse_string(AVDictionary **pm, const char *str,
#endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE #endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE
#if HAVE_LIBAVUTIL #if HAVE_LIBAVUTIL
#if LIBAVUTIL_VERSION_CHECK(56, 0, 0, 17, 100)
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb){ int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb){
int64_t a, b, this_thing; int64_t a, b, this_thing;
@ -156,6 +157,7 @@ simple_round:
return av_rescale_q(this_thing, fs_tb, out_tb); return av_rescale_q(this_thing, fs_tb, out_tb);
} }
#endif #endif
#endif
int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename) { int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename) {
AVFormatContext *s = avformat_alloc_context(); AVFormatContext *s = avformat_alloc_context();

View File

@ -116,9 +116,9 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
Debug(1,"Packet is for our stream (%d)", packet.stream_index ); 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) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
AVCodecContext *context = streams[packet.stream_index].context;
ret = avcodec_send_packet( context, &packet ); ret = avcodec_send_packet( context, &packet );
if ( ret < 0 ) { if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );

View File

@ -130,7 +130,6 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin
} }
Image::Image( const AVFrame *frame ) { Image::Image( const AVFrame *frame ) {
AVFrame *dest_frame = zm_av_frame_alloc();
width = frame->width; width = frame->width;
height = frame->height; height = frame->height;
@ -141,7 +140,13 @@ Image::Image( const AVFrame *frame ) {
buffer = 0; buffer = 0;
holdbuffer = 0; holdbuffer = 0;
AllocImgBuffer(size); AllocImgBuffer(size);
this->Assign( frame );
}
void Image::Assign( const AVFrame *frame ) {
/* Assume the dimensions etc are correct. FIXME */
AVFrame *dest_frame = zm_av_frame_alloc();
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(dest_frame->data, dest_frame->linesize, av_image_fill_arrays(dest_frame->data, dest_frame->linesize,
buffer, AV_PIX_FMT_RGBA, width, height, 1); buffer, AV_PIX_FMT_RGBA, width, height, 1);
@ -167,7 +172,7 @@ Image::Image( const AVFrame *frame ) {
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
av_frame_free( &dest_frame ); av_frame_free( &dest_frame );
} } // end Image::Image( const AVFrame *frame )
Image::Image( const Image &p_image ) { Image::Image( const Image &p_image ) {
if ( !initialised ) if ( !initialised )
@ -605,7 +610,7 @@ void Image::Assign(const unsigned int p_width, const unsigned int p_height, cons
size = new_size; size = new_size;
} }
if(new_buffer != buffer) if ( new_buffer != buffer )
(*fptr_imgbufcpy)(buffer, new_buffer, size); (*fptr_imgbufcpy)(buffer, new_buffer, size);
} }
@ -613,25 +618,25 @@ void Image::Assign(const unsigned int p_width, const unsigned int p_height, cons
void Image::Assign( const Image &image ) { void Image::Assign( const Image &image ) {
unsigned int new_size = (image.width * image.height) * image.colours; unsigned int new_size = (image.width * image.height) * image.colours;
if(image.buffer == NULL) { if ( image.buffer == NULL ) {
Error("Attempt to assign image with an empty buffer"); Error("Attempt to assign image with an empty buffer");
return; return;
} }
if(image.colours != ZM_COLOUR_GRAY8 && image.colours != ZM_COLOUR_RGB24 && image.colours != ZM_COLOUR_RGB32) { if ( image.colours != ZM_COLOUR_GRAY8 && image.colours != ZM_COLOUR_RGB24 && image.colours != ZM_COLOUR_RGB32 ) {
Error("Attempt to assign image with unexpected colours per pixel: %d",image.colours); Error("Attempt to assign image with unexpected colours per pixel: %d",image.colours);
return; return;
} }
if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder) { if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder ) {
if (holdbuffer && buffer) { if ( holdbuffer && buffer ) {
if (new_size > allocation) { if ( new_size > allocation ) {
Error("Held buffer is undersized for assigned buffer"); Error("Held buffer is undersized for assigned buffer");
return; return;
} }
} else { } else {
if(new_size > allocation || !buffer) { if ( new_size > allocation || !buffer ) {
// DumpImgBuffer(); This is also done in AllocImgBuffer // DumpImgBuffer(); This is also done in AllocImgBuffer
AllocImgBuffer(new_size); AllocImgBuffer(new_size);
} }
@ -645,7 +650,7 @@ void Image::Assign( const Image &image ) {
size = new_size; size = new_size;
} }
if(image.buffer != buffer) if ( image.buffer != buffer )
(*fptr_imgbufcpy)(buffer, image.buffer, size); (*fptr_imgbufcpy)(buffer, image.buffer, size);
} }

View File

@ -95,14 +95,15 @@ protected:
double _1_m; double _1_m;
static int CompareYX( const void *p1, const void *p2 ) { static int CompareYX( const void *p1, const void *p2 ) {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; // This is because these functions are passed to qsort
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
if ( e1->min_y == e2->min_y ) if ( e1->min_y == e2->min_y )
return( int(e1->min_x - e2->min_x) ); return( int(e1->min_x - e2->min_x) );
else else
return( int(e1->min_y - e2->min_y) ); return( int(e1->min_y - e2->min_y) );
} }
static int CompareX( const void *p1, const void *p2 ) { static int CompareX( const void *p1, const void *p2 ) {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
return( int(e1->min_x - e2->min_x) ); return( int(e1->min_x - e2->min_x) );
} }
}; };
@ -145,6 +146,7 @@ protected:
unsigned int size; unsigned int size;
unsigned int subpixelorder; unsigned int subpixelorder;
unsigned long allocation; unsigned long allocation;
_AVPIXELFORMAT imagePixFormat;
uint8_t *buffer; uint8_t *buffer;
int buffertype; /* 0=not ours, no need to call free(), 1=malloc() buffer, 2=new buffer */ int buffertype; /* 0=not ours, no need to call free(), 1=malloc() buffer, 2=new buffer */
int holdbuffer; /* Hold the buffer instead of replacing it with new one */ int holdbuffer; /* Hold the buffer instead of replacing it with new one */
@ -171,7 +173,11 @@ public:
if ( colours == ZM_COLOUR_RGB32 ) { if ( colours == ZM_COLOUR_RGB32 ) {
return AV_PIX_FMT_RGBA; return AV_PIX_FMT_RGBA;
} else if ( colours == ZM_COLOUR_RGB24 ) { } else if ( colours == ZM_COLOUR_RGB24 ) {
return AV_PIX_FMT_RGB24; if ( subpixelorder == ZM_SUBPIX_ORDER_BGR){
return AV_PIX_FMT_BGR24;
} else {
return AV_PIX_FMT_RGB24;
}
} else if ( colours == ZM_COLOUR_GRAY8 ) { } else if ( colours == ZM_COLOUR_GRAY8 ) {
return AV_PIX_FMT_GRAY8; return AV_PIX_FMT_GRAY8;
} else { } else {
@ -179,13 +185,14 @@ public:
return AV_PIX_FMT_RGBA; return AV_PIX_FMT_RGBA;
} }
} }
/* Internal buffer should not be modified from functions outside of this class */ /* Internal buffer should not be modified from functions outside of this class */
inline const uint8_t* Buffer() const { return( buffer ); } inline const uint8_t* Buffer() const { return( buffer ); }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); } inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); }
/* Request writeable buffer */ /* Request writeable buffer */
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder); uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
// Is only acceptable on a pre-allocated buffer
uint8_t* WriteBuffer() { if ( holdbuffer ) return buffer; return NULL; };
inline int IsBufferHeld() const { return holdbuffer; } inline int IsBufferHeld() const { return holdbuffer; }
inline void HoldBuffer(int tohold) { holdbuffer = tohold; } inline void HoldBuffer(int tohold) { holdbuffer = tohold; }
@ -199,6 +206,7 @@ public:
void Assign( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size); void Assign( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size);
void Assign( const Image &image ); void Assign( const Image &image );
void Assign( const AVFrame *frame );
void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype); void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype);
inline void CopyBuffer( const Image &image ) { inline void CopyBuffer( const Image &image ) {

View File

@ -1898,7 +1898,7 @@ int LocalCamera::PrimeCapture() {
} }
int LocalCamera::PreCapture() { int LocalCamera::PreCapture() {
Debug( 5, "Pre-capturing" ); Debug( 4, "Pre-capturing" );
return( 0 ); return( 0 );
} }
@ -2000,7 +2000,7 @@ int LocalCamera::Capture( ZMPacket &zm_packet ) {
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
if ( conversion_type == 1 ) { if ( conversion_type == 1 ) {
Debug( 9, "Calling sws_scale to perform the conversion" ); Debug( 9, "Setting up a frame" );
/* Use swscale to convert the image directly into the shared memory */ /* Use swscale to convert the image directly into the shared memory */
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(tmpPicture->data, av_image_fill_arrays(tmpPicture->data,
@ -2010,6 +2010,7 @@ int LocalCamera::Capture( ZMPacket &zm_packet ) {
avpicture_fill( (AVPicture *)tmpPicture, directbuffer, avpicture_fill( (AVPicture *)tmpPicture, directbuffer,
imagePixFormat, width, height ); imagePixFormat, width, height );
#endif #endif
Debug( 9, "Calling sws_scale to perform the conversion" );
sws_scale( sws_scale(
imgConversionContext, imgConversionContext,
capturePictures[capture_frame]->data, capturePictures[capture_frame]->data,
@ -2019,6 +2020,7 @@ int LocalCamera::Capture( ZMPacket &zm_packet ) {
tmpPicture->data, tmpPicture->data,
tmpPicture->linesize tmpPicture->linesize
); );
Debug( 9, "Done sws_scale to perform the conversion" );
} else } else
#endif #endif
if ( conversion_type == 2 ) { if ( conversion_type == 2 ) {
@ -2071,7 +2073,7 @@ int LocalCamera::PostCapture() {
} else { } else {
Error( "Unable to requeue buffer due to not v4l2_data" ) Error( "Unable to requeue buffer due to not v4l2_data" )
} }
} } else
#endif // ZM_HAS_V4L2 #endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1 #if ZM_HAS_V4L1
if ( v4l_version == 1 ) { if ( v4l_version == 1 ) {

View File

@ -36,6 +36,7 @@
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <sys/thr.h> #include <sys/thr.h>
#endif #endif
#include <cstdarg>
bool Logger::smInitialised = false; bool Logger::smInitialised = false;
Logger *Logger::smInstance = 0; Logger *Logger::smInstance = 0;

View File

@ -63,16 +63,6 @@
#define MAP_LOCKED 0 #define MAP_LOCKED 0
#endif #endif
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(trimSpaces(item));
}
return elems;
}
Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) { Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) {
strncpy( name, p_name, sizeof(name) ); strncpy( name, p_name, sizeof(name) );
@ -231,9 +221,11 @@ bool Monitor::MonitorLink::inAlarm() {
bool Monitor::MonitorLink::hasAlarmed() { bool Monitor::MonitorLink::hasAlarmed() {
if ( shared_data->state == ALARM ) { if ( shared_data->state == ALARM ) {
return( true ); return( true );
} else if ( shared_data->last_event_id != (unsigned int)last_event_id ) {
last_event_id = shared_data->last_event_id;
} }
//} else if ( shared_data->last_event_id != (unsigned int)last_event ) {
// Why test for it, just set it...
last_event_id = shared_data->last_event_id;
//}
return( false ); return( false );
} }
@ -251,6 +243,8 @@ Monitor::Monitor(
int p_savejpegs, int p_savejpegs,
VideoWriter p_videowriter, VideoWriter p_videowriter,
std::string p_encoderparams, std::string p_encoderparams,
std::string p_output_codec,
std::string p_output_container,
bool p_record_audio, bool p_record_audio,
const char *p_event_prefix, const char *p_event_prefix,
const char *p_label_format, const char *p_label_format,
@ -290,6 +284,8 @@ Monitor::Monitor(
savejpegspref( p_savejpegs ), savejpegspref( p_savejpegs ),
videowriter( p_videowriter ), videowriter( p_videowriter ),
encoderparams( p_encoderparams ), encoderparams( p_encoderparams ),
output_codec( p_output_codec ),
output_container( p_output_container ),
record_audio( p_record_audio ), record_audio( p_record_audio ),
label_coord( p_label_coord ), label_coord( p_label_coord ),
label_size( p_label_size ), label_size( p_label_size ),
@ -372,7 +368,6 @@ Monitor::Monitor(
mem_size = sizeof(SharedData) mem_size = sizeof(SharedData)
+ sizeof(TriggerData) + sizeof(TriggerData)
+ sizeof(VideoStoreData) //Information to pass back to the capture process + sizeof(VideoStoreData) //Information to pass back to the capture process
+ (image_buffer_count*sizeof(struct timeval))
+ (image_buffer_count*camera->ImageSize()) + (image_buffer_count*camera->ImageSize())
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */ + 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
@ -386,6 +381,7 @@ Monitor::Monitor(
snprintf( monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id ); snprintf( monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id );
struct stat statbuf; struct stat statbuf;
// If we are going to actually do capture, then yes, we should stat this dir, otherwise not
if ( stat( monitor_dir, &statbuf ) ) { if ( stat( monitor_dir, &statbuf ) ) {
if ( errno == ENOENT || errno == ENOTDIR ) { if ( errno == ENOENT || errno == ENOTDIR ) {
if ( mkdir( monitor_dir, 0755 ) ) { if ( mkdir( monitor_dir, 0755 ) ) {
@ -428,61 +424,39 @@ Monitor::Monitor(
trigger_data->trigger_showtext[0] = 0; trigger_data->trigger_showtext[0] = 0;
shared_data->valid = true; shared_data->valid = true;
video_store_data->recording = (struct timeval){0}; video_store_data->recording = (struct timeval){0};
// Uh, why nothing? Why not NULL?
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "nothing"); snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "nothing");
video_store_data->size = sizeof(VideoStoreData); video_store_data->size = sizeof(VideoStoreData);
//video_store_data->frameNumber = 0; //video_store_data->frameNumber = 0;
} else if ( purpose == ANALYSIS ) {
this->connect();
if ( ! mem_ptr ) exit(-1);
shared_data->state = IDLE;
shared_data->last_read_time = 0;
shared_data->alarm_x = -1;
shared_data->alarm_y = -1;
} }
if ( ( ! mem_ptr ) || ! shared_data->valid ) { //if ( ( ! mem_ptr ) || ! shared_data->valid ) {
if ( purpose != QUERY ) { //if ( purpose != QUERY ) {
Error( "Shared data not initialised by capture daemon for monitor %s", name ); //Error( "Shared data not initialised by capture daemon for monitor %s", name );
exit( -1 ); //exit( -1 );
} //}
} //}
// Will this not happen every time a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after..
// In my storage areas branch, I took this out.. and didn't notice any problems.
if ( false && !n_zones ) {
Debug( 1, "Monitor %s has no zones, adding one.", name );
n_zones = 1;
zones = new Zone *[1];
Coord coords[4] = { Coord( 0, 0 ), Coord( width-1, 0 ), Coord( width-1, height-1 ), Coord( 0, height-1 ) };
zones[0] = new Zone( this, 0, "All", Zone::ACTIVE, Polygon( sizeof(coords)/sizeof(*coords), coords ), RGB_RED, Zone::BLOBS );
}
start_time = last_fps_time = time( 0 ); start_time = last_fps_time = time( 0 );
event = 0; event = 0;
Debug( 1, "Monitor %s has function %d", name, function ); Debug( 1, "Monitor %s\
Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d, LBS = %d", name, label_format, label_coord.X(), label_coord.Y(), label_size ); function: %d\
Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); label_format: '%s', label_x = %d, label_y = %d, label size = %d \
IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d",
name,
function, label_format, label_coord.X(), label_coord.Y(), label_size,
image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion
);
//Set video recording flag for event start constructor and easy reference in code n_linked_monitors = 0;
videoRecording = ((GetOptVideoWriter() == H264PASSTHROUGH) && camera->SupportsNativeVideo()); linked_monitors = 0;
if ( purpose == ANALYSIS ) { adaptive_skip = true;
while( shared_data->last_write_index == (unsigned int)image_buffer_count ReloadLinkedMonitors( p_linked_monitors );
&& shared_data->last_write_time == 0) { videoStore = NULL;
Warning( "Waiting for capture daemon" );
sleep( 1 );
}
ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize());
n_linked_monitors = 0;
linked_monitors = 0;
adaptive_skip = true;
ReloadLinkedMonitors( p_linked_monitors );
}
} // Monitor::Monitor } // Monitor::Monitor
bool Monitor::connect() { bool Monitor::connect() {
@ -553,44 +527,46 @@ bool Monitor::connect() {
shared_data = (SharedData *)mem_ptr; shared_data = (SharedData *)mem_ptr;
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
video_store_data = (VideoStoreData *)((char *)trigger_data + sizeof(TriggerData)); video_store_data = (VideoStoreData *)((char *)trigger_data + sizeof(TriggerData));
struct timeval *shared_timestamps = (struct timeval *)((char *)video_store_data + sizeof(VideoStoreData)); unsigned char *shared_images = (unsigned char *)((char *)video_store_data + sizeof(VideoStoreData));
unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval)));
if ( ((unsigned long)shared_images % 64) != 0 ) { if ( ((unsigned long)shared_images % 64) != 0 ) {
/* Align images buffer to nearest 64 byte boundary */ /* Align images buffer to nearest 64 byte boundary */
Debug(3,"Aligning shared memory images to the next 64 byte boundary"); Debug(3,"Aligning shared memory images to the next 64 byte boundary");
shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64))); shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64)));
} }
Debug(3, "Allocating %d image buffers", image_buffer_count );
image_buffer = new Snapshot[image_buffer_count]; Debug(3, "Allocating %d image buffers", image_buffer_count );
for ( int i = 0; i < image_buffer_count; i++ ) { image_buffer = new ZMPacket[image_buffer_count];
image_buffer[i].timestamp = &(shared_timestamps[i]); for ( int i = 0; i < image_buffer_count; i++ ) {
image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) );
image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
} }
if ( (deinterlacing & 0xff) == 4) { if ( (deinterlacing & 0xff) == 4) {
/* Four field motion adaptive deinterlacing in use */ /* Four field motion adaptive deinterlacing in use */
/* Allocate a buffer for the next image */ /* Allocate a buffer for the next image */
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.timestamp = new struct timeval; }
} pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
if ( ( purpose == ANALYSIS ) && analysis_fps ) { //if ( ( purpose == ANALYSIS ) && analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count // Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains // if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created // alarmed images that must be discarded when event is created
// Couldn't we just make sure there is always enough frames in the ring buffer?
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1; pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
pre_event_buffer = new Snapshot[pre_event_buffer_count]; pre_event_buffer = new ZMPacket[pre_event_buffer_count];
for ( int i = 0; i < pre_event_buffer_count; i++ ) { for ( int i = 0; i < pre_event_buffer_count; i++ ) {
pre_event_buffer[i].timestamp = new struct timeval;
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
} }
}
Debug(3, "Success connecting"); Debug(3, "Success connecting");
return true; return true;
} } // Monitor::connect
Monitor::~Monitor() { Monitor::~Monitor() {
if ( videoStore ) {
delete videoStore;
videoStore = NULL;
}
if ( timestamps ) { if ( timestamps ) {
delete[] timestamps; delete[] timestamps;
timestamps = 0; timestamps = 0;
@ -611,23 +587,12 @@ Monitor::~Monitor() {
if ( (deinterlacing & 0xff) == 4) { if ( (deinterlacing & 0xff) == 4) {
delete next_buffer.image; delete next_buffer.image;
delete next_buffer.timestamp;
} }
for ( int i = 0; i < image_buffer_count; i++ ) { for ( int i = 0; i < image_buffer_count; i++ ) {
delete image_buffer[i].image; delete image_buffer[i].image;
} }
delete[] image_buffer; delete[] image_buffer;
} // end if mem_ptr
for ( int i = 0; i < n_zones; i++ ) {
delete zones[i];
}
delete[] zones;
delete camera;
delete storage;
if ( mem_ptr ) {
if ( purpose == ANALYSIS ) { if ( purpose == ANALYSIS ) {
shared_data->state = state = IDLE; shared_data->state = state = IDLE;
shared_data->last_read_index = image_buffer_count; shared_data->last_read_index = image_buffer_count;
@ -636,7 +601,6 @@ Monitor::~Monitor() {
if ( analysis_fps ) { if ( analysis_fps ) {
for ( int i = 0; i < pre_event_buffer_count; i++ ) { for ( int i = 0; i < pre_event_buffer_count; i++ ) {
delete pre_event_buffer[i].image; delete pre_event_buffer[i].image;
delete pre_event_buffer[i].timestamp;
} }
delete[] pre_event_buffer; delete[] pre_event_buffer;
} }
@ -675,6 +639,14 @@ Monitor::~Monitor() {
} }
#endif // ZM_MEM_MAPPED #endif // ZM_MEM_MAPPED
} // end if mem_ptr } // end if mem_ptr
for ( int i = 0; i < n_zones; i++ ) {
delete zones[i];
}
delete[] zones;
delete camera;
delete storage;
} }
void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) { void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) {
@ -713,17 +685,16 @@ int Monitor::GetImage( int index, int scale ) {
if ( index < 0 || index > image_buffer_count ) { if ( index < 0 || index > image_buffer_count ) {
index = shared_data->last_write_index; index = shared_data->last_write_index;
} }
Debug(3, "GetImage");
if ( index != image_buffer_count ) { if ( index != image_buffer_count ) {
Image *image; Image *image;
// If we are going to be modifying the snapshot before writing, then we need to copy it // If we are going to be modifying the snapshot before writing, then we need to copy it
if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) { if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) {
Snapshot *snap = &image_buffer[index]; ZMPacket *snap = &image_buffer[index];
Image *snap_image = snap->image; Image *snap_image = snap->image;
alarm_image.Assign( *snap_image ); alarm_image.Assign( *snap_image );
//write_image.Assign( *snap_image ); //write_image.Assign( *snap_image );
if ( scale != ZM_SCALE_BASE ) { if ( scale != ZM_SCALE_BASE ) {
@ -731,7 +702,7 @@ int Monitor::GetImage( int index, int scale ) {
} }
if ( !config.timestamp_on_capture ) { if ( !config.timestamp_on_capture ) {
TimestampImage( &alarm_image, snap->timestamp ); TimestampImage( &alarm_image, &snap->timestamp );
} }
image = &alarm_image; image = &alarm_image;
} else { } else {
@ -753,13 +724,13 @@ struct timeval Monitor::GetTimestamp( int index ) const {
} }
if ( index != image_buffer_count ) { if ( index != image_buffer_count ) {
Snapshot *snap = &image_buffer[index]; ZMPacket *snap = &image_buffer[index];
return( *(snap->timestamp) ); return snap->timestamp;
} else { } else {
static struct timeval null_tv = { 0, 0 }; static struct timeval null_tv = { 0, 0 };
return( null_tv ); return null_tv;
} }
} }
@ -778,29 +749,29 @@ unsigned int Monitor::GetLastEvent() const {
double Monitor::GetFPS() const { double Monitor::GetFPS() const {
int index1 = shared_data->last_write_index; int index1 = shared_data->last_write_index;
if ( index1 == image_buffer_count ) { if ( index1 == image_buffer_count ) {
return( 0.0 ); return 0.0;
} }
Snapshot *snap1 = &image_buffer[index1]; ZMPacket *snap1 = &image_buffer[index1];
if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) { if ( !snap1->timestamp.tv_sec ) {
return( 0.0 ); return 0.0;
} }
struct timeval time1 = *snap1->timestamp; struct timeval time1 = snap1->timestamp;
int image_count = image_buffer_count; int image_count = image_buffer_count;
int index2 = (index1+1)%image_buffer_count; int index2 = (index1+1)%image_buffer_count;
if ( index2 == image_buffer_count ) { if ( index2 == image_buffer_count ) {
return( 0.0 ); return 0.0;
} }
Snapshot *snap2 = &image_buffer[index2]; ZMPacket *snap2 = &image_buffer[index2];
while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) { while ( !snap2->timestamp.tv_sec ) {
if ( index1 == index2 ) { if ( index1 == index2 ) {
return( 0.0 ); return 0.0;
} }
index2 = (index2+1)%image_buffer_count; index2 = (index2+1)%image_buffer_count;
snap2 = &image_buffer[index2]; snap2 = &image_buffer[index2];
image_count--; image_count--;
} }
struct timeval time2 = *snap2->timestamp; struct timeval time2 = snap2->timestamp;
double time_diff = tvDiffSec( time2, time1 ); double time_diff = tvDiffSec( time2, time1 );
@ -808,9 +779,9 @@ double Monitor::GetFPS() const {
if ( curr_fps < 0.0 ) { if ( curr_fps < 0.0 ) {
//Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count ); //Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count );
return( 0.0 ); return 0.0;
} }
return( curr_fps ); return curr_fps;
} }
useconds_t Monitor::GetAnalysisRate() { useconds_t Monitor::GetAnalysisRate() {
@ -1028,7 +999,7 @@ void Monitor::DumpZoneImage( const char *zone_string ) {
if ( ( (!staticConfig.SERVER_ID) || ( staticConfig.SERVER_ID == server_id ) ) && mem_ptr ) { if ( ( (!staticConfig.SERVER_ID) || ( staticConfig.SERVER_ID == server_id ) ) && mem_ptr ) {
Debug(3, "Trying to load from local zmc"); Debug(3, "Trying to load from local zmc");
int index = shared_data->last_write_index; int index = shared_data->last_write_index;
Snapshot *snap = &image_buffer[index]; ZMPacket *snap = &image_buffer[index];
zone_image = new Image( *snap->image ); zone_image = new Image( *snap->image );
} else { } else {
Debug(3, "Trying to load from event"); Debug(3, "Trying to load from event");
@ -1169,23 +1140,27 @@ bool Monitor::CheckSignal( const Image *image ) {
} }
bool Monitor::Analyse() { bool Monitor::Analyse() {
mutex.lock();
if ( shared_data->last_read_index == shared_data->last_write_index ) { if ( shared_data->last_read_index == shared_data->last_write_index ) {
// I wonder how often this happens. Maybe if this happens we should sleep or something? // I wonder how often this happens. Maybe if this happens we should sleep or something?
return( false ); //Debug(3, " shared_data->last_read_index == shared_data->last_write_index " );
// If analysis is keeping up, then it happens lots
mutex.unlock();
return false;
} }
struct timeval now; struct timeval now;
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) { if ( analysis_image_count && fps_report_interval && !(analysis_image_count%fps_report_interval) ) {
fps = double(fps_report_interval)/(now.tv_sec - last_fps_time); fps = double(fps_report_interval)/(now.tv_sec - last_analysis_fps_time);
Info( "%s: %d - Analysing at %.2f fps", name, image_count, fps ); Info( "%s: %d - Analysing at %.2f fps", name, image_count, fps );
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf( sql, sizeof(sql), "UPDATE Monitors SET AnalysisFPS = '%.2lf' WHERE Id = '%d'", fps, id ); snprintf( sql, sizeof(sql), "UPDATE Monitors SET AnalysisFPS = '%.2lf' WHERE Id = '%d'", fps, id );
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
} }
last_fps_time = now.tv_sec; last_analysis_fps_time = now.tv_sec;
} }
int index; int index;
@ -1203,7 +1178,8 @@ bool Monitor::Analyse() {
int pending_frames = shared_data->last_write_index - shared_data->last_read_index; int pending_frames = shared_data->last_write_index - shared_data->last_read_index;
if ( pending_frames < 0 ) pending_frames += image_buffer_count; if ( pending_frames < 0 ) pending_frames += image_buffer_count;
Debug( 4, "ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); Debug( 4, "ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d",
shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step );
if ( step <= pending_frames ) { if ( step <= pending_frames ) {
index = (shared_data->last_read_index+step)%image_buffer_count; index = (shared_data->last_read_index+step)%image_buffer_count;
} else { } else {
@ -1216,10 +1192,12 @@ bool Monitor::Analyse() {
index = shared_data->last_write_index%image_buffer_count; index = shared_data->last_write_index%image_buffer_count;
} }
Snapshot *snap = &image_buffer[index]; ZMPacket *snap = &image_buffer[index];
struct timeval *timestamp = snap->timestamp; struct timeval *timestamp = &snap->timestamp;
//Debug(2, "timestamp for index (%d) %s", index, timeval_to_string( *timestamp ) );
Image *snap_image = snap->image; Image *snap_image = snap->image;
// This chunk o fcode is not analysis, so shouldn't be in here. Move it up to whereever analyse is called
if ( shared_data->action ) { if ( shared_data->action ) {
// Can there be more than 1 bit set in the action? Shouldn't these be elseifs? // Can there be more than 1 bit set in the action? Shouldn't these be elseifs?
if ( shared_data->action & RELOAD ) { if ( shared_data->action & RELOAD ) {
@ -1267,7 +1245,6 @@ bool Monitor::Analyse() {
if ( static_undef ) { if ( static_undef ) {
// Sure would be nice to be able to assume that these were already initialized. It's just 1 compare/branch, but really not neccessary. // Sure would be nice to be able to assume that these were already initialized. It's just 1 compare/branch, but really not neccessary.
static_undef = false; static_undef = false;
timestamps = new struct timeval *[pre_event_count];
images = new Image *[pre_event_count]; images = new Image *[pre_event_count];
last_signal = shared_data->signal; last_signal = shared_data->signal;
} }
@ -1279,13 +1256,16 @@ bool Monitor::Analyse() {
Debug(3, "Motion detection is enabled signal(%d) signal_change(%d)", signal, signal_change); Debug(3, "Motion detection is enabled signal(%d) signal_change(%d)", signal, signal_change);
if ( trigger_data->trigger_state != TRIGGER_OFF ) { if ( trigger_data->trigger_state != TRIGGER_OFF ) {
Debug(3, "trigger != off"); Debug(9, "Trigger not oFF state is (%d)", trigger_data->trigger_state );
unsigned int score = 0; unsigned int score = 0;
// Ready means that we have captured the warmpup # of frames
if ( Ready() ) { if ( Ready() ) {
Debug(9, "Ready");
std::string cause; std::string cause;
Event::StringSetMap noteSetMap; Event::StringSetMap noteSetMap;
if ( trigger_data->trigger_state == TRIGGER_ON ) { if ( trigger_data->trigger_state == TRIGGER_ON ) {
score += trigger_data->trigger_score; score += trigger_data->trigger_score;
if ( !event ) { if ( !event ) {
if ( cause.length() ) if ( cause.length() )
@ -1323,10 +1303,12 @@ bool Monitor::Analyse() {
ref_image = *snap_image; ref_image = *snap_image;
} else if ( signal && Active() && (function == MODECT || function == MOCORD) ) { } else if ( signal && Active() && (function == MODECT || function == MOCORD) ) {
Debug(3, "signal and active and modtect");
Event::StringSet zoneSet; Event::StringSet zoneSet;
int motion_score = last_motion_score; int motion_score = last_motion_score;
if ( !(image_count % (motion_frame_skip+1) ) ) { if ( !(image_count % (motion_frame_skip+1) ) ) {
// Get new score. // Get new score.
Debug(3,"before DetectMotion");
motion_score = DetectMotion( *snap_image, zoneSet ); motion_score = DetectMotion( *snap_image, zoneSet );
Debug( 3, "After motion detection, last_motion_score(%d), new motion score(%d)", last_motion_score, motion_score ); Debug( 3, "After motion detection, last_motion_score(%d), new motion score(%d)", last_motion_score, motion_score );
@ -1377,14 +1359,10 @@ bool Monitor::Analyse() {
//TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag? //TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag?
if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) { if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) {
if ( event ) { 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 );
if ( section_length ) { if ( section_length ) {
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > 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; int section_mod = timestamp->tv_sec % section_length;
Debug( 4, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod ); Debug( 3, "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 ( section_mod < last_section_mod ) {
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) { //if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
//if ( state == TAPE ) { //if ( state == TAPE ) {
@ -1419,59 +1397,10 @@ bool Monitor::Analyse() {
if ( state == IDLE ) { if ( state == IDLE ) {
shared_data->state = state = TAPE; shared_data->state = state = TAPE;
} }
//if ( config.overlap_timed_events )
if ( false ) {
int pre_index;
int pre_event_images = pre_event_count;
if ( analysis_fps ) {
// If analysis fps is set,
// compute the index for pre event images in the dedicated buffer
pre_index = image_count%pre_event_buffer_count;
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) {
pre_index = (pre_index + 1)%pre_event_buffer_count;
// Slot is empty, removing image from counter
pre_event_images--;
}
} else {
// If analysis fps is not set (analysis performed at capturing framerate),
// compute the index for pre event images in the capturing buffer
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count;
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) {
pre_index = (pre_index + 1)%image_buffer_count;
// Slot is empty, removing image from counter
pre_event_images--;
}
}
if ( pre_event_images ) {
if ( analysis_fps ) {
for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = pre_event_buffer[pre_index].timestamp;
images[i] = pre_event_buffer[pre_index].image;
pre_index = (pre_index + 1)%pre_event_buffer_count;
}
} else {
for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = image_buffer[pre_index].timestamp;
images[i] = image_buffer[pre_index].image;
pre_index = (pre_index + 1)%image_buffer_count;
}
}
event->AddFrames( pre_event_images, images, timestamps );
}
} // end if false or config.overlap_timed_events
} // end if ! event } // end if ! event
} }
if ( score ) { if ( score ) {
Debug(9, "Score: (%d)", score );
if ( (state == IDLE || state == TAPE || state == PREALARM ) ) { if ( (state == IDLE || state == TAPE || state == PREALARM ) ) {
if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) { if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) {
Info( "%s: %03d - Gone into alarm state", name, image_count ); Info( "%s: %03d - Gone into alarm state", name, image_count );
@ -1484,16 +1413,18 @@ bool Monitor::Analyse() {
// If analysis fps is set, // If analysis fps is set,
// compute the index for pre event images in the dedicated buffer // compute the index for pre event images in the dedicated buffer
pre_index = image_count%pre_event_buffer_count; pre_index = image_count%pre_event_buffer_count;
Debug(3, "Pre Index = (%d) = image_count(%d) %% pre_event_buffer_count (%d)", pre_index, image_count, pre_event_buffer_count );
// Seek forward the next filled slot in to the buffer (oldest data) // Seek forward the next filled slot in to the buffer (oldest data)
// from the current position // from the current position
while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) { // ICON: I think this is supposed to handle when we havn't recorded enough images.
while ( pre_event_images && !pre_event_buffer[pre_index].timestamp.tv_sec ) {
pre_index = (pre_index + 1)%pre_event_buffer_count; pre_index = (pre_index + 1)%pre_event_buffer_count;
// Slot is empty, removing image from counter // Slot is empty, removing image from counter
pre_event_images--; pre_event_images--;
} }
event = new Event( this, *(pre_event_buffer[pre_index].timestamp), cause, noteSetMap ); event = new Event( this, pre_event_buffer[pre_index].timestamp, cause, noteSetMap );
} else { } else {
// If analysis fps is not set (analysis performed at capturing framerate), // If analysis fps is not set (analysis performed at capturing framerate),
// compute the index for pre event images in the capturing buffer // compute the index for pre event images in the capturing buffer
@ -1504,13 +1435,13 @@ bool Monitor::Analyse() {
// Seek forward the next filled slot in to the buffer (oldest data) // Seek forward the next filled slot in to the buffer (oldest data)
// from the current position // from the current position
while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) { while ( pre_event_images && !image_buffer[pre_index].timestamp.tv_sec ) {
pre_index = (pre_index + 1)%image_buffer_count; pre_index = (pre_index + 1)%image_buffer_count;
// Slot is empty, removing image from counter // Slot is empty, removing image from counter
pre_event_images--; pre_event_images--;
} }
event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); event = new Event( this, image_buffer[pre_index].timestamp, cause, noteSetMap );
} }
shared_data->last_event_id = event->Id(); shared_data->last_event_id = event->Id();
//set up video store data //set up video store data
@ -1522,13 +1453,13 @@ bool Monitor::Analyse() {
if ( pre_event_images ) { if ( pre_event_images ) {
if ( analysis_fps ) { if ( analysis_fps ) {
for ( int i = 0; i < pre_event_images; i++ ) { for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = pre_event_buffer[pre_index].timestamp; timestamps[i] = &pre_event_buffer[pre_index].timestamp;
images[i] = pre_event_buffer[pre_index].image; images[i] = pre_event_buffer[pre_index].image;
pre_index = (pre_index + 1)%pre_event_buffer_count; pre_index = (pre_index + 1)%pre_event_buffer_count;
} }
} else { } else {
for ( int i = 0; i < pre_event_images; i++ ) { for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = image_buffer[pre_index].timestamp; timestamps[i] = &image_buffer[pre_index].timestamp;
images[i] = image_buffer[pre_index].image; images[i] = image_buffer[pre_index].image;
pre_index = (pre_index + 1)%image_buffer_count; pre_index = (pre_index + 1)%image_buffer_count;
} }
@ -1596,12 +1527,14 @@ bool Monitor::Analyse() {
if ( state == PREALARM ) if ( state == PREALARM )
Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image );
else else
event->AddFrame( snap_image, *timestamp, score, &alarm_image ); //event->AddFrame( snap_image, *timestamp, score, &alarm_image );
event->AddPacket( snap, score, &alarm_image );
} else { } else {
if ( state == PREALARM ) if ( state == PREALARM )
Event::AddPreAlarmFrame( snap_image, *timestamp, score ); Event::AddPreAlarmFrame( snap_image, *timestamp, score );
else else
event->AddFrame( snap_image, *timestamp, score ); //event->AddFrame( snap_image, *timestamp, score );
event->AddPacket( snap, score );
} }
} else { } else {
for( int i = 0; i < n_zones; i++ ) { for( int i = 0; i < n_zones; i++ ) {
@ -1614,7 +1547,8 @@ bool Monitor::Analyse() {
if ( state == PREALARM ) if ( state == PREALARM )
Event::AddPreAlarmFrame( snap_image, *timestamp, score ); Event::AddPreAlarmFrame( snap_image, *timestamp, score );
else else
event->AddFrame( snap_image, *timestamp, score ); event->AddPacket( snap, score );
//event->AddFrame( snap_image, *timestamp, score );
} }
if ( event && noteSetMap.size() > 0 ) if ( event && noteSetMap.size() > 0 )
event->updateNotes( noteSetMap ); event->updateNotes( noteSetMap );
@ -1631,13 +1565,17 @@ bool Monitor::Analyse() {
//} //}
if ( !(image_count%(frame_skip+1)) ) { if ( !(image_count%(frame_skip+1)) ) {
if ( config.bulk_frame_interval > 1 ) { if ( config.bulk_frame_interval > 1 ) {
event->AddFrame( snap_image, *timestamp, (event->Frames()<pre_event_count?0:-1) ); event->AddPacket( snap, (event->Frames()<pre_event_count?0:-1) );
//event->AddFrame( snap_image, *timestamp, (event->Frames()<pre_event_count?0:-1) );
} else { } else {
event->AddFrame( snap_image, *timestamp ); //event->AddFrame( snap_image, *timestamp );
event->AddPacket( snap );
} }
} }
} }
} // end if ! IDLE } // end if ! IDLE
} else {
Debug(3,"Not ready?");
} }
} else { } else {
Debug(3, "trigger == off"); Debug(3, "trigger == off");
@ -1660,17 +1598,19 @@ bool Monitor::Analyse() {
} // end if Enabled() } // end if Enabled()
shared_data->last_read_index = index % image_buffer_count; shared_data->last_read_index = index % image_buffer_count;
//shared_data->last_read_time = image_buffer[index].timestamp->tv_sec;
shared_data->last_read_time = now.tv_sec; shared_data->last_read_time = now.tv_sec;
mutex.unlock();
if ( analysis_fps ) { if ( analysis_fps ) {
// If analysis fps is set, add analysed image to dedicated pre event buffer // If analysis fps is set, add analysed image to dedicated pre event buffer
int pre_index = image_count%pre_event_buffer_count; Debug(3,"analysis fps image_count(%d) pre_event_buffer_count(%d)", image_count, pre_event_buffer_count );
int pre_index = pre_event_buffer_count ? image_count%pre_event_buffer_count : 0;
Debug(3,"analysis fps pre_index(%d) = image_count(%d) %% pre_event_buffer_count(%d)", pre_index, image_count, pre_event_buffer_count );
pre_event_buffer[pre_index].image->Assign(*snap->image); pre_event_buffer[pre_index].image->Assign(*snap->image);
memcpy( pre_event_buffer[pre_index].timestamp, snap->timestamp, sizeof(struct timeval) ); pre_event_buffer[pre_index].timestamp = snap->timestamp;
} }
image_count++; analysis_image_count++;
return true; return true;
} }
@ -1850,7 +1790,7 @@ void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) {
#if ZM_HAS_V4L #if ZM_HAS_V4L
int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) { int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) {
std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'";
; ;
if ( device[0] ) { if ( device[0] ) {
sql += " AND Device='"; sql += " AND Device='";
@ -1915,6 +1855,8 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose
int savejpegs = atoi(dbrow[col]); col++; int savejpegs = atoi(dbrow[col]); col++;
VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++;
std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_container = dbrow[col] ? dbrow[col] : ""; col++;
bool record_audio = (*dbrow[col] != '0'); col++; bool record_audio = (*dbrow[col] != '0'); col++;
int brightness = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++;
@ -1992,6 +1934,8 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose
savejpegs, savejpegs,
videowriter, videowriter,
encoderparams, encoderparams,
output_codec,
output_container,
record_audio, record_audio,
event_prefix, event_prefix,
label_format, label_format,
@ -2039,7 +1983,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) {
std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'";
if ( staticConfig.SERVER_ID ) { if ( staticConfig.SERVER_ID ) {
sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID );
} }
@ -2085,6 +2029,8 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
int savejpegs = atoi(dbrow[col]); col++; int savejpegs = atoi(dbrow[col]); col++;
VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++;
std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_container = dbrow[col] ? dbrow[col] : ""; col++;
bool record_audio = (*dbrow[col] != '0'); col++; bool record_audio = (*dbrow[col] != '0'); col++;
int brightness = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++;
@ -2176,6 +2122,8 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
savejpegs, savejpegs,
videowriter, videowriter,
encoderparams, encoderparams,
output_codec,
output_container,
record_audio, record_audio,
event_prefix, event_prefix,
label_format, label_format,
@ -2222,7 +2170,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
} }
int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) {
std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'";
if ( file[0] ) { if ( file[0] ) {
sql += " AND Path='"; sql += " AND Path='";
sql += file; sql += file;
@ -2264,6 +2212,8 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
int savejpegs = atoi(dbrow[col]); col++; int savejpegs = atoi(dbrow[col]); col++;
VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++;
std::string encoderparams = dbrow[col]; col++; std::string encoderparams = dbrow[col]; col++;
std::string output_codec = dbrow[col]; col++;
std::string output_container = dbrow[col]; col++;
bool record_audio = (*dbrow[col] != '0'); col++; bool record_audio = (*dbrow[col] != '0'); col++;
int brightness = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++;
@ -2325,6 +2275,8 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
savejpegs, savejpegs,
videowriter, videowriter,
encoderparams, encoderparams,
output_codec,
output_container,
record_audio, record_audio,
event_prefix, event_prefix,
label_format, label_format,
@ -2372,7 +2324,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
#if HAVE_LIBAVFORMAT #if HAVE_LIBAVFORMAT
int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) {
std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'";
if ( file[0] ) { if ( file[0] ) {
sql += " AND Path = '"; sql += " AND Path = '";
sql += file; sql += file;
@ -2417,6 +2369,8 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
int savejpegs = atoi(dbrow[col]); col++; int savejpegs = atoi(dbrow[col]); col++;
VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++;
std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_container = dbrow[col] ? dbrow[col] : ""; col++;
bool record_audio = (*dbrow[col] != '0'); col++; bool record_audio = (*dbrow[col] != '0'); col++;
int brightness = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++;
@ -2484,6 +2438,8 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
savejpegs, savejpegs,
videowriter, videowriter,
encoderparams, encoderparams,
output_codec,
output_container,
record_audio, record_audio,
event_prefix, event_prefix,
label_format, label_format,
@ -2532,7 +2488,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
std::string sql = stringtf( "select Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id ); std::string sql = stringtf( "select Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, OutputCodec, OutputContainer, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id );
zmDbRow dbrow; zmDbRow dbrow;
if ( ! dbrow.fetch( sql.c_str() ) ) { if ( ! dbrow.fetch( sql.c_str() ) ) {
@ -2592,7 +2548,9 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
bool rtsp_describe = (dbrow[col] && *dbrow[col] != '0'); col++; bool rtsp_describe = (dbrow[col] && *dbrow[col] != '0'); col++;
int savejpegs = atoi(dbrow[col]); col++; int savejpegs = atoi(dbrow[col]); col++;
VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++; VideoWriter videowriter = (VideoWriter)atoi(dbrow[col]); col++;
std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++; std::string encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_codec = dbrow[col] ? dbrow[col] : ""; col++;
std::string output_container = dbrow[col] ? dbrow[col] : ""; col++;
bool record_audio = (*dbrow[col] != '0'); col++; bool record_audio = (*dbrow[col] != '0'); col++;
int brightness = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++;
@ -2811,6 +2769,8 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
savejpegs, savejpegs,
videowriter, videowriter,
encoderparams, encoderparams,
output_codec,
output_container,
record_audio, record_audio,
event_prefix, event_prefix,
label_format, label_format,
@ -2858,12 +2818,14 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
* Returns -1 on failure. * Returns -1 on failure.
*/ */
int Monitor::Capture() { int Monitor::Capture() {
mutex.lock();
static int FirstCapture = 1; // Used in de-interlacing to indicate whether this is the even or odd image static int FirstCapture = 1; // Used in de-interlacing to indicate whether this is the even or odd image
unsigned int index = image_count % image_buffer_count; unsigned int index = image_count % image_buffer_count;
Image* capture_image = image_buffer[index].image; Image* capture_image = image_buffer[index].image;
ZMPacket packet; ZMPacket *packet = &image_buffer[index];
packet.set_image(capture_image); // clears frame
packet->reset();
int captureResult = 0; int captureResult = 0;
unsigned int deinterlacing_value = deinterlacing & 0xff; unsigned int deinterlacing_value = deinterlacing & 0xff;
@ -2872,16 +2834,17 @@ int Monitor::Capture() {
/* Copy the next image into the shared memory */ /* Copy the next image into the shared memory */
capture_image->CopyBuffer(*(next_buffer.image)); capture_image->CopyBuffer(*(next_buffer.image));
} }
/* Capture a new next image */ /* Capture a new next image */
captureResult = camera->Capture(packet); captureResult = camera->Capture(*packet);
gettimeofday( &packet->timestamp, NULL );
if ( FirstCapture ) { if ( FirstCapture ) {
FirstCapture = 0; FirstCapture = 0;
return 0; return 0;
} }
} else { } else {
captureResult = camera->Capture(packet); captureResult = camera->Capture(*packet);
gettimeofday( &packet->timestamp, NULL );
if ( captureResult < 0 ) { if ( captureResult < 0 ) {
// Unable to capture image for temporary reason // Unable to capture image for temporary reason
// Fake a signal loss image // Fake a signal loss image
@ -2928,7 +2891,7 @@ int Monitor::Capture() {
if ( (index == shared_data->last_read_index) && (function > MONITOR) ) { if ( (index == shared_data->last_read_index) && (function > MONITOR) ) {
Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count );
time_t now = time(0); time_t now = time(0);
double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp.tv_sec);
time_t last_read_delta = now - shared_data->last_read_time; time_t last_read_delta = now - shared_data->last_read_time;
if ( last_read_delta > (image_buffer_count/approxFps) ) { if ( last_read_delta > (image_buffer_count/approxFps) ) {
Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta )
@ -2939,44 +2902,96 @@ int Monitor::Capture() {
if ( privacy_bitmask ) if ( privacy_bitmask )
capture_image->MaskPrivacy( privacy_bitmask ); capture_image->MaskPrivacy( privacy_bitmask );
//gettimeofday( image_buffer[index].timestamp, NULL );
if ( config.timestamp_on_capture ) { if ( config.timestamp_on_capture ) {
TimestampImage( capture_image, &packet.timestamp ); TimestampImage( capture_image, &packet->timestamp );
} }
int video_stream_id = camera->get_VideoStreamId(); int video_stream_id = camera->get_VideoStreamId();
//packetqueue.clear_unwanted_packets(&video_store_data->recording, video_stream_id); #if 0
//videoStore->write_packets(packetqueue); //Video recording
if ( ! event ) { if ( video_store_data->recording.tv_sec ) {
if ( shared_data->last_event_id != this->GetVideoWriterEventId() ) {
Debug(2, "Have change of event. last_event(%d), our current (%d)",
shared_data->last_event_id,
this->GetVideoWriterEventId()
);
if ( videoStore ) {
Debug(2, "Have videostore already?");
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it.
// Also don't know how much it matters for audio.
int ret = videoStore->writePacket( packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}
delete videoStore;
videoStore = NULL;
this->SetVideoWriterEventId( 0 );
} // end if videoStore
} // end if end of recording
if ( shared_data->last_event_id and ! videoStore ) {
Debug(2,"New videostore");
videoStore = new VideoStore(
(const char *) video_store_data->event_file,
"mp4",
camera->get_VideoStream(),
( record_audio ? camera->get_AudioStream() : NULL ),
video_store_data->recording.tv_sec,
this );
if ( ! videoStore->open() ) {
delete videoStore;
videoStore = NULL;
} else {
this->SetVideoWriterEventId(shared_data->last_event_id);
Debug(2, "Clearing packets");
// Clear all packets that predate the moment when the recording began
packetqueue.clear_unwanted_packets(&video_store_data->recording, video_stream_id);
videoStore->write_packets(packetqueue);
} // success opening
} // end if ! was recording
} else { // Not recording
if ( videoStore ) {
Info("Deleting videoStore instance");
delete videoStore;
videoStore = NULL;
this->SetVideoWriterEventId( 0 );
}
// Buffer video packets, since we are not recording. // Buffer video packets, since we are not recording.
// All audio packets are keyframes, so only if it's a video keyframe // All audio packets are keyframes, so only if it's a video keyframe
if ( ( packet.packet.stream_index == video_stream_id ) && ( packet.keyframe ) ) { if ( ( packet->packet.stream_index == video_stream_id ) && ( packet->keyframe ) ) {
packetqueue.clearQueue( this->GetPreEventCount(), video_stream_id ); packetqueue.clearQueue( this->GetPreEventCount(), video_stream_id );
} }
// The following lines should ensure that the queue always begins with a video keyframe // The following lines should ensure that the queue always begins with a video keyframe
if ( packet.packet.stream_index == camera->get_AudioStreamId() ) { if ( packet->packet.stream_index == camera->get_AudioStreamId() ) {
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() ); //Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
if ( record_audio && packetqueue.size() ) { if ( record_audio && packetqueue.size() ) {
// if it's audio, and we are doing audio, and there is already something in the queue // if it's audio, and we are doing audio, and there is already something in the queue
packetqueue.queuePacket( &packet ); packetqueue.queuePacket( packet );
} }
} else if ( packet.packet.stream_index == video_stream_id ) { } else if ( packet->packet.stream_index == video_stream_id ) {
if ( packet.keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue if ( packet->keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
packetqueue.queuePacket( &packet ); packetqueue.queuePacket( packet );
} // end if audio or video } // end if audio or video
} else { } // end if recording or not
if ( videoStore ) {
//Write the packet to our video store, it will be smart enough to know what to do //Write the packet to our video store, it will be smart enough to know what to do
if ( ! event->WritePacket( packet ) ) { int ret = videoStore->writePacket( packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("problem writing packet"); Warning("problem writing packet");
} }
} // end if recording or not }
#endif
} // end if deinterlacing } // end if deinterlacing
shared_data->signal = CheckSignal(capture_image); shared_data->signal = CheckSignal(capture_image);
shared_data->last_write_index = index; shared_data->last_write_index = index;
shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; shared_data->last_write_time = image_buffer[index].timestamp.tv_sec;
mutex.unlock();
image_count++; image_count++;
if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) { if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) {
@ -2984,7 +2999,7 @@ int Monitor::Capture() {
if ( !captureResult ) { if ( !captureResult ) {
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
} else { } else {
now.tv_sec = image_buffer[index].timestamp->tv_sec; now.tv_sec = image_buffer[index].timestamp.tv_sec;
} }
// If we are too fast, we get div by zero. This seems to happen in the case of audio packets. // If we are too fast, we get div by zero. This seems to happen in the case of audio packets.
@ -3018,7 +3033,7 @@ int Monitor::Capture() {
shared_data->action &= ~SET_SETTINGS; shared_data->action &= ~SET_SETTINGS;
} }
return captureResult; return captureResult;
} } // end Monitor::Capture
void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) const { void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) const {
if ( label_format[0] ) { if ( label_format[0] ) {
@ -3305,6 +3320,19 @@ int Monitor::PostCapture() {
} }
Monitor::Orientation Monitor::getOrientation() const { return orientation; } Monitor::Orientation Monitor::getOrientation() const { return orientation; }
Monitor::Snapshot *Monitor::getSnapshot() { ZMPacket *Monitor::getSnapshot() {
return &image_buffer[ shared_data->last_write_index%image_buffer_count ]; return &image_buffer[ shared_data->last_write_index%image_buffer_count ];
} }
// Wait for camera to get an image, and then assign it as the base reference image. So this should be done as the first task in the analysis thread startup.
void Monitor::get_ref_image() {
while (
( shared_data->last_write_index == (unsigned int)image_buffer_count )
&&
( shared_data->last_write_time == 0 )
) {
Warning( "Waiting for capture daemon" );
usleep( 100000 );
}
ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize());
}

View File

@ -31,6 +31,7 @@
#include "zm_event.h" #include "zm_event.h"
#include "zm_videostore.h" #include "zm_videostore.h"
#include "zm_packetqueue.h" #include "zm_packetqueue.h"
#include "zm_thread.h"
class Monitor; class Monitor;
#include "zm_camera.h" #include "zm_camera.h"
@ -153,13 +154,6 @@ protected:
char trigger_showtext[256]; char trigger_showtext[256];
} TriggerData; } TriggerData;
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
struct Snapshot {
struct timeval *timestamp;
Image *image;
void* padding;
};
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat //TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit //sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
typedef struct { typedef struct {
@ -167,11 +161,13 @@ protected:
uint32_t current_event; uint32_t current_event;
char event_file[4096]; char event_file[4096];
timeval recording; // used as both bool and a pointer to the timestamp when recording should begin timeval recording; // used as both bool and a pointer to the timestamp when recording should begin
//uint32_t frameNumber;
} VideoStoreData; } VideoStoreData;
VideoStore *videoStore; VideoStore *videoStore;
zm_packetqueue packetqueue; zm_packetqueue packetqueue;
Mutex mutex;
std::string output_codec;
std::string output_container;
class MonitorLink { class MonitorLink {
protected: protected:
@ -197,7 +193,6 @@ protected:
int last_state; int last_state;
int last_event_id; int last_event_id;
public: public:
MonitorLink( int p_id, const char *p_name ); MonitorLink( int p_id, const char *p_name );
~MonitorLink(); ~MonitorLink();
@ -286,6 +281,7 @@ protected:
Purpose purpose; // What this monitor has been created to do Purpose purpose; // What this monitor has been created to do
int event_count; int event_count;
int image_count; int image_count;
int analysis_image_count;
int ready_count; int ready_count;
int first_alarm_count; int first_alarm_count;
int last_alarm_count; int last_alarm_count;
@ -294,6 +290,7 @@ protected:
State state; State state;
time_t start_time; time_t start_time;
time_t last_fps_time; time_t last_fps_time;
time_t last_analysis_fps_time;
time_t auto_resume_time; time_t auto_resume_time;
unsigned int last_motion_score; unsigned int last_motion_score;
@ -313,9 +310,9 @@ protected:
TriggerData *trigger_data; TriggerData *trigger_data;
VideoStoreData *video_store_data; VideoStoreData *video_store_data;
Snapshot *image_buffer; ZMPacket *image_buffer;
Snapshot next_buffer; /* Used by four field deinterlacing */ ZMPacket next_buffer; /* Used by four field deinterlacing */
Snapshot *pre_event_buffer; ZMPacket *pre_event_buffer;
Camera *camera; Camera *camera;
@ -351,6 +348,8 @@ public:
int p_savejpegs, int p_savejpegs,
VideoWriter p_videowriter, VideoWriter p_videowriter,
std::string p_encoderparams, std::string p_encoderparams,
std::string p_output_codec,
std::string p_output_container,
bool p_record_audio, bool p_record_audio,
const char *p_event_prefix, const char *p_event_prefix,
const char *p_label_format, const char *p_label_format,
@ -442,6 +441,8 @@ public:
VideoWriter GetOptVideoWriter() const { return( videowriter ); } VideoWriter GetOptVideoWriter() const { return( videowriter ); }
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); } const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
const std::string &GetEncoderOptions() const { return( encoderparams ); } const std::string &GetEncoderOptions() const { return( encoderparams ); }
const std::string &OutputCodec() const { return output_codec; }
const std::string &OutputContainer() const { return output_container; }
uint32_t GetLastEventId() const { return shared_data->last_event_id; } uint32_t GetLastEventId() const { return shared_data->last_event_id; }
uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; } uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; }
@ -450,7 +451,7 @@ public:
unsigned int GetPreEventCount() const { return pre_event_count; }; unsigned int GetPreEventCount() const { return pre_event_count; };
State GetState() const; State GetState() const;
int GetImage( int index=-1, int scale=100 ); int GetImage( int index=-1, int scale=100 );
Snapshot *getSnapshot(); ZMPacket *getSnapshot();
struct timeval GetTimestamp( int index=-1 ) const; struct timeval GetTimestamp( int index=-1 ) const;
void UpdateAdaptiveSkip(); void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate(); useconds_t GetAnalysisRate();
@ -471,6 +472,7 @@ public:
inline void setStartupTime( time_t p_time ) { inline void setStartupTime( time_t p_time ) {
shared_data->startup_time = p_time; shared_data->startup_time = p_time;
} }
void get_ref_image();
void actionReload(); void actionReload();
void actionEnable(); void actionEnable();

View File

@ -665,13 +665,13 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
if ( !paused && !delayed ) { if ( !paused && !delayed ) {
// Send the next frame // Send the next frame
Monitor::Snapshot *snap = &monitor->image_buffer[index]; ZMPacket *snap = &monitor->image_buffer[index];
if ( !sendFrame( snap->image, snap->timestamp ) ) { if ( !sendFrame( snap->image, &snap->timestamp ) ) {
Debug(2, "sendFrame failed, quiting."); Debug(2, "sendFrame failed, quiting.");
zm_terminate = true; zm_terminate = true;
} }
memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); last_frame_timestamp = snap->timestamp;
//frame_sent = true; //frame_sent = true;
temp_read_index = temp_write_index; temp_read_index = temp_write_index;
@ -679,14 +679,14 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
} }
if ( buffered_playback ) { if ( buffered_playback ) {
if ( monitor->shared_data->valid ) { if ( monitor->shared_data->valid ) {
if ( monitor->image_buffer[index].timestamp->tv_sec ) { if ( monitor->image_buffer[index].timestamp.tv_sec ) {
int temp_index = temp_write_index%temp_image_buffer_count; int temp_index = temp_write_index%temp_image_buffer_count;
Debug( 2, "Storing frame %d", temp_index ); Debug( 2, "Storing frame %d", temp_index );
if ( !temp_image_buffer[temp_index].valid ) { if ( !temp_image_buffer[temp_index].valid ) {
snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index );
temp_image_buffer[temp_index].valid = true; temp_image_buffer[temp_index].valid = true;
} }
memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); temp_image_buffer[temp_index].timestamp = monitor->image_buffer[index].timestamp;
monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality );
temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count );
if ( temp_write_index == temp_read_index ) { if ( temp_write_index == temp_read_index ) {
@ -764,7 +764,7 @@ void MonitorStream::SingleImage( int scale ) {
int img_buffer_size = 0; int img_buffer_size = 0;
static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE];
Image scaled_image; Image scaled_image;
Monitor::Snapshot *snap = monitor->getSnapshot(); ZMPacket *snap = monitor->getSnapshot();
Image *snap_image = snap->image; Image *snap_image = snap->image;
if ( scale != ZM_SCALE_BASE ) { if ( scale != ZM_SCALE_BASE ) {
@ -773,7 +773,7 @@ void MonitorStream::SingleImage( int scale ) {
snap_image = &scaled_image; snap_image = &scaled_image;
} }
if ( !config.timestamp_on_capture ) { if ( !config.timestamp_on_capture ) {
monitor->TimestampImage( snap_image, snap->timestamp ); monitor->TimestampImage( snap_image, &snap->timestamp );
} }
snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); snap_image->EncodeJpeg( img_buffer, &img_buffer_size );
@ -784,7 +784,7 @@ void MonitorStream::SingleImage( int scale ) {
void MonitorStream::SingleImageRaw( int scale ) { void MonitorStream::SingleImageRaw( int scale ) {
Image scaled_image; Image scaled_image;
Monitor::Snapshot *snap = monitor->getSnapshot(); ZMPacket *snap = monitor->getSnapshot();
Image *snap_image = snap->image; Image *snap_image = snap->image;
if ( scale != ZM_SCALE_BASE ) { if ( scale != ZM_SCALE_BASE ) {
@ -793,7 +793,7 @@ void MonitorStream::SingleImageRaw( int scale ) {
snap_image = &scaled_image; snap_image = &scaled_image;
} }
if ( !config.timestamp_on_capture ) { if ( !config.timestamp_on_capture ) {
monitor->TimestampImage( snap_image, snap->timestamp ); monitor->TimestampImage( snap_image, &snap->timestamp );
} }
fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() );
@ -806,7 +806,7 @@ void MonitorStream::SingleImageZip( int scale ) {
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
Image scaled_image; Image scaled_image;
Monitor::Snapshot *snap = monitor->getSnapshot(); ZMPacket *snap = monitor->getSnapshot();
Image *snap_image = snap->image; Image *snap_image = snap->image;
if ( scale != ZM_SCALE_BASE ) { if ( scale != ZM_SCALE_BASE ) {
@ -815,7 +815,7 @@ void MonitorStream::SingleImageZip( int scale ) {
snap_image = &scaled_image; snap_image = &scaled_image;
} }
if ( !config.timestamp_on_capture ) { if ( !config.timestamp_on_capture ) {
monitor->TimestampImage( snap_image, snap->timestamp ); monitor->TimestampImage( snap_image, &snap->timestamp );
} }
snap_image->Zip( img_buffer, &img_buffer_size ); snap_image->Zip( img_buffer, &img_buffer_size );

View File

@ -117,10 +117,10 @@ void VideoStream::SetupFormat( ) {
void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) { void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) {
/* ffmpeg format matching */ /* ffmpeg format matching */
switch(colours) { switch ( colours ) {
case ZM_COLOUR_RGB24: case ZM_COLOUR_RGB24:
{ {
if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
/* BGR subpixel order */ /* BGR subpixel order */
pf = AV_PIX_FMT_BGR24; pf = AV_PIX_FMT_BGR24;
} else { } else {
@ -211,39 +211,52 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
Debug( 1, "Allocated stream" ); Debug( 1, "Allocated stream" );
AVCodecContext *c = ost->codec; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
codec_context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_context, ost->codecpar);
#else
codec_context = ost->codec;
#endif
c->codec_id = codec->id; codec_context->codec_id = codec->id;
c->codec_type = codec->type; codec_context->codec_type = codec->type;
c->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; codec_context->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P;
if ( bitrate <= 100 ) { if ( bitrate <= 100 ) {
// Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best.
// This gets rid of artifacts in the beginning of the movie; and well, even quality. // This gets rid of artifacts in the beginning of the movie; and well, even quality.
c->flags |= CODEC_FLAG_QSCALE; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
c->global_quality = FF_QP2LAMBDA * (31 - (31 * (bitrate / 100.0))); codec_context->flags |= AV_CODEC_FLAG_QSCALE;
#else
codec_context->flags |= CODEC_FLAG_QSCALE;
#endif
codec_context->global_quality = FF_QP2LAMBDA * (31 - (31 * (bitrate / 100.0)));
} else { } else {
c->bit_rate = bitrate; codec_context->bit_rate = bitrate;
} }
/* resolution must be a multiple of two */ /* resolution must be a multiple of two */
c->width = width; codec_context->width = width;
c->height = height; codec_context->height = height;
/* time base: this is the fundamental unit of time (in seconds) in terms /* time base: this is the fundamental unit of time (in seconds) in terms
of which frame timestamps are represented. for fixed-fps content, of which frame timestamps are represented. for fixed-fps content,
timebase should be 1/framerate and timestamp increments should be timebase should be 1/framerate and timestamp increments should be
identically 1. */ identically 1. */
c->time_base.den = frame_rate; codec_context->time_base.den = frame_rate;
c->time_base.num = 1; codec_context->time_base.num = 1;
Debug( 1, "Will encode in %d fps.", c->time_base.den ); Debug( 1, "Will encode in %d fps.", codec_context->time_base.den );
/* emit one intra frame every second */ /* emit one intra frame every second */
c->gop_size = frame_rate; codec_context->gop_size = frame_rate;
// some formats want stream headers to be separate // some formats want stream headers to be separate
if ( of->flags & AVFMT_GLOBALHEADER ) if ( of->flags & AVFMT_GLOBALHEADER )
c->flags |= CODEC_FLAG_GLOBAL_HEADER; #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0)
codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
#else
codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
#endif
} else { } else {
Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); Fatal( "of->video_codec == AV_CODEC_ID_NONE" );
} }
@ -278,13 +291,11 @@ void VideoStream::OpenStream( ) {
/* now that all the parameters are set, we can open the /* now that all the parameters are set, we can open the
video codecs and allocate the necessary encode buffers */ video codecs and allocate the necessary encode buffers */
if ( ost ) { if ( ost ) {
AVCodecContext *c = ost->codec;
/* open the codec */ /* open the codec */
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
if ( (avRet = avcodec_open( c, codec )) < 0 ) if ( (avRet = avcodec_open( codec_context, codec )) < 0 )
#else #else
if ( (avRet = avcodec_open2( c, codec, 0 )) < 0 ) if ( (avRet = avcodec_open2( codec_context, codec, 0 )) < 0 )
#endif #endif
{ {
Fatal( "Could not open codec. Error code %d \"%s\"", avRet, av_err2str( avRet ) ); Fatal( "Could not open codec. Error code %d \"%s\"", avRet, av_err2str( avRet ) );
@ -293,19 +304,15 @@ void VideoStream::OpenStream( ) {
Debug( 1, "Opened codec" ); Debug( 1, "Opened codec" );
/* allocate the encoded raw picture */ /* allocate the encoded raw picture */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) opicture = zm_av_frame_alloc( );
opicture = av_frame_alloc( );
#else
opicture = avcodec_alloc_frame( );
#endif
if ( !opicture ) { if ( !opicture ) {
Panic( "Could not allocate opicture" ); Panic( "Could not allocate opicture" );
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int size = av_image_get_buffer_size( c->pix_fmt, c->width, c->height, 1 ); int size = av_image_get_buffer_size( codec_context->pix_fmt, codec_context->width, codec_context->height, 1 );
#else #else
int size = avpicture_get_size( c->pix_fmt, c->width, c->height ); int size = avpicture_get_size( codec_context->pix_fmt, codec_context->width, codec_context->height );
#endif #endif
uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); uint8_t *opicture_buf = (uint8_t *)av_malloc( size );
@ -315,17 +322,17 @@ void VideoStream::OpenStream( ) {
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(opicture->data, opicture->linesize, av_image_fill_arrays(opicture->data, opicture->linesize,
opicture_buf, c->pix_fmt, c->width, c->height, 1); opicture_buf, codec_context->pix_fmt, codec_context->width, codec_context->height, 1);
#else #else
avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, avpicture_fill( (AVPicture *)opicture, opicture_buf, codec_context->pix_fmt,
c->width, c->height ); codec_context->width, codec_context->height );
#endif #endif
/* if the output format is not identical to the input format, then a temporary /* if the output format is not identical to the input format, then a temporary
picture is needed too. It is then converted to the required picture is needed too. It is then converted to the required
output format */ output format */
tmp_opicture = NULL; tmp_opicture = NULL;
if ( c->pix_fmt != pf ) { if ( codec_context->pix_fmt != pf ) {
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
tmp_opicture = av_frame_alloc( ); tmp_opicture = av_frame_alloc( );
#else #else
@ -335,9 +342,9 @@ void VideoStream::OpenStream( ) {
Panic( "Could not allocate tmp_opicture" ); Panic( "Could not allocate tmp_opicture" );
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int size = av_image_get_buffer_size( pf, c->width, c->height,1 ); int size = av_image_get_buffer_size( pf, codec_context->width, codec_context->height,1 );
#else #else
int size = avpicture_get_size( pf, c->width, c->height ); int size = avpicture_get_size( pf, codec_context->width, codec_context->height );
#endif #endif
uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size );
if ( !tmp_opicture_buf ) { if ( !tmp_opicture_buf ) {
@ -347,10 +354,10 @@ void VideoStream::OpenStream( ) {
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(tmp_opicture->data, av_image_fill_arrays(tmp_opicture->data,
tmp_opicture->linesize, tmp_opicture_buf, pf, tmp_opicture->linesize, tmp_opicture_buf, pf,
c->width, c->height, 1); codec_context->width, codec_context->height, 1);
#else #else
avpicture_fill( (AVPicture *)tmp_opicture, avpicture_fill( (AVPicture *)tmp_opicture,
tmp_opicture_buf, pf, c->width, c->height ); tmp_opicture_buf, pf, codec_context->width, codec_context->height );
#endif #endif
} }
} }
@ -375,7 +382,12 @@ void VideoStream::OpenStream( ) {
} }
video_outbuf = NULL; video_outbuf = NULL;
#if LIBAVFORMAT_VERSION_CHECK(57, 0, 0, 0, 0)
if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO &&
codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) {
#else
if ( !(of->flags & AVFMT_RAWPICTURE) ) { if ( !(of->flags & AVFMT_RAWPICTURE) ) {
#endif
/* allocate output buffer */ /* allocate output buffer */
/* XXX: API change will be done */ /* XXX: API change will be done */
// TODO: Make buffer dynamic. // TODO: Make buffer dynamic.
@ -446,6 +458,8 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi
if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 ) { if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 ) {
Fatal("pthread_mutex_init failed"); Fatal("pthread_mutex_init failed");
} }
codec_context = NULL;
} }
VideoStream::~VideoStream( ) { VideoStream::~VideoStream( ) {
@ -481,7 +495,7 @@ VideoStream::~VideoStream( ) {
/* close each codec */ /* close each codec */
if ( ost ) { if ( ost ) {
avcodec_close( ost->codec ); avcodec_close( codec_context );
av_free( opicture->data[0] ); av_free( opicture->data[0] );
av_frame_free( &opicture ); av_frame_free( &opicture );
if ( tmp_opicture ) { if ( tmp_opicture ) {
@ -564,17 +578,15 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
static struct SwsContext *img_convert_ctx = 0; static struct SwsContext *img_convert_ctx = 0;
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
AVCodecContext *c = ost->codec; if ( codec_context->pix_fmt != pf ) {
if ( c->pix_fmt != pf ) {
memcpy( tmp_opicture->data[0], buffer, buffer_size ); memcpy( tmp_opicture->data[0], buffer, buffer_size );
#ifdef HAVE_LIBSWSCALE #ifdef HAVE_LIBSWSCALE
if ( !img_convert_ctx ) { if ( !img_convert_ctx ) {
img_convert_ctx = sws_getCachedContext( NULL, c->width, c->height, pf, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL ); img_convert_ctx = sws_getCachedContext( NULL, codec_context->width, codec_context->height, pf, codec_context->width, codec_context->height, codec_context->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL );
if ( !img_convert_ctx ) if ( !img_convert_ctx )
Panic( "Unable to initialise image scaling context" ); Panic( "Unable to initialise image scaling context" );
} }
sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, c->height, opicture->data, opicture->linesize ); sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, codec_context->height, opicture->data, opicture->linesize );
#else // HAVE_LIBSWSCALE #else // HAVE_LIBSWSCALE
Fatal( "swscale is required for MPEG mode" ); Fatal( "swscale is required for MPEG mode" );
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
@ -586,7 +598,13 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
AVPacket *pkt = packet_buffers[packet_index]; AVPacket *pkt = packet_buffers[packet_index];
av_init_packet( pkt ); av_init_packet( pkt );
int got_packet = 0; int got_packet = 0;
#if LIBAVFORMAT_VERSION_CHECK(57, 0, 0, 0, 0)
if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO &&
codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) {
#else
if ( of->flags & AVFMT_RAWPICTURE ) { if ( of->flags & AVFMT_RAWPICTURE ) {
#endif
#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) #if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2)
pkt->flags |= AV_PKT_FLAG_KEY; pkt->flags |= AV_PKT_FLAG_KEY;
#else #else
@ -597,19 +615,34 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
pkt->size = sizeof (AVPicture); pkt->size = sizeof (AVPicture);
got_packet = 1; got_packet = 1;
} else { } else {
opicture_ptr->pts = c->frame_number; opicture_ptr->pts = codec_context->frame_number;
opicture_ptr->quality = c->global_quality; opicture_ptr->quality = codec_context->global_quality;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
// Put encoder into flushing mode
avcodec_send_frame(codec_context, opicture_ptr);
int ret = avcodec_receive_packet(codec_context, pkt);
if ( ret < 0 ) {
if ( AVERROR_EOF != ret ) {
Error("ERror encoding video (%d) (%s)", ret,
av_err2str(ret));
}
} else {
got_packet = 1;
}
#else
#if LIBAVFORMAT_VERSION_CHECK(54, 1, 0, 2, 100) #if LIBAVFORMAT_VERSION_CHECK(54, 1, 0, 2, 100)
int ret = avcodec_encode_video2( c, pkt, opicture_ptr, &got_packet ); int ret = avcodec_encode_video2( codec_context, pkt, opicture_ptr, &got_packet );
if ( ret != 0 ) { if ( ret != 0 ) {
Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) ); Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) );
} }
#else #else
int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr ); int out_size = avcodec_encode_video( codec_context, video_outbuf, video_outbuf_size, opicture_ptr );
got_packet = out_size > 0 ? 1 : 0; got_packet = out_size > 0 ? 1 : 0;
pkt->data = got_packet ? video_outbuf : NULL; pkt->data = got_packet ? video_outbuf : NULL;
pkt->size = got_packet ? out_size : 0; pkt->size = got_packet ? out_size : 0;
#endif
#endif #endif
if ( got_packet ) { if ( got_packet ) {
// if ( c->coded_frame->key_frame ) // if ( c->coded_frame->key_frame )
@ -622,12 +655,12 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
// } // }
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) { if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) {
pkt->pts = av_rescale_q( pkt->pts, c->time_base, ost->time_base ); pkt->pts = av_rescale_q( pkt->pts, codec_context->time_base, ost->time_base );
} }
if ( pkt->dts != (int64_t)AV_NOPTS_VALUE ) { if ( pkt->dts != (int64_t)AV_NOPTS_VALUE ) {
pkt->dts = av_rescale_q( pkt->dts, c->time_base, ost->time_base ); pkt->dts = av_rescale_q( pkt->dts, codec_context->time_base, ost->time_base );
} }
pkt->duration = av_rescale_q( pkt->duration, c->time_base, ost->time_base ); pkt->duration = av_rescale_q( pkt->duration, codec_context->time_base, ost->time_base );
pkt->stream_index = ost->index; pkt->stream_index = ost->index;
} }
} }
@ -658,8 +691,12 @@ void *VideoStream::StreamingThreadCallback(void *ctx){
VideoStream* videoStream = reinterpret_cast<VideoStream*>(ctx); VideoStream* videoStream = reinterpret_cast<VideoStream*>(ctx);
const uint64_t nanosecond_multiplier = 1000000000; const uint64_t nanosecond_multiplier = 1000000000;
uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->ost->codec->time_base.num) / (videoStream->ost->codec->time_base.den) ); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->codec_context->time_base.num) / (videoStream->codec_context->time_base.den) );
#else
uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->codec_context->time_base.num) / (videoStream->codec_context->time_base.den) );
#endif
uint64_t frame_count = 0; uint64_t frame_count = 0;
timespec start_time; timespec start_time;
clock_gettime(CLOCK_MONOTONIC, &start_time); clock_gettime(CLOCK_MONOTONIC, &start_time);

View File

@ -46,6 +46,7 @@ protected:
AVOutputFormat *of; AVOutputFormat *of;
AVFormatContext *ofc; AVFormatContext *ofc;
AVStream *ost; AVStream *ost;
AVCodecContext *codec_context;
AVCodec *codec; AVCodec *codec;
AVFrame *opicture; AVFrame *opicture;
AVFrame *tmp_opicture; AVFrame *tmp_opicture;

View File

@ -28,23 +28,26 @@ ZMPacket::ZMPacket( ) {
keyframe = 0; keyframe = 0;
image = NULL; image = NULL;
frame = NULL; frame = NULL;
buffer = NULL;
av_init_packet( &packet ); av_init_packet( &packet );
packet.size = 0; packet.size = 0; // So we can detect whether it has been filled.
gettimeofday( &timestamp, NULL ); timestamp = (struct timeval){0};
} }
ZMPacket::ZMPacket( Image *i ) { ZMPacket::ZMPacket( Image *i ) {
keyframe = 1; keyframe = 1;
image = i; image = i;
frame = NULL; frame = NULL;
buffer = NULL;
av_init_packet( &packet ); av_init_packet( &packet );
gettimeofday( &timestamp, NULL ); timestamp = (struct timeval){0};
} }
ZMPacket::ZMPacket( AVPacket *p ) { ZMPacket::ZMPacket( AVPacket *p ) {
av_init_packet( &packet ); av_init_packet( &packet );
set_packet( p ); set_packet( p );
keyframe = p->flags & AV_PKT_FLAG_KEY; keyframe = p->flags & AV_PKT_FLAG_KEY;
buffer = NULL;
} }
ZMPacket::ZMPacket( AVPacket *p, struct timeval *t ) { ZMPacket::ZMPacket( AVPacket *p, struct timeval *t ) {
@ -52,23 +55,40 @@ ZMPacket::ZMPacket( AVPacket *p, struct timeval *t ) {
set_packet( p ); set_packet( p );
timestamp = *t; timestamp = *t;
keyframe = p->flags & AV_PKT_FLAG_KEY; keyframe = p->flags & AV_PKT_FLAG_KEY;
buffer = NULL;
} }
ZMPacket::ZMPacket( AVPacket *p, AVFrame *f, Image *i ) { ZMPacket::ZMPacket( AVPacket *p, AVFrame *f, Image *i ) {
av_init_packet( &packet ); av_init_packet( &packet );
set_packet( p ); set_packet( p );
image = i; image = i;
frame = f; frame = f;
buffer = NULL;
} }
ZMPacket::~ZMPacket() { ZMPacket::~ZMPacket() {
zm_av_packet_unref( &packet ); zm_av_packet_unref( &packet );
if ( frame ) { if ( frame ) {
//av_free(frame->data);
av_frame_free( &frame ); av_frame_free( &frame );
} }
if ( buffer ) {
av_freep( &buffer );
}
// We assume the image was allocated elsewhere, so we just unref it.
image = NULL; image = NULL;
//if ( image ) { }
//delete image;
//} void ZMPacket::reset() {
Debug(2,"reset");
zm_av_packet_unref( &packet );
packet.size = 0;
if ( frame ) {
av_frame_free( &frame );
}
if ( buffer ) {
Debug(2,"freeing buffer");
av_freep( &buffer );
}
} }
int ZMPacket::decode( AVCodecContext *ctx ) { int ZMPacket::decode( AVCodecContext *ctx ) {

View File

@ -36,7 +36,8 @@ class ZMPacket {
int keyframe; int keyframe;
AVPacket packet; // Input packet, undecoded AVPacket packet; // Input packet, undecoded
AVFrame *frame; // Input image, decoded Theoretically only filled if needed. AVFrame *frame; // Input image, decoded Theoretically only filled if needed.
Image *image; // Our internal image oject representing this frame uint8_t *buffer;
Image *image; // Our internal image object representing this frame
struct timeval timestamp; struct timeval timestamp;
public: public:
AVPacket *av_packet() { return &packet; } AVPacket *av_packet() { return &packet; }
@ -47,6 +48,7 @@ class ZMPacket {
int is_keyframe() { return keyframe; }; int is_keyframe() { return keyframe; };
int decode( AVCodecContext *ctx ); int decode( AVCodecContext *ctx );
void reset();
ZMPacket( AVPacket *packet, struct timeval *timestamp ); ZMPacket( AVPacket *packet, struct timeval *timestamp );
ZMPacket( AVPacket *packet ); ZMPacket( AVPacket *packet );
ZMPacket( AVPacket *packet, AVFrame *frame, Image *image ); ZMPacket( AVPacket *packet, AVFrame *frame, Image *image );

View File

@ -68,6 +68,7 @@ RemoteCameraNVSocket::RemoteCameraNVSocket(
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 0; timeout.tv_usec = 0;
subpixelorder = ZM_SUBPIX_ORDER_BGR; subpixelorder = ZM_SUBPIX_ORDER_BGR;
video_stream = NULL;
if ( capture ) { if ( capture ) {
Initialise(); Initialise();
@ -212,3 +213,26 @@ int RemoteCameraNVSocket::Capture( ZMPacket &zm_packet ) {
int RemoteCameraNVSocket::PostCapture() { int RemoteCameraNVSocket::PostCapture() {
return( 0 ); return( 0 );
} }
AVStream *RemoteCameraNVSocket::get_VideoStream() {
if ( ! video_stream ) {
AVFormatContext *oc = avformat_alloc_context();
video_stream = avformat_new_stream( oc, NULL );
if ( video_stream ) {
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
video_stream->codecpar->width = width;
video_stream->codecpar->height = height;
video_stream->codecpar->format = GetFFMPEGPixelFormat(colours,subpixelorder);
#else
video_stream->codec->width = width;
video_stream->codec->height = height;
video_stream->codec->pix_fmt = GetFFMPEGPixelFormat(colours,subpixelorder);
#endif
} else {
Error("Can't create video stream");
}
} else {
Debug(2,"Have videostream");
}
Debug(2,"Get videoStream");
return video_stream;
}

View File

@ -69,6 +69,7 @@ bool p_record_audio );
int PrimeCapture(); int PrimeCapture();
int Capture( ZMPacket &p ); int Capture( ZMPacket &p );
int PostCapture(); int PostCapture();
AVStream* get_VideoStream();
}; };
#endif // ZM_REMOTE_CAMERA_NVSOCKET_H #endif // ZM_REMOTE_CAMERA_NVSOCKET_H

View File

@ -287,13 +287,13 @@ int RemoteCameraRtsp::Capture( ZMPacket &zm_packet ) {
directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder); directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder);
if ( directbuffer == NULL ) { if ( directbuffer == NULL ) {
Error("Failed requesting writeable buffer for the captured image."); Error("Failed requesting writeable buffer for the captured image.");
return -1; return NULL;
} }
while ( !frameComplete ) { while ( !frameComplete ) {
buffer.clear(); buffer.clear();
if ( !rtspThread->isRunning() ) if ( !rtspThread->isRunning() )
return -1; return NULL;
if ( rtspThread->getFrame( buffer ) ) { if ( rtspThread->getFrame( buffer ) ) {
Debug( 3, "Read frame %d bytes", buffer.size() ); Debug( 3, "Read frame %d bytes", buffer.size() );
@ -301,7 +301,7 @@ int RemoteCameraRtsp::Capture( ZMPacket &zm_packet ) {
Hexdump( 4, buffer.head(), 16 ); Hexdump( 4, buffer.head(), 16 );
if ( !buffer.size() ) if ( !buffer.size() )
return -1; return NULL;
if ( mCodecContext->codec_id == AV_CODEC_ID_H264 ) { if ( mCodecContext->codec_id == AV_CODEC_ID_H264 ) {
// SPS and PPS frames should be saved and appended to IDR frames // SPS and PPS frames should be saved and appended to IDR frames

View File

@ -379,21 +379,31 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
stream->id = i; stream->id = i;
#endif #endif
AVCodecContext *codec_context = NULL;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
codec_context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_context, stream->codecpar);
#else
codec_context = stream->codec;
#endif
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mediaDesc->getType() == "video" ) if ( mediaDesc->getType() == "video" )
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" ) else if ( mediaDesc->getType() == "audio" )
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO; codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" ) else if ( mediaDesc->getType() == "application" )
stream->codec->codec_type = AVMEDIA_TYPE_DATA; codec_context->codec_type = AVMEDIA_TYPE_DATA;
#else #else
if ( mediaDesc->getType() == "video" ) if ( mediaDesc->getType() == "video" )
stream->codec->codec_type = CODEC_TYPE_VIDEO; codec_context->codec_type = CODEC_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" ) else if ( mediaDesc->getType() == "audio" )
stream->codec->codec_type = CODEC_TYPE_AUDIO; codec_context->codec_type = CODEC_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" ) else if ( mediaDesc->getType() == "application" )
stream->codec->codec_type = CODEC_TYPE_DATA; codec_context->codec_type = CODEC_TYPE_DATA;
#endif #endif
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
@ -410,31 +420,27 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName ); codec_name = std::string( smStaticPayloads[i].payloadName );
#else #else
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; strncpy( codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
#endif #endif
stream->codec->codec_type = smStaticPayloads[i].codecType; codec_context->codec_type = smStaticPayloads[i].codecType;
stream->codec->codec_id = smStaticPayloads[i].codecId; codec_context->codec_id = smStaticPayloads[i].codecId;
stream->codec->sample_rate = smStaticPayloads[i].clockRate; codec_context->sample_rate = smStaticPayloads[i].clockRate;
break; break;
} }
} }
} } else {
else
{
// Look in dynamic table // Look in dynamic table
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) {
{ if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) {
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
{
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName ); Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName ); codec_name = std::string( smStaticPayloads[i].payloadName );
#else #else
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; strncpy( codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
#endif #endif
stream->codec->codec_type = smDynamicPayloads[i].codecType; codec_context->codec_type = smDynamicPayloads[i].codecType;
stream->codec->codec_id = smDynamicPayloads[i].codecId; codec_context->codec_id = smDynamicPayloads[i].codecId;
stream->codec->sample_rate = mediaDesc->getClock(); codec_context->sample_rate = mediaDesc->getClock();
break; break;
} }
} }
@ -450,14 +456,13 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
//return( 0 ); //return( 0 );
} }
if ( mediaDesc->getWidth() ) if ( mediaDesc->getWidth() )
stream->codec->width = mediaDesc->getWidth(); codec_context->width = mediaDesc->getWidth();
if ( mediaDesc->getHeight() ) if ( mediaDesc->getHeight() )
stream->codec->height = mediaDesc->getHeight(); codec_context->height = mediaDesc->getHeight();
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size()) if ( codec_context->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size()) {
{
uint8_t start_sequence[]= { 0, 0, 1 }; uint8_t start_sequence[]= { 0, 0, 1 };
stream->codec->extradata_size= 0; codec_context->extradata_size= 0;
stream->codec->extradata= NULL; codec_context->extradata= NULL;
char pvalue[1024], *value = pvalue; char pvalue[1024], *value = pvalue;
strcpy(pvalue, mediaDesc->getSprops().c_str()); strcpy(pvalue, mediaDesc->getSprops().c_str());
@ -482,22 +487,33 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
if (packet_size) { if (packet_size) {
uint8_t *dest = uint8_t *dest =
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) + (uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
stream->codec->extradata_size + codec_context->extradata_size +
FF_INPUT_BUFFER_PADDING_SIZE); #if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0)
AV_INPUT_BUFFER_PADDING_SIZE
#else
FF_INPUT_BUFFER_PADDING_SIZE
#endif
);
if(dest) { if(dest) {
if(stream->codec->extradata_size) { if(codec_context->extradata_size) {
// av_realloc? // av_realloc?
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size); memcpy(dest, codec_context->extradata, codec_context->extradata_size);
av_free(stream->codec->extradata); av_free(codec_context->extradata);
} }
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence)); memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+ memset(dest+codec_context->extradata_size+sizeof(start_sequence)+
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); packet_size, 0,
#if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0)
AV_INPUT_BUFFER_PADDING_SIZE
#else
FF_INPUT_BUFFER_PADDING_SIZE
#endif
);
stream->codec->extradata= dest; codec_context->extradata= dest;
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size; codec_context->extradata_size+= sizeof(start_sequence)+packet_size;
// } else { // } else {
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); // av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
// return AVERROR(ENOMEM); // return AVERROR(ENOMEM);

View File

@ -31,13 +31,11 @@
#include <string> #include <string>
#include <vector> #include <vector>
class SessionDescriptor class SessionDescriptor {
{
protected: protected:
enum { PAYLOAD_TYPE_DYNAMIC=96 }; enum { PAYLOAD_TYPE_DYNAMIC=96 };
struct StaticPayloadDesc struct StaticPayloadDesc {
{
int payloadType; int payloadType;
const char payloadName[6]; const char payloadName[6];
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
@ -50,8 +48,7 @@ protected:
int autoChannels; int autoChannels;
}; };
struct DynamicPayloadDesc struct DynamicPayloadDesc {
{
const char payloadName[32]; const char payloadName[32];
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
AVMediaType codecType; AVMediaType codecType;
@ -65,8 +62,7 @@ protected:
}; };
public: public:
class ConnInfo class ConnInfo {
{
protected: protected:
std::string mNetworkType; std::string mNetworkType;
std::string mAddressType; std::string mAddressType;
@ -78,8 +74,7 @@ public:
ConnInfo( const std::string &connInfo ); ConnInfo( const std::string &connInfo );
}; };
class BandInfo class BandInfo {
{
protected: protected:
std::string mType; std::string mType;
int mValue; int mValue;
@ -88,8 +83,7 @@ public:
BandInfo( const std::string &bandInfo ); BandInfo( const std::string &bandInfo );
}; };
class MediaDescriptor class MediaDescriptor {
{
protected: protected:
std::string mType; std::string mType;
int mPort; int mPort;

View File

@ -78,7 +78,17 @@ int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf,
return 0; return 0;
} }
int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
int SWScale::Convert(
const uint8_t* in_buffer,
const size_t in_buffer_size,
uint8_t* out_buffer,
const size_t out_buffer_size,
enum _AVPIXELFORMAT in_pf,
enum _AVPIXELFORMAT out_pf,
unsigned int width,
unsigned int height
) {
/* Parameter checking */ /* Parameter checking */
if(in_buffer == NULL || out_buffer == NULL) { if(in_buffer == NULL || out_buffer == NULL) {
Error("NULL Input or output buffer"); Error("NULL Input or output buffer");
@ -119,14 +129,14 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
size_t outsize = avpicture_get_size(out_pf, width, height); size_t outsize = avpicture_get_size(out_pf, width, height);
#endif #endif
if(outsize < out_buffer_size) { if ( outsize < out_buffer_size ) {
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
return -5; return -5;
} }
/* Get the context */ /* Get the context */
swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL ); swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL );
if(swscale_ctx == NULL) { if ( swscale_ctx == NULL ) {
Error("Failed getting swscale context"); Error("Failed getting swscale context");
return -6; return -6;
} }
@ -163,22 +173,22 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
} }
int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
if(img->Width() != width) { if ( img->Width() != width ) {
Error("Source image width differs. Source: %d Output: %d",img->Width(), width); Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
return -12; return -12;
} }
if(img->Height() != height) { if ( img->Height() != height ) {
Error("Source image height differs. Source: %d Output: %d",img->Height(), height); Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
return -13; return -13;
} }
return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); return Convert(img->Buffer(), img->Size(), out_buffer, out_buffer_size, in_pf, out_pf, width, height);
} }
int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) { int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) {
if(!gotdefaults) { if ( !gotdefaults ) {
Error("Defaults are not set"); Error("Defaults are not set");
return -24; return -24;
} }
@ -188,7 +198,7 @@ int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t
int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) { int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) {
if(!gotdefaults) { if ( !gotdefaults ) {
Error("Defaults are not set"); Error("Defaults are not set");
return -24; return -24;
} }

View File

@ -100,8 +100,17 @@ bool startsWith( const std::string &haystack, const std::string &needle )
return( haystack.substr( 0, needle.length() ) == needle ); return( haystack.substr( 0, needle.length() ) == needle );
} }
StringVector split( const std::string &string, const std::string &chars, int limit ) std::vector<std::string> split(const std::string &s, char delim) {
{ std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(trimSpaces(item));
}
return elems;
}
StringVector split( const std::string &string, const std::string &chars, int limit ) {
StringVector stringVector; StringVector stringVector;
std::string tempString = string; std::string tempString = string;
std::string::size_type startIndex = 0; std::string::size_type startIndex = 0;

View File

@ -86,9 +86,12 @@ VideoStore::VideoStore(
video_in_stream_index = video_in_stream->index; video_in_stream_index = video_in_stream->index;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
video_in_ctx = avcodec_alloc_context3(NULL); video_in_ctx = avcodec_alloc_context3(NULL);
Debug(2,"About to copy aparames");
avcodec_parameters_to_context(video_in_ctx, avcodec_parameters_to_context(video_in_ctx,
video_in_stream->codecpar); video_in_stream->codecpar);
zm_dump_codecpar( video_in_stream->codecpar ); zm_dump_codecpar( video_in_stream->codecpar );
Debug(2,"About to copy aparames");
//video_in_ctx.codec_id = video_in_stream->codecpar.codec_id;
#else #else
video_in_ctx = video_in_stream->codec; video_in_ctx = video_in_stream->codec;
#endif #endif
@ -99,6 +102,7 @@ VideoStore::VideoStore(
} }
video_out_ctx = NULL; video_out_ctx = NULL;
video_out_ctx = avcodec_alloc_context3(NULL);
// Copy params from instream to ctx // Copy params from instream to ctx
if ( video_in_stream && ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) ) { if ( video_in_stream && ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) ) {
@ -113,13 +117,10 @@ VideoStore::VideoStore(
zm_dump_codec(video_out_ctx); zm_dump_codec(video_out_ctx);
} }
#else #else
video_out_ctx = avcodec_alloc_context3(NULL);
avcodec_copy_context( video_out_ctx, video_in_ctx ); avcodec_copy_context( video_out_ctx, video_in_ctx );
#endif #endif
// Same codec, just copy the packets, otherwise we have to decode/encode // Same codec, just copy the packets, otherwise we have to decode/encode
video_out_codec = (AVCodec *)video_in_ctx->codec; video_out_codec = (AVCodec *)video_in_ctx->codec;
video_out_ctx->time_base = video_in_ctx->time_base;
video_out_stream->time_base = video_in_stream->time_base;
} else { } else {
/** Create a new frame to store the */ /** Create a new frame to store the */
@ -127,84 +128,95 @@ VideoStore::VideoStore(
Error("Could not allocate in frame"); Error("Could not allocate in frame");
return; return;
} }
video_out_codec = avcodec_find_encoder_by_name("h264_omx"); video_out_ctx = avcodec_alloc_context3( video_out_codec );
if ( ! video_out_codec ) { // Don't have an input stream, so need to tell it what we are sending it, or are transcoding
Debug(1, "Didn't find omx"); video_out_ctx->width = monitor->Width();
video_out_codec = avcodec_find_encoder(AV_CODEC_ID_H264); video_out_ctx->height = monitor->Height();
} video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
if ( !video_out_codec ) {
Fatal("Could not find codec for H264");
}
Debug(2, "Have video out codec");
video_out_ctx = avcodec_alloc_context3( video_out_codec ); if ( monitor->OutputCodec() == "mjpeg" ) {
// Don't have an input stream, so need to tell it what we are sending it, or are transcoding video_out_codec = avcodec_find_encoder_by_name("mjpeg");
video_out_ctx->width = monitor->Width();
video_out_ctx->height = monitor->Height();
video_out_ctx->codec_id = AV_CODEC_ID_H264;
//video_out_ctx->sample_aspect_ratio = (AVRational){4,3};
video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
//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_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
/* video time_base can be set to whatever is handy and supported by encoder */
video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate
video_out_ctx->framerate = (AVRational){0,1}; // Unknown framerate
video_out_ctx->gop_size = 12;
video_out_ctx->bit_rate = 4000000;
video_out_ctx->qmin = 10;
video_out_ctx->qmax = 51;
video_out_ctx->qcompress = 0.6;
if (oc->oformat->flags & AVFMT_GLOBALHEADER) {
#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0)
video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
#else
video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
#endif
}
AVDictionary *opts = 0;
std::string Options = monitor->GetEncoderOptions();
ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
if ( ret < 0 ) {
Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str());
} else {
AVDictionaryEntry *e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Debug( 3, "Encoder Option %s=%s", e->key, e->value );
}
}
if ( ! av_dict_get( opts, "preset", NULL, 0 ) )
av_dict_set( &opts, "preset", "superfast", 0 );
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) {
Warning("Can't open video codec (%s)! %s, trying h264",
video_out_codec->name,
av_make_error_string(ret).c_str()
);
video_out_codec = avcodec_find_encoder_by_name("h264");
if ( ! video_out_codec ) { if ( ! video_out_codec ) {
Error("Can't find h264 encoder"); Debug(1, "Didn't find omx");
video_out_codec = avcodec_find_encoder_by_name("libx264"); video_out_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
}
video_out_ctx->codec_id = video_out_codec->id;
video_out_ctx->pix_fmt = AV_PIX_FMT_YUVJ422P;
} else if ( monitor->OutputCodec() == "h264" ) {
video_out_codec = avcodec_find_encoder_by_name("h264_omx");
if ( ! video_out_codec ) {
Debug(1, "Didn't find omx");
video_out_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
}
if ( !video_out_codec ) {
Fatal("Could not find codec for H264");
}
Debug(2, "Have video out codec");
video_out_ctx->codec_id = AV_CODEC_ID_H264;
//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_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
/* video time_base can be set to whatever is handy and supported by encoder */
video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate
video_out_ctx->framerate = (AVRational){0,1}; // Unknown framerate
video_out_ctx->gop_size = 12;
video_out_ctx->bit_rate = 4000000;
video_out_ctx->qmin = 10;
video_out_ctx->qmax = 51;
video_out_ctx->qcompress = 0.6;
AVDictionary *opts = 0;
std::string Options = monitor->GetEncoderOptions();
ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
if ( ret < 0 ) {
Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str());
} else {
AVDictionaryEntry *e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Debug( 3, "Encoder Option %s=%s", e->key, e->value );
}
}
if ( ! av_dict_get( opts, "preset", NULL, 0 ) ) {
Debug(2,"Setting preset to ultrafast");
av_dict_set( &opts, "preset", "ultrafast", 0 );
}
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) {
Warning("Can't open video codec (%s)! %s, trying h264",
video_out_codec->name,
av_make_error_string(ret).c_str()
);
video_out_codec = avcodec_find_encoder_by_name("h264");
if ( ! video_out_codec ) { if ( ! video_out_codec ) {
Error("Can't find libx264 encoder"); Error("Can't find h264 encoder");
video_out_codec = avcodec_find_encoder_by_name("libx264");
if ( ! video_out_codec ) {
Error("Can't find libx264 encoder");
return;
}
}
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) {
Error("Can't open video codec (%s)! %s",
video_out_codec->name,
av_make_error_string(ret).c_str() );
return; return;
} }
} }
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { AVDictionaryEntry *e = NULL;
Error("Can't open video codec (%s)! %s", while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
video_out_codec->name, Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key);
av_make_error_string(ret).c_str() );
return;
} }
} av_dict_free(&opts);
AVDictionaryEntry *e = NULL; if ( !video_out_ctx->codec_tag ) {
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { video_out_ctx->codec_tag =
Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key); av_codec_get_tag(oc->oformat->codec_tag, AV_CODEC_ID_H264 );
} Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
av_dict_free(&opts); }
}// end if codec == h264
swscale.SetDefaults( swscale.SetDefaults(
video_in_ctx->pix_fmt, video_in_ctx->pix_fmt,
@ -214,11 +226,6 @@ VideoStore::VideoStore(
); );
} // end if copying or trasncoding } // end if copying or trasncoding
if ( !video_out_ctx->codec_tag ) {
video_out_ctx->codec_tag =
av_codec_get_tag(oc->oformat->codec_tag, AV_CODEC_ID_H264 );
Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
}
video_out_stream = avformat_new_stream(oc, video_out_codec); video_out_stream = avformat_new_stream(oc, video_out_codec);
if ( !video_out_stream ) { if ( !video_out_stream ) {
@ -434,6 +441,7 @@ void VideoStore::write_audio_packet( AVPacket &pkt ) {
VideoStore::~VideoStore() { VideoStore::~VideoStore() {
if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) { if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) {
Debug(2,"Different codecs between in and out");
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & AV_CODEC_CAP_DELAY ) ) { if ( video_out_ctx->codec && ( video_out_ctx->codec->capabilities & AV_CODEC_CAP_DELAY ) ) {
@ -475,7 +483,8 @@ VideoStore::~VideoStore() {
break; break;
} }
#endif #endif
Debug(3, "dts:%d, pts:%d", pkt.dts, pkt.pts ); int keyframe = pkt.flags & AV_PKT_FLAG_KEY;
Debug(3, "dts:%d, pts:%d, keyframe:%d", pkt.dts, pkt.pts, keyframe );
//pkt.dts = video_next_dts; //pkt.dts = video_next_dts;
pkt.pts = pkt.dts; pkt.pts = pkt.dts;
//pkt.duration = video_last_duration; //pkt.duration = video_last_duration;
@ -830,7 +839,7 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
return 0; return 0;
} }
#else #else
int data_present; int data_present;
if ((ret = avcodec_decode_video2(video_in_ctx, in_frame, if ((ret = avcodec_decode_video2(video_in_ctx, in_frame,
&data_present, ipkt )) < 0) { &data_present, ipkt )) < 0) {
Error("Could not decode frame (error '%s')\n", Error("Could not decode frame (error '%s')\n",
@ -857,11 +866,11 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
video_out_ctx->pix_fmt, video_out_ctx->pix_fmt,
video_out_ctx->width, video_out_ctx->width,
video_out_ctx->height, 1); video_out_ctx->height, 1);
uint8_t *buffer = (uint8_t *)av_malloc(codec_imgsize); zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize);
av_image_fill_arrays( av_image_fill_arrays(
frame->data, frame->data,
frame->linesize, frame->linesize,
buffer, zm_packet->buffer,
video_out_ctx->pix_fmt, video_out_ctx->pix_fmt,
video_out_ctx->width, video_out_ctx->width,
video_out_ctx->height, video_out_ctx->height,
@ -871,10 +880,10 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
video_out_ctx->pix_fmt, video_out_ctx->pix_fmt,
video_out_ctx->width, video_out_ctx->width,
video_out_ctx->height); video_out_ctx->height);
uint8_t *buffer = (uint8_t *)av_malloc(codec_imgsize); zm_packet->buffer = (uint8_t *)av_malloc(codec_imgsize);
avpicture_fill( avpicture_fill(
(AVPicture *)frame, (AVPicture *)frame,
buffer, zm_packet->buffer,
video_out_ctx->pix_fmt, video_out_ctx->pix_fmt,
video_out_ctx->width, video_out_ctx->width,
video_out_ctx->height video_out_ctx->height
@ -885,7 +894,7 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
frame->height = video_out_ctx->height; frame->height = video_out_ctx->height;
frame->format = video_out_ctx->pix_fmt; frame->format = video_out_ctx->pix_fmt;
swscale.Convert(zm_packet->image, swscale.Convert(zm_packet->image,
buffer, zm_packet->buffer,
codec_imgsize, codec_imgsize,
(AVPixelFormat)zm_packet->image->AVPixFormat(), (AVPixelFormat)zm_packet->image->AVPixFormat(),
video_out_ctx->pix_fmt, video_out_ctx->pix_fmt,
@ -907,19 +916,20 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->frame)) < 0 ) { if ( (ret = avcodec_send_frame(video_out_ctx, zm_packet->frame)) < 0 ) {
Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str()); Error("Could not send frame (error '%s')", av_make_error_string(ret).c_str());
zm_av_packet_unref(&opkt); // NOT SURE THIS IS NECCESSARY zm_av_packet_unref(&opkt); // NOT SURE THIS IS NECCESSARY
return 0; return -1;
} }
if ( (ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0 ) { if ( (ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0 ) {
zm_av_packet_unref(&opkt);
if ( AVERROR(EAGAIN) == ret ) { if ( AVERROR(EAGAIN) == ret ) {
// THe codec may need more samples than it has, perfectly valid // THe codec may need more samples than it has, perfectly valid
Debug(3, "Could not recieve packet (error '%s')", Debug(3, "Could not recieve packet (error '%s')",
av_make_error_string(ret).c_str()); av_make_error_string(ret).c_str());
return 0;
} else { } else {
Error("Could not recieve packet (error %d = '%s')", ret, Error("Could not recieve packet (error %d = '%s')", ret,
av_make_error_string(ret).c_str()); av_make_error_string(ret).c_str());
} }
zm_av_packet_unref(&opkt); return -1;
return 0;
} }
#else #else
if ( (ret = avcodec_encode_video2( if ( (ret = avcodec_encode_video2(
@ -945,34 +955,8 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
opkt.flags = ipkt->flags; opkt.flags = ipkt->flags;
} }
//opkt.dts = opkt.pts = ( zm_packet->timestamp.tv_sec*1000000 + zm_packet->timestamp.tv_usec ) - video_last_pts; int keyframe = opkt.flags & AV_PKT_FLAG_KEY;
#if 0 Debug(3, "dts:%d, pts:%d, keyframe:%d", opkt.dts, opkt.pts, keyframe );
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;
}
}
// our timebase is always /1000000 now, so we can use the timestamp as the pts/dts
video_last_pts = zm_packet->timestamp.tv_sec*1000000 + zm_packet->timestamp.tv_usec;
video_last_dts = video_last_pts;
video_last_duration = duration;
opkt.duration = duration;
#endif
write_video_packet( opkt ); write_video_packet( opkt );
zm_av_packet_unref(&opkt); zm_av_packet_unref(&opkt);
@ -981,7 +965,7 @@ int VideoStore::writeVideoFramePacket( ZMPacket * zm_packet ) {
void VideoStore::write_video_packet( AVPacket &opkt ) { void VideoStore::write_video_packet( AVPacket &opkt ) {
if (opkt.dts > opkt.pts) { if ( opkt.dts > opkt.pts ) {
Debug(1, Debug(1,
"opkt.dts(%d) must be <= opkt.pts(%d). Decompression must happen " "opkt.dts(%d) must be <= opkt.pts(%d). Decompression must happen "
"before presentation.", "before presentation.",
@ -1012,8 +996,7 @@ void VideoStore::write_video_packet( AVPacket &opkt ) {
//dumpPacket(&opkt); //dumpPacket(&opkt);
} else { } else {
ret = av_interleaved_write_frame(oc, &opkt); if ( (ret = av_interleaved_write_frame(oc, &opkt)) < 0 ) {
if (ret < 0) {
// There's nothing we can really do if the frame is rejected, just drop it // There's nothing we can really do if the frame is rejected, just drop it
// and get on with the next // and get on with the next
Warning( Warning(

View File

@ -70,6 +70,7 @@ possible, this should run at more or less constant speed.
#include "zm_time.h" #include "zm_time.h"
#include "zm_signal.h" #include "zm_signal.h"
#include "zm_monitor.h" #include "zm_monitor.h"
#include "zm_analysis_thread.h"
void Usage() { void Usage() {
fprintf(stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n"); fprintf(stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n");
@ -244,6 +245,7 @@ int main(int argc, char *argv[]) {
exit(-1); exit(-1);
} }
AnalysisThread **analysis_threads = new AnalysisThread *[n_monitors];
long *capture_delays = new long[n_monitors]; long *capture_delays = new long[n_monitors];
long *alarm_capture_delays = new long[n_monitors]; long *alarm_capture_delays = new long[n_monitors];
long *next_delays = new long[n_monitors]; long *next_delays = new long[n_monitors];
@ -252,12 +254,22 @@ int main(int argc, char *argv[]) {
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
capture_delays[i] = monitors[i]->GetCaptureDelay(); capture_delays[i] = monitors[i]->GetCaptureDelay();
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
}
Monitor::Function function = monitors[0]->GetFunction();
if ( function == Monitor::MODECT || function == Monitor::MOCORD || function == Monitor::RECORD) {
Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id());
analysis_threads[i] = new AnalysisThread(monitors[i]);
analysis_threads[i]->start();
} else {
analysis_threads[i] = NULL;
}
} // end foreach monitor
int result = 0; int result = 0;
struct timeval now; struct timeval now;
struct DeltaTimeval delta_time; struct DeltaTimeval delta_time;
while ( !zm_terminate ) { while ( !zm_terminate ) {
//Debug(2,"blocking");
sigprocmask(SIG_BLOCK, &block_set, 0); sigprocmask(SIG_BLOCK, &block_set, 0);
for ( int i = 0; i < n_monitors; i++ ) { for ( int i = 0; i < n_monitors; i++ ) {
long min_delay = MAXINT; long min_delay = MAXINT;
@ -305,18 +317,29 @@ int main(int argc, char *argv[]) {
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3); DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3);
long sleep_time = next_delays[i]-delta_time.delta; long sleep_time = next_delays[i]-delta_time.delta;
if ( sleep_time > 0 ) { if ( sleep_time > 0 ) {
Debug(2,"usleeping (%d)", sleep_time*(DT_MAXGRAN/DT_PREC_3) );
usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3)); usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3));
} }
last_capture_times[i] = now;
} else {
gettimeofday(&(last_capture_times[i]), NULL);
} }
gettimeofday(&(last_capture_times[i]), NULL);
} // end if next_delay <= min_delay || next_delays[i] <= 0 ) } // end if next_delay <= min_delay || next_delays[i] <= 0 )
} // end foreach n_monitors } // end foreach n_monitors
//Debug(2,"unblocking");
sigprocmask(SIG_UNBLOCK, &block_set, 0); sigprocmask(SIG_UNBLOCK, &block_set, 0);
} // end while ! zm_terminate } // end while ! zm_terminate
for ( int i = 0; i < n_monitors; i++ ) { for ( int i = 0; i < n_monitors; i++ ) {
if ( analysis_threads[i] ) {
analysis_threads[i]->stop();
analysis_threads[i]->join();
delete analysis_threads[i];
analysis_threads[i] = 0;
}
delete monitors[i]; delete monitors[i];
} }
delete [] analysis_threads;
delete [] monitors; delete [] monitors;
delete [] alarm_capture_delays; delete [] alarm_capture_delays;
delete [] capture_delays; delete [] capture_delays;

View File

@ -1 +1 @@
1.31.12 1.31.13

View File

@ -360,7 +360,8 @@ function getNearEvents() {
$eventId = $_REQUEST['id']; $eventId = $_REQUEST['id'];
$event = dbFetchOne( 'SELECT * FROM Events WHERE Id=?', NULL, array( $eventId ) ); $event = dbFetchOne( 'SELECT * FROM Events WHERE Id=?', NULL, array( $eventId ) );
parseFilter( $_REQUEST['filter'] ); if ( isset($_REQUEST['filter']) )
parseFilter( $_REQUEST['filter'] );
parseSort(); parseSort();
if ( $user['MonitorIds'] ) if ( $user['MonitorIds'] )

View File

@ -9,6 +9,7 @@ class Event {
'StorageId', 'StorageId',
'Name', 'Name',
'DiskSpace', 'DiskSpace',
'SaveJPEGs',
); );
public function __construct( $IdOrRow = null ) { public function __construct( $IdOrRow = null ) {
$row = NULL; $row = NULL;

View File

@ -15,6 +15,8 @@ private $defaults = array(
'Height' => null, 'Height' => null,
'Orientation' => null, 'Orientation' => null,
'AnalysisFPSLimit' => null, 'AnalysisFPSLimit' => null,
'OutputCodec',
'OutputContainer',
); );
private $control_fields = array( private $control_fields = array(
'Name' => '', 'Name' => '',

View File

@ -326,7 +326,7 @@ function changeGroup( e, depth ) {
} }
function changeMonitor( e ) { function changeMonitor( e ) {
var monitor_id = e.value; var monitor_id = e.value;
Cookie.write( 'zmMonitorId', monitor_id, { duration: 10*365 } ); Cookie.write( 'MonitorId', monitor_id, { duration: 10*365 } );
window.location = window.location; window.location = window.location;
} }
function changeFilter( e ) { function changeFilter( e ) {

View File

@ -97,12 +97,17 @@ $groupSql = Group::get_group_sql( $group_id );
$monitors_dropdown[$monitors[$i]['Id']] = $monitors[$i]['Name']; $monitors_dropdown[$monitors[$i]['Id']] = $monitors[$i]['Name'];
if ( $monitors[$i]['Id'] == $monitor_id ) { if ( $monitors[$i]['Id'] == $monitor_id ) {
$found_selected_monitor = true; $found_selected_monitor = true;
} else {
Warning("didn't find monitor $monitor_id " . $monitors[$i]['Id'] );
} }
} } // end foreach monitor
if ( ! $found_selected_monitor ) { if ( ! $found_selected_monitor ) {
$monitor_id = ''; $monitor_id = '';
} }
} } else {
Warning("Monitor id not specified");
} // end if a monitor was specified
for ( $i = 0; $i < count($monitors); $i++ ) { for ( $i = 0; $i < count($monitors); $i++ ) {
if ( !visibleMonitor( $monitors[$i]['Id'] ) ) { if ( !visibleMonitor( $monitors[$i]['Id'] ) ) {
continue; continue;
@ -114,7 +119,7 @@ $groupSql = Group::get_group_sql( $group_id );
} }
$displayMonitors[] = $monitors[$i]; $displayMonitors[] = $monitors[$i];
} }
echo htmlSelect( 'MonitorId', $monitors_dropdown, $monitor_id, array('onchange'=>'changeMonitor(this);') ); echo htmlSelect( 'MonitorId', $monitors_dropdown, $monitor_id, array('onchange'=>'changeFilter(this);') );
?> ?>
</span> </span>
<?php <?php

View File

@ -0,0 +1,14 @@
<input type="hidden" name="newMonitor[Method]" value="<?php echo validHtmlStr($monitor->Method()) ?>"/>
<tr><td><?php echo translate('HostName') ?></td><td><input type="text" name="newMonitor[Host]" value="<?php echo validHtmlStr($monitor->Host()) ?>" size="36"/></td></tr>
<tr><td><?php echo translate('Port') ?></td><td><input type="number" name="newMonitor[Port]" value="<?php echo validHtmlStr($monitor->Port()) ?>" size="6"/></td></tr>
<tr><td><?php echo translate('Path') ?></td><td><input type="text" name="newMonitor[Path]" value="<?php echo validHtmlStr($monitor->Path()) ?>" size="36"/></td></tr>
<input type="hidden" name="newMonitor[User]" value="<?php echo validHtmlStr($monitor->User()) ?>"/>
<input type="hidden" name="newMonitor[Pass]" value="<?php echo validHtmlStr($monitor->Pass()) ?>"/>
<input type="hidden" name="newMonitor[Options]" value="<?php echo validHtmlStr($monitor->Options()) ?>"/>
<tr><td><?php echo translate('TargetColorspace') ?></td><td><select name="newMonitor[Colours]"><?php foreach ( $Colours as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->Colours()) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="number" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="number" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
<tr><td><?php echo translate('Orientation') ?></td><td><?php echo htmlselect( 'newMonitor[Orientation]', $orientations, $monitor->Orientation() );?></td></tr>
<input type="hidden" name="newMonitor[Deinterlacing]" value="<?php echo validHtmlStr($monitor->Deinterlacing()) ?>"/>
<input type="hidden" name="newMonitor[RTSPDescribe]" value="<?php echo validHtmlStr($monitor->RTSPDescribe()) ?>"/>

View File

@ -74,6 +74,10 @@ if ( ( ! $replayMode ) or ( ! $replayModes[$replayMode] ) ) {
$replayMode = 'none'; $replayMode = 'none';
} }
$video_tag = false;
if ( $Event->DefaultVideo() and ( 'mp4' == pathinfo($Event->DefaultVideo(), PATHINFO_EXTENSION) ) ) {
$video_tag = true;
}
// videojs zoomrotate only when direct recording // videojs zoomrotate only when direct recording
$Zoom = 1; $Zoom = 1;
$Rotation = 0; $Rotation = 0;
@ -150,7 +154,7 @@ if ( $Event->SaveJPEGs() & 3 ) { // Analysis or Jpegs
<div id="exportEvent"><a href="#" onclick="exportEvent();"><?php echo translate('Export') ?></a></div> <div id="exportEvent"><a href="#" onclick="exportEvent();"><?php echo translate('Export') ?></a></div>
</div> </div>
<?php <?php
if ( $Event->DefaultVideo() ) { if ( $video_tag ) {
?> ?>
<div id="eventVideo" class=""> <div id="eventVideo" class="">
<div id="videoFeed"> <div id="videoFeed">
@ -170,7 +174,7 @@ if ( $Event->DefaultVideo() ) {
<?php <?php
} // end if DefaultVideo } // end if DefaultVideo
?> ?>
<?php if (!$Event->DefaultVideo()) { ?> <?php if ( !$video_tag ) { ?>
<div id="imageFeed"> <div id="imageFeed">
<?php <?php
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {

View File

@ -471,6 +471,18 @@ $videowriteropts = array(
'X264 Encode' => 1, 'X264 Encode' => 1,
'H264 Camera Passthrough' => 2 'H264 Camera Passthrough' => 2
); );
$videowriter_codecs = array(
'' => translate('Disabled'),
'h264' => 'h264',
'mjpeg' => 'mjpeg',
'mpeg1' => 'mpeg1',
'mpeg2' => 'mpeg2',
);
$videowriter_containers = array(
'' => translate('Auto'),
'mp4' => 'mp4',
'mkv' => 'mkv',
);
xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor->Name()) ); xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor->Name()) );
?> ?>
@ -597,6 +609,8 @@ if ( $tab != 'storage' ) {
?> ?>
<input type="hidden" name="newMonitor[SaveJPEGs]" value="<?php echo validHtmlStr($monitor->SaveJPEGs()) ?>"/> <input type="hidden" name="newMonitor[SaveJPEGs]" value="<?php echo validHtmlStr($monitor->SaveJPEGs()) ?>"/>
<input type="hidden" name="newMonitor[VideoWriter]" value="<?php echo validHtmlStr($monitor->VideoWriter()) ?>"/> <input type="hidden" name="newMonitor[VideoWriter]" value="<?php echo validHtmlStr($monitor->VideoWriter()) ?>"/>
<input type="hidden" name="newMonitor[OutputCodec]" value="<?php echo validHtmlStr($monitor->OutputCodec()) ?>"/>
<input type="hidden" name="newMonitor[OutputContainer]" value="<?php echo validHtmlStr($monitor->OutputContainer()) ?>"/>
<input type="hidden" name="newMonitor[EncoderParameters]" value="<?php echo validHtmlStr($monitor->EncoderParameters()) ?>"/> <input type="hidden" name="newMonitor[EncoderParameters]" value="<?php echo validHtmlStr($monitor->EncoderParameters()) ?>"/>
<input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($monitor->RecordAudio()) ?>"/> <input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($monitor->RecordAudio()) ?>"/>
<?php <?php
@ -894,6 +908,8 @@ if ( $monitor->Type() == 'Local' ) {
?> ?>
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->VideoWriter() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->VideoWriter() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('OutputCodec') ?></td><td><?php echo htmlSelect( 'newMonitor[OutputCodec]', $videowriter_codecs, $monitor->OutputCodec() );?></td></tr>
<tr><td><?php echo translate('OutputContainer') ?></td><td><?php echo htmlSelect( 'newMonitor[OutputContainer]', $videowriter_containers, $monitor->OutputContainer() );?></td></tr>
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($monitor->EncoderParameters()) ?></textarea></td></tr> <tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($monitor->EncoderParameters()) ?></textarea></td></tr>
<tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( $monitor->RecordAudio() ) { ?> checked="checked"<?php } ?>/></td></tr> <tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( $monitor->RecordAudio() ) { ?> checked="checked"<?php } ?>/></td></tr>
<?php <?php