Merge branch 'zma_to_thread' into put_ffmpeg_into_event_wip
This commit is contained in:
commit
ecc68da0fd
|
@ -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,
|
||||||
|
|
|
@ -67,3 +67,4 @@ SET @s = (SELECT IF(
|
||||||
|
|
||||||
PREPARE stmt FROM @s;
|
PREPARE stmt FROM @s;
|
||||||
EXECUTE stmt;
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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() ) {
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
if ( $params ) {
|
||||||
$log->debug("field: $field, param: ".$$params{$field}) if $debug;
|
$log->debug("field: $field, param: ".$$params{$field}) if $debug;
|
||||||
if ( exists $$params{$field} ) {
|
if ( exists $$params{$field} ) {
|
||||||
$openprint::log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug;
|
$log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug;
|
||||||
if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) {
|
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} ) {
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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} )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
@ -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
|
|
@ -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; };
|
||||||
};
|
};
|
||||||
|
|
157
src/zm_event.cpp
157
src/zm_event.cpp
|
@ -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 );
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
AVCodecContext *context = streams[packet.stream_index].context;
|
AVCodecContext *context = streams[packet.stream_index].context;
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
|
||||||
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 );
|
||||||
|
|
|
@ -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 )
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR){
|
||||||
|
return AV_PIX_FMT_BGR24;
|
||||||
|
} else {
|
||||||
return AV_PIX_FMT_RGB24;
|
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 {
|
||||||
|
@ -180,12 +186,13 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 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 ) {
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,53 +424,31 @@ 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",
|
||||||
//Set video recording flag for event start constructor and easy reference in code
|
name,
|
||||||
videoRecording = ((GetOptVideoWriter() == H264PASSTHROUGH) && camera->SupportsNativeVideo());
|
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
|
||||||
if ( purpose == ANALYSIS ) {
|
);
|
||||||
|
|
||||||
while( shared_data->last_write_index == (unsigned int)image_buffer_count
|
|
||||||
&& shared_data->last_write_time == 0) {
|
|
||||||
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;
|
n_linked_monitors = 0;
|
||||||
linked_monitors = 0;
|
linked_monitors = 0;
|
||||||
|
@ -482,7 +456,7 @@ Monitor::Monitor(
|
||||||
adaptive_skip = true;
|
adaptive_skip = true;
|
||||||
|
|
||||||
ReloadLinkedMonitors( p_linked_monitors );
|
ReloadLinkedMonitors( p_linked_monitors );
|
||||||
}
|
videoStore = NULL;
|
||||||
} // Monitor::Monitor
|
} // Monitor::Monitor
|
||||||
|
|
||||||
bool Monitor::connect() {
|
bool Monitor::connect() {
|
||||||
|
@ -553,19 +527,17 @@ 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 );
|
Debug(3, "Allocating %d image buffers", image_buffer_count );
|
||||||
image_buffer = new Snapshot[image_buffer_count];
|
image_buffer = new ZMPacket[image_buffer_count];
|
||||||
for ( int i = 0; i < image_buffer_count; i++ ) {
|
for ( int i = 0; i < image_buffer_count; i++ ) {
|
||||||
image_buffer[i].timestamp = &(shared_timestamps[i]);
|
|
||||||
image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) );
|
image_buffer[i].image = 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 */
|
||||||
}
|
}
|
||||||
|
@ -573,24 +545,28 @@ bool Monitor::connect() {
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
|
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
|
||||||
|
//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() ) ) {
|
||||||
|
@ -2593,6 +2549,8 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose 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++;
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
||||||
|
|
131
src/zm_mpeg.cpp
131
src/zm_mpeg.cpp
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -659,7 +692,11 @@ void *VideoStream::StreamingThreadCallback(void *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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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( ×tamp, 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( ×tamp, 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 ) {
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
18
src/zm_sdp.h
18
src/zm_sdp.h
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,6 +128,22 @@ VideoStore::VideoStore(
|
||||||
Error("Could not allocate in frame");
|
Error("Could not allocate in frame");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
video_out_ctx = avcodec_alloc_context3( video_out_codec );
|
||||||
|
// Don't have an input stream, so need to tell it what we are sending it, or are transcoding
|
||||||
|
video_out_ctx->width = monitor->Width();
|
||||||
|
video_out_ctx->height = monitor->Height();
|
||||||
|
video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
|
|
||||||
|
if ( monitor->OutputCodec() == "mjpeg" ) {
|
||||||
|
video_out_codec = avcodec_find_encoder_by_name("mjpeg");
|
||||||
|
if ( ! video_out_codec ) {
|
||||||
|
Debug(1, "Didn't find omx");
|
||||||
|
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");
|
video_out_codec = avcodec_find_encoder_by_name("h264_omx");
|
||||||
if ( ! video_out_codec ) {
|
if ( ! video_out_codec ) {
|
||||||
Debug(1, "Didn't find omx");
|
Debug(1, "Didn't find omx");
|
||||||
|
@ -137,13 +154,7 @@ VideoStore::VideoStore(
|
||||||
}
|
}
|
||||||
Debug(2, "Have video out codec");
|
Debug(2, "Have video out codec");
|
||||||
|
|
||||||
video_out_ctx = avcodec_alloc_context3( video_out_codec );
|
|
||||||
// Don't have an input stream, so need to tell it what we are sending it, or are transcoding
|
|
||||||
video_out_ctx->width = monitor->Width();
|
|
||||||
video_out_ctx->height = monitor->Height();
|
|
||||||
video_out_ctx->codec_id = AV_CODEC_ID_H264;
|
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;
|
//video_in_ctx->sample_aspect_ratio;
|
||||||
/* take first format from list of supported formats */
|
/* take first format from list of supported formats */
|
||||||
//video_out_ctx->pix_fmt = video_out_codec->pix_fmts[0];
|
//video_out_ctx->pix_fmt = video_out_codec->pix_fmts[0];
|
||||||
|
@ -157,14 +168,6 @@ VideoStore::VideoStore(
|
||||||
video_out_ctx->qmax = 51;
|
video_out_ctx->qmax = 51;
|
||||||
video_out_ctx->qcompress = 0.6;
|
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;
|
AVDictionary *opts = 0;
|
||||||
std::string Options = monitor->GetEncoderOptions();
|
std::string Options = monitor->GetEncoderOptions();
|
||||||
ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
||||||
|
@ -176,8 +179,11 @@ VideoStore::VideoStore(
|
||||||
Debug( 3, "Encoder Option %s=%s", e->key, e->value );
|
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 ( ! 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 ) {
|
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) {
|
||||||
Warning("Can't open video codec (%s)! %s, trying h264",
|
Warning("Can't open video codec (%s)! %s, trying h264",
|
||||||
|
@ -205,6 +211,12 @@ VideoStore::VideoStore(
|
||||||
Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key);
|
Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key);
|
||||||
}
|
}
|
||||||
av_dict_free(&opts);
|
av_dict_free(&opts);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}// 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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
|
25
src/zmc.cpp
25
src/zmc.cpp
|
@ -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;
|
||||||
|
|
|
@ -360,6 +360,7 @@ 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 ) );
|
||||||
|
|
||||||
|
if ( isset($_REQUEST['filter']) )
|
||||||
parseFilter( $_REQUEST['filter'] );
|
parseFilter( $_REQUEST['filter'] );
|
||||||
parseSort();
|
parseSort();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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' => '',
|
||||||
|
|
|
@ -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 ) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()) ?>"/>
|
|
@ -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 ) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue