Merge branch 'master' of github.com:/ZoneMinder/zoneminder
This commit is contained in:
commit
b5f64f1c69
|
@ -1,6 +1,7 @@
|
||||||
target_compile_options(zm-warning-interface
|
target_compile_options(zm-warning-interface
|
||||||
INTERFACE
|
INTERFACE
|
||||||
-Wall
|
-Wall
|
||||||
|
-Wconditionally-supported
|
||||||
-Wextra
|
-Wextra
|
||||||
-Wformat-security
|
-Wformat-security
|
||||||
-Wno-cast-function-type
|
-Wno-cast-function-type
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 20846b25ffc0c9a1de1b6701ca99425ef39e9f3f
|
Subproject commit c81ec629f091b77e2948b1e6113064de7e9bc9e3
|
|
@ -138,6 +138,14 @@ if ( $options{command} ) {
|
||||||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $zm_terminate = 0;
|
||||||
|
sub TermHandler {
|
||||||
|
Info('Received TERM, exiting');
|
||||||
|
$zm_terminate = 1;
|
||||||
|
}
|
||||||
|
$SIG{TERM} = \&TermHandler;
|
||||||
|
$SIG{INT} = \&TermHandler;
|
||||||
|
|
||||||
Info("Control server $id/$protocol starting at "
|
Info("Control server $id/$protocol starting at "
|
||||||
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
||||||
);
|
);
|
||||||
|
@ -166,7 +174,7 @@ if ( $options{command} ) {
|
||||||
my $win = $rin;
|
my $win = $rin;
|
||||||
my $ein = $win;
|
my $ein = $win;
|
||||||
my $timeout = MAX_COMMAND_WAIT;
|
my $timeout = MAX_COMMAND_WAIT;
|
||||||
while ( 1 ) {
|
while (!$zm_terminate) {
|
||||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||||
if ( $nfound > 0 ) {
|
if ( $nfound > 0 ) {
|
||||||
if ( vec($rout, fileno(SERVER), 1) ) {
|
if ( vec($rout, fileno(SERVER), 1) ) {
|
||||||
|
@ -202,7 +210,7 @@ if ( $options{command} ) {
|
||||||
} else {
|
} else {
|
||||||
Debug('Select timed out');
|
Debug('Select timed out');
|
||||||
}
|
}
|
||||||
} # end while forever
|
} # end while !$zm_terminate
|
||||||
Info("Control server $id/$protocol exiting");
|
Info("Control server $id/$protocol exiting");
|
||||||
unlink($sock_file);
|
unlink($sock_file);
|
||||||
$control->close();
|
$control->close();
|
||||||
|
|
|
@ -28,13 +28,20 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
logInit();
|
logInit();
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
|
my $zm_terminate = 0;
|
||||||
|
sub TermHandler {
|
||||||
|
Info('Received TERM, exiting');
|
||||||
|
$zm_terminate = 1;
|
||||||
|
}
|
||||||
|
$SIG{TERM} = \&TermHandler;
|
||||||
|
$SIG{INT} = \&TermHandler;
|
||||||
|
|
||||||
Info('Stats Daemon starting in '.START_DELAY.' seconds');
|
Info('Stats Daemon starting in '.START_DELAY.' seconds');
|
||||||
sleep(START_DELAY);
|
sleep(START_DELAY);
|
||||||
|
|
||||||
my $dbh = zmDbConnect();
|
my $dbh = zmDbConnect();
|
||||||
|
|
||||||
while( 1 ) {
|
while (!$zm_terminate) {
|
||||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||||
Info('Reconnecting to db');
|
Info('Reconnecting to db');
|
||||||
if ( !($dbh = zmDbConnect()) ) {
|
if ( !($dbh = zmDbConnect()) ) {
|
||||||
|
@ -95,7 +102,7 @@ while( 1 ) {
|
||||||
zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7));
|
zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7));
|
||||||
|
|
||||||
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
||||||
} # end while (1)
|
} # end while (!$zm_terminate)
|
||||||
|
|
||||||
Info('Stats Daemon exiting');
|
Info('Stats Daemon exiting');
|
||||||
exit();
|
exit();
|
||||||
|
|
|
@ -87,6 +87,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
logInit();
|
logInit();
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
|
my $zm_terminate = 0;
|
||||||
|
sub TermHandler {
|
||||||
|
Info('Received TERM, exiting');
|
||||||
|
$zm_terminate = 1;
|
||||||
|
}
|
||||||
|
$SIG{TERM} = \&TermHandler;
|
||||||
|
$SIG{INT} = \&TermHandler;
|
||||||
|
|
||||||
Info('Trigger daemon starting');
|
Info('Trigger daemon starting');
|
||||||
|
|
||||||
|
@ -118,7 +125,7 @@ my $win = $rin;
|
||||||
my $ein = $win;
|
my $ein = $win;
|
||||||
my $timeout = SELECT_TIMEOUT;
|
my $timeout = SELECT_TIMEOUT;
|
||||||
my %actions;
|
my %actions;
|
||||||
while (1) {
|
while (!$zm_terminate) {
|
||||||
$rin = $base_rin;
|
$rin = $base_rin;
|
||||||
# Add the file descriptors of any spawned connections
|
# Add the file descriptors of any spawned connections
|
||||||
foreach my $fileno ( keys %spawned_connections ) {
|
foreach my $fileno ( keys %spawned_connections ) {
|
||||||
|
@ -312,7 +319,7 @@ while (1) {
|
||||||
|
|
||||||
# zmDbConnect will ping and reconnect if neccessary
|
# zmDbConnect will ping and reconnect if neccessary
|
||||||
$dbh = zmDbConnect();
|
$dbh = zmDbConnect();
|
||||||
} # end while ( 1 )
|
} # end while (!$zm_terminate)
|
||||||
Info('Trigger daemon exiting');
|
Info('Trigger daemon exiting');
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
logInit();
|
logInit();
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
|
my $zm_terminate = 0;
|
||||||
|
sub TermHandler {
|
||||||
|
Info('Received TERM, exiting');
|
||||||
|
$zm_terminate = 1;
|
||||||
|
}
|
||||||
|
$SIG{TERM} = \&TermHandler;
|
||||||
|
$SIG{INT} = \&TermHandler;
|
||||||
|
|
||||||
Info('Watchdog starting, pausing for '.START_DELAY.' seconds');
|
Info('Watchdog starting, pausing for '.START_DELAY.' seconds');
|
||||||
sleep(START_DELAY);
|
sleep(START_DELAY);
|
||||||
|
@ -77,7 +84,7 @@ my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'S
|
||||||
my $sth = $dbh->prepare_cached($sql)
|
my $sth = $dbh->prepare_cached($sql)
|
||||||
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
||||||
|
|
||||||
while( 1 ) {
|
while (!$zm_terminate) {
|
||||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||||
if ( ! ( $dbh = zmDbConnect() ) ) {
|
if ( ! ( $dbh = zmDbConnect() ) ) {
|
||||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||||
|
@ -192,7 +199,7 @@ while( 1 ) {
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
|
|
||||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||||
} # end while (1)
|
} # end while (!$zm_terminate)
|
||||||
|
|
||||||
Info("Watchdog exiting");
|
Info("Watchdog exiting");
|
||||||
exit();
|
exit();
|
||||||
|
|
|
@ -16,6 +16,7 @@ set(ZM_BIN_SRC_FILES
|
||||||
zm_crypt.cpp
|
zm_crypt.cpp
|
||||||
zm.cpp
|
zm.cpp
|
||||||
zm_db.cpp
|
zm_db.cpp
|
||||||
|
zm_decoder_thread.cpp
|
||||||
zm_logger.cpp
|
zm_logger.cpp
|
||||||
zm_event.cpp
|
zm_event.cpp
|
||||||
zm_eventstream.cpp
|
zm_eventstream.cpp
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
#include "zm_analysis_thread.h"
|
#include "zm_analysis_thread.h"
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
#include "zm_signal.h"
|
#include "zm_signal.h"
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
|
|
||||||
AnalysisThread::AnalysisThread(std::shared_ptr<Monitor> monitor) :
|
//AnalysisThread::AnalysisThread(std::shared_ptr<Monitor> monitor) :
|
||||||
monitor_(std::move(monitor)), terminate_(false) {
|
AnalysisThread::AnalysisThread(Monitor* monitor) :
|
||||||
|
monitor_(monitor), terminate_(false) {
|
||||||
|
//monitor_(std::move(monitor)), terminate_(false) {
|
||||||
thread_ = std::thread(&AnalysisThread::Run, this);
|
thread_ = std::thread(&AnalysisThread::Run, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
#ifndef ZM_ANALYSIS_THREAD_H
|
#ifndef ZM_ANALYSIS_THREAD_H
|
||||||
#define ZM_ANALYSIS_THREAD_H
|
#define ZM_ANALYSIS_THREAD_H
|
||||||
|
|
||||||
#include "zm_monitor.h"
|
class Monitor;
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
class AnalysisThread {
|
class AnalysisThread {
|
||||||
public:
|
public:
|
||||||
explicit AnalysisThread(std::shared_ptr<Monitor> monitor);
|
explicit AnalysisThread(Monitor* monitor);
|
||||||
|
//explicit AnalysisThread(std::shared_ptr<Monitor> monitor);
|
||||||
~AnalysisThread();
|
~AnalysisThread();
|
||||||
AnalysisThread(AnalysisThread &rhs) = delete;
|
AnalysisThread(AnalysisThread &rhs) = delete;
|
||||||
AnalysisThread(AnalysisThread &&rhs) = delete;
|
AnalysisThread(AnalysisThread &&rhs) = delete;
|
||||||
|
@ -18,7 +19,8 @@ class AnalysisThread {
|
||||||
private:
|
private:
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
std::shared_ptr<Monitor> monitor_;
|
Monitor* monitor_;
|
||||||
|
//std::shared_ptr<Monitor> monitor_;
|
||||||
std::atomic<bool> terminate_;
|
std::atomic<bool> terminate_;
|
||||||
std::thread thread_;
|
std::thread thread_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "zm_decoder_thread.h"
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
|
||||||
|
//DecoderThread::DecoderThread(std::shared_ptr<Monitor> monitor) :
|
||||||
|
DecoderThread::DecoderThread(Monitor * monitor) :
|
||||||
|
monitor_(monitor), terminate_(false) {
|
||||||
|
//monitor_(std::move(monitor)), terminate_(false) {
|
||||||
|
thread_ = std::thread(&DecoderThread::Run, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
DecoderThread::~DecoderThread() {
|
||||||
|
Stop();
|
||||||
|
if (thread_.joinable())
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecoderThread::Run() {
|
||||||
|
Debug(2, "DecoderThread::Run() for %d", monitor_->Id());
|
||||||
|
|
||||||
|
//Microseconds decoder_rate = Microseconds(monitor_->GetDecoderRate());
|
||||||
|
//Seconds decoder_update_delay = Seconds(monitor_->GetDecoderUpdateDelay());
|
||||||
|
//Debug(2, "DecoderThread::Run() have update delay %d", decoder_update_delay);
|
||||||
|
|
||||||
|
//TimePoint last_decoder_update_time = std::chrono::steady_clock::now();
|
||||||
|
//TimePoint cur_time;
|
||||||
|
|
||||||
|
while (!(terminate_ or zm_terminate)) {
|
||||||
|
// Some periodic updates are required for variable capturing framerate
|
||||||
|
//if (decoder_update_delay != Seconds::zero()) {
|
||||||
|
//cur_time = std::chrono::steady_clock::now();
|
||||||
|
//Debug(2, "Updating adaptive skip");
|
||||||
|
//if ((cur_time - last_decoder_update_time) > decoder_update_delay) {
|
||||||
|
//decoder_rate = Microseconds(monitor_->GetDecoderRate());
|
||||||
|
//last_decoder_update_time = cur_time;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (!monitor_->Decode()) {
|
||||||
|
//if ( !(terminate_ or zm_terminate) ) {
|
||||||
|
//Microseconds sleep_for = monitor_->Active() ? Microseconds(ZM_SAMPLE_RATE) : Microseconds(ZM_SUSPENDED_RATE);
|
||||||
|
//Debug(2, "Sleeping for %" PRId64 "us", int64(sleep_for.count()));
|
||||||
|
//std::this_thread::sleep_for(sleep_for);
|
||||||
|
//}
|
||||||
|
//} else if (decoder_rate != Microseconds::zero()) {
|
||||||
|
//Debug(2, "Sleeping for %" PRId64 " us", int64(decoder_rate.count()));
|
||||||
|
//std::this_thread::sleep_for(decoder_rate);
|
||||||
|
//} else {
|
||||||
|
//Debug(2, "Not sleeping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef ZM_DECODER_THREAD_H
|
||||||
|
#define ZM_DECODER_THREAD_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class Monitor;
|
||||||
|
|
||||||
|
class DecoderThread {
|
||||||
|
public:
|
||||||
|
explicit DecoderThread(Monitor* monitor);
|
||||||
|
//explicit DecoderThread(std::shared_ptr<Monitor> monitor);
|
||||||
|
~DecoderThread();
|
||||||
|
DecoderThread(DecoderThread &rhs) = delete;
|
||||||
|
DecoderThread(DecoderThread &&rhs) = delete;
|
||||||
|
|
||||||
|
void Stop() { terminate_ = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
Monitor* monitor_;
|
||||||
|
//std::shared_ptr<Monitor> monitor_;
|
||||||
|
std::atomic<bool> terminate_;
|
||||||
|
std::thread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -71,8 +71,8 @@ Event::Event(
|
||||||
std::string notes;
|
std::string notes;
|
||||||
createNotes(notes);
|
createNotes(notes);
|
||||||
|
|
||||||
struct timeval now;
|
timeval now = {};
|
||||||
gettimeofday(&now, 0);
|
gettimeofday(&now, nullptr);
|
||||||
|
|
||||||
if ( !start_time.tv_sec ) {
|
if ( !start_time.tv_sec ) {
|
||||||
Warning("Event has zero time, setting to now");
|
Warning("Event has zero time, setting to now");
|
||||||
|
@ -80,12 +80,12 @@ Event::Event(
|
||||||
} else if ( start_time.tv_sec > now.tv_sec ) {
|
} else if ( start_time.tv_sec > now.tv_sec ) {
|
||||||
char buffer[26];
|
char buffer[26];
|
||||||
char buffer_now[26];
|
char buffer_now[26];
|
||||||
struct tm* tm_info;
|
tm tm_info = {};
|
||||||
|
|
||||||
tm_info = localtime(&start_time.tv_sec);
|
localtime_r(&start_time.tv_sec, &tm_info);
|
||||||
strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", tm_info);
|
strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", &tm_info);
|
||||||
tm_info = localtime(&now.tv_sec);
|
localtime_r(&now.tv_sec, &tm_info);
|
||||||
strftime(buffer_now, 26, "%Y:%m:%d %H:%M:%S", tm_info);
|
strftime(buffer_now, 26, "%Y:%m:%d %H:%M:%S", &tm_info);
|
||||||
|
|
||||||
Error(
|
Error(
|
||||||
"StartDateTime in the future starttime %u.%u >? now %u.%u difference %d\n%s\n%s",
|
"StartDateTime in the future starttime %u.%u >? now %u.%u difference %d\n%s\n%s",
|
||||||
|
@ -661,16 +661,17 @@ bool Event::SetPath(Storage *storage) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tm *stime = localtime(&start_time.tv_sec);
|
tm stime = {};
|
||||||
|
localtime_r(&start_time.tv_sec, &stime);
|
||||||
if ( scheme == Storage::DEEP ) {
|
if ( scheme == Storage::DEEP ) {
|
||||||
|
|
||||||
int dt_parts[6];
|
int dt_parts[6];
|
||||||
dt_parts[0] = stime->tm_year-100;
|
dt_parts[0] = stime.tm_year-100;
|
||||||
dt_parts[1] = stime->tm_mon+1;
|
dt_parts[1] = stime.tm_mon+1;
|
||||||
dt_parts[2] = stime->tm_mday;
|
dt_parts[2] = stime.tm_mday;
|
||||||
dt_parts[3] = stime->tm_hour;
|
dt_parts[3] = stime.tm_hour;
|
||||||
dt_parts[4] = stime->tm_min;
|
dt_parts[4] = stime.tm_min;
|
||||||
dt_parts[5] = stime->tm_sec;
|
dt_parts[5] = stime.tm_sec;
|
||||||
|
|
||||||
std::string date_path;
|
std::string date_path;
|
||||||
std::string time_path;
|
std::string time_path;
|
||||||
|
@ -685,7 +686,7 @@ bool Event::SetPath(Storage *storage) {
|
||||||
if ( i == 2 )
|
if ( i == 2 )
|
||||||
date_path = path;
|
date_path = path;
|
||||||
}
|
}
|
||||||
time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec);
|
time_path = stringtf("%02d/%02d/%02d", stime.tm_hour, stime.tm_min, stime.tm_sec);
|
||||||
|
|
||||||
// Create event id symlink
|
// Create event id symlink
|
||||||
std::string id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id);
|
std::string id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id);
|
||||||
|
@ -695,7 +696,7 @@ bool Event::SetPath(Storage *storage) {
|
||||||
}
|
}
|
||||||
} else if ( scheme == Storage::MEDIUM ) {
|
} else if ( scheme == Storage::MEDIUM ) {
|
||||||
path += stringtf("/%04d-%02d-%02d",
|
path += stringtf("/%04d-%02d-%02d",
|
||||||
stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
|
stime.tm_year+1900, stime.tm_mon+1, stime.tm_mday
|
||||||
);
|
);
|
||||||
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
|
||||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||||
|
|
|
@ -138,18 +138,20 @@ class Event {
|
||||||
bool SetPath(Storage *storage);
|
bool SetPath(Storage *storage);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const char *getSubPath(struct tm *time) {
|
static const char *getSubPath(tm time) {
|
||||||
static char subpath[PATH_MAX] = "";
|
static char subpath[PATH_MAX] = "";
|
||||||
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
|
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
|
||||||
time->tm_year-100, time->tm_mon+1, time->tm_mday,
|
time.tm_year-100, time.tm_mon+1, time.tm_mday,
|
||||||
time->tm_hour, time->tm_min, time->tm_sec);
|
time.tm_hour, time.tm_min, time.tm_sec);
|
||||||
return subpath;
|
return subpath;
|
||||||
}
|
}
|
||||||
static const char *getSubPath(time_t *time) {
|
static const char *getSubPath(time_t *time) {
|
||||||
return Event::getSubPath(localtime(time));
|
tm time_tm = {};
|
||||||
|
localtime_r(time, &time_tm);
|
||||||
|
return Event::getSubPath(time_tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* getEventFile(void) const {
|
const char* getEventFile() const {
|
||||||
return video_file.c_str();
|
return video_file.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,33 +171,35 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
const char *storage_path = storage->Path();
|
const char *storage_path = storage->Path();
|
||||||
|
|
||||||
if ( event_data->scheme == Storage::DEEP ) {
|
if ( event_data->scheme == Storage::DEEP ) {
|
||||||
struct tm *event_time = localtime(&event_data->start_time);
|
tm event_time = {};
|
||||||
|
localtime_r(&event_data->start_time, &event_time);
|
||||||
|
|
||||||
if ( storage_path[0] == '/' )
|
if ( storage_path[0] == '/' )
|
||||||
snprintf(event_data->path, sizeof(event_data->path),
|
snprintf(event_data->path, sizeof(event_data->path),
|
||||||
"%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
"%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||||
storage_path, event_data->monitor_id,
|
storage_path, event_data->monitor_id,
|
||||||
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday,
|
event_time.tm_year-100, event_time.tm_mon+1, event_time.tm_mday,
|
||||||
event_time->tm_hour, event_time->tm_min, event_time->tm_sec);
|
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||||
else
|
else
|
||||||
snprintf(event_data->path, sizeof(event_data->path),
|
snprintf(event_data->path, sizeof(event_data->path),
|
||||||
"%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
"%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||||
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday,
|
event_time.tm_year-100, event_time.tm_mon+1, event_time.tm_mday,
|
||||||
event_time->tm_hour, event_time->tm_min, event_time->tm_sec);
|
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||||
} else if ( event_data->scheme == Storage::MEDIUM ) {
|
} else if ( event_data->scheme == Storage::MEDIUM ) {
|
||||||
struct tm *event_time = localtime(&event_data->start_time);
|
tm event_time = {};
|
||||||
|
localtime_r(&event_data->start_time, &event_time);
|
||||||
if ( storage_path[0] == '/' )
|
if ( storage_path[0] == '/' )
|
||||||
snprintf(event_data->path, sizeof(event_data->path),
|
snprintf(event_data->path, sizeof(event_data->path),
|
||||||
"%s/%u/%04d-%02d-%02d/%" PRIu64,
|
"%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||||
storage_path, event_data->monitor_id,
|
storage_path, event_data->monitor_id,
|
||||||
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
|
event_time.tm_year+1900, event_time.tm_mon+1, event_time.tm_mday,
|
||||||
event_data->event_id);
|
event_data->event_id);
|
||||||
else
|
else
|
||||||
snprintf(event_data->path, sizeof(event_data->path),
|
snprintf(event_data->path, sizeof(event_data->path),
|
||||||
"%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
|
"%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||||
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
|
event_time.tm_year+1900, event_time.tm_mon+1, event_time.tm_mday,
|
||||||
event_data->event_id);
|
event_data->event_id);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -200,10 +200,10 @@ int FfmpegCamera::Capture(ZMPacket &zm_packet) {
|
||||||
) ) {
|
) ) {
|
||||||
// if audio stream is behind video stream, then read from audio, otherwise video
|
// if audio stream is behind video stream, then read from audio, otherwise video
|
||||||
mFormatContextPtr = mSecondFormatContext;
|
mFormatContextPtr = mSecondFormatContext;
|
||||||
Debug(1, "Using audio input");
|
Debug(2, "Using audio input");
|
||||||
} else {
|
} else {
|
||||||
mFormatContextPtr = mFormatContext;
|
mFormatContextPtr = mFormatContext;
|
||||||
Debug(1, "Using video input because %" PRId64 " >= %" PRId64,
|
Debug(2, "Using video input because %" PRId64 " >= %" PRId64,
|
||||||
(mAudioStream?av_rescale_q(mLastAudioPTS, mAudioStream->time_base, AV_TIME_BASE_Q):0),
|
(mAudioStream?av_rescale_q(mLastAudioPTS, mAudioStream->time_base, AV_TIME_BASE_Q):0),
|
||||||
av_rescale_q(mLastVideoPTS, mVideoStream->time_base, AV_TIME_BASE_Q)
|
av_rescale_q(mLastVideoPTS, mVideoStream->time_base, AV_TIME_BASE_Q)
|
||||||
);
|
);
|
||||||
|
|
|
@ -86,7 +86,7 @@ bool Fifo::open() {
|
||||||
}
|
}
|
||||||
long pipe_size = (long)fcntl(raw_fd, F_GETPIPE_SZ);
|
long pipe_size = (long)fcntl(raw_fd, F_GETPIPE_SZ);
|
||||||
if (pipe_size == -1) {
|
if (pipe_size == -1) {
|
||||||
perror("get pipe size failed.");
|
Error("get pipe size failed.");
|
||||||
}
|
}
|
||||||
Debug(1, "default pipe size: %ld\n", pipe_size);
|
Debug(1, "default pipe size: %ld\n", pipe_size);
|
||||||
#endif
|
#endif
|
||||||
|
@ -104,9 +104,9 @@ bool Fifo::close() {
|
||||||
bool Fifo::writePacket(ZMPacket &packet) {
|
bool Fifo::writePacket(ZMPacket &packet) {
|
||||||
if (!(outfile or open())) return false;
|
if (!(outfile or open())) return false;
|
||||||
|
|
||||||
Debug(1, "Writing header ZM %u %" PRId64, packet.packet.size, packet.pts);
|
Debug(2, "Writing header ZM %u %" PRId64, packet.packet.size, packet.pts);
|
||||||
// Going to write a brief header
|
// Going to write a brief header
|
||||||
if ( fprintf(outfile, "ZM %u %" PRId64 "\n", packet.packet.size, packet.pts) < 0 ) {
|
if (fprintf(outfile, "ZM %u %" PRId64 "\n", packet.packet.size, packet.pts) < 0) {
|
||||||
if (errno != EAGAIN) {
|
if (errno != EAGAIN) {
|
||||||
Error("Problem during writing: %s", strerror(errno));
|
Error("Problem during writing: %s", strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
|
@ -121,6 +121,7 @@ bool Fifo::writePacket(ZMPacket &packet) {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Fifo::writePacket(std::string filename, ZMPacket &packet) {
|
bool Fifo::writePacket(std::string filename, ZMPacket &packet) {
|
||||||
bool on_blocking_abort = true;
|
bool on_blocking_abort = true;
|
||||||
FILE *outfile = nullptr;
|
FILE *outfile = nullptr;
|
||||||
|
|
|
@ -1186,7 +1186,8 @@ cinfo->out_color_space = JCS_RGB;
|
||||||
|
|
||||||
// This is a lot of stuff to allocate on the stack. Recommend char *timebuf[64];
|
// This is a lot of stuff to allocate on the stack. Recommend char *timebuf[64];
|
||||||
char timebuf[64], msbuf[64];
|
char timebuf[64], msbuf[64];
|
||||||
strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime(&(timestamp.tv_sec)));
|
tm timestamp_tm = {};
|
||||||
|
strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime_r(×tamp.tv_sec, ×tamp_tm));
|
||||||
snprintf(msbuf, sizeof msbuf, "%06d",(int)(timestamp.tv_usec)); // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it
|
snprintf(msbuf, sizeof msbuf, "%06d",(int)(timestamp.tv_usec)); // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it
|
||||||
unsigned char exiftimes[82] = {
|
unsigned char exiftimes[82] = {
|
||||||
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
|
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
@ -2132,7 +2133,8 @@ void Image::Annotate(
|
||||||
|
|
||||||
void Image::Timestamp( const char *label, const time_t when, const Coord &coord, const int size ) {
|
void Image::Timestamp( const char *label, const time_t when, const Coord &coord, const int size ) {
|
||||||
char time_text[64];
|
char time_text[64];
|
||||||
strftime(time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime(&when));
|
tm when_tm = {};
|
||||||
|
strftime(time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime_r(&when, &when_tm));
|
||||||
if ( label ) {
|
if ( label ) {
|
||||||
// Assume label is max 64, + ' - ' + 64 chars of time_text
|
// Assume label is max 64, + ' - ' + 64 chars of time_text
|
||||||
char text[132];
|
char text[132];
|
||||||
|
|
|
@ -798,10 +798,10 @@ void LocalCamera::Initialise() {
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( v4l2_data.fmt.fmt.pix.width != width ) {
|
if ( v4l2_data.fmt.fmt.pix.width != width ) {
|
||||||
Warning("Failed to set requested width");
|
Warning("Failed to set requested width");
|
||||||
}
|
}
|
||||||
if ( v4l2_data.fmt.fmt.pix.height != height ) {
|
if ( v4l2_data.fmt.fmt.pix.height != height ) {
|
||||||
Warning("Failed to set requested height");
|
Warning("Failed to set requested height");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buggy driver paranoia. */
|
/* Buggy driver paranoia. */
|
||||||
|
@ -2087,8 +2087,11 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
|
||||||
buffer_bytesused = v4l2_data.bufptr->bytesused;
|
buffer_bytesused = v4l2_data.bufptr->bytesused;
|
||||||
bytes += buffer_bytesused;
|
bytes += buffer_bytesused;
|
||||||
|
|
||||||
if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height) ) {
|
if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) > (width * height) ) {
|
||||||
Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",
|
Fatal("Captured image dimensions larger than image buffer: V4L2: %dx%d monitor: %dx%d",
|
||||||
|
v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height);
|
||||||
|
} else if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height) ) {
|
||||||
|
Error("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",
|
||||||
v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height);
|
v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height);
|
||||||
}
|
}
|
||||||
} // end if v4l2
|
} // end if v4l2
|
||||||
|
|
|
@ -447,7 +447,8 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
char *timePtr = timeString;
|
char *timePtr = timeString;
|
||||||
timePtr += strftime(timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec));
|
tm now_tm = {};
|
||||||
|
timePtr += strftime(timePtr, sizeof(timeString), "%x %H:%M:%S", localtime_r(&timeVal.tv_sec, &now_tm));
|
||||||
snprintf(timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec);
|
snprintf(timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec);
|
||||||
#if 0
|
#if 0
|
||||||
}
|
}
|
||||||
|
@ -524,8 +525,8 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
||||||
if (mLogFileFP) {
|
if (mLogFileFP) {
|
||||||
fputs(logString, mLogFileFP);
|
fputs(logString, mLogFileFP);
|
||||||
if (mFlush) fflush(mLogFileFP);
|
if (mFlush) fflush(mLogFileFP);
|
||||||
} else {
|
} else if (mTerminalLevel != NOLOG) {
|
||||||
puts("Logging to file, but failed to open it\n");
|
puts("Logging to file but failed to open it\n");
|
||||||
}
|
}
|
||||||
} // end if level <= mFileLevel
|
} // end if level <= mFileLevel
|
||||||
|
|
||||||
|
|
|
@ -395,6 +395,9 @@ Monitor::Monitor()
|
||||||
storage(nullptr),
|
storage(nullptr),
|
||||||
videoStore(nullptr),
|
videoStore(nullptr),
|
||||||
analysis_it(nullptr),
|
analysis_it(nullptr),
|
||||||
|
analysis_thread(nullptr),
|
||||||
|
decoder_it(nullptr),
|
||||||
|
decoder(nullptr),
|
||||||
n_zones(0),
|
n_zones(0),
|
||||||
zones(nullptr),
|
zones(nullptr),
|
||||||
privacy_bitmask(nullptr),
|
privacy_bitmask(nullptr),
|
||||||
|
@ -444,8 +447,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
||||||
|
|
||||||
storage_id = atoi(dbrow[col]); col++;
|
storage_id = atoi(dbrow[col]); col++;
|
||||||
if ( storage )
|
if (storage) delete storage;
|
||||||
delete storage;
|
|
||||||
storage = new Storage(storage_id);
|
storage = new Storage(storage_id);
|
||||||
|
|
||||||
if ( ! strcmp(dbrow[col], "Local") ) {
|
if ( ! strcmp(dbrow[col], "Local") ) {
|
||||||
|
@ -580,6 +582,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
warmup_count = atoi(dbrow[col]); col++;
|
warmup_count = atoi(dbrow[col]); col++;
|
||||||
pre_event_count = atoi(dbrow[col]); col++;
|
pre_event_count = atoi(dbrow[col]); col++;
|
||||||
packetqueue.setMaxVideoPackets(pre_event_count);
|
packetqueue.setMaxVideoPackets(pre_event_count);
|
||||||
|
packetqueue.setKeepKeyframes(videowriter == PASSTHROUGH);
|
||||||
post_event_count = atoi(dbrow[col]); col++;
|
post_event_count = atoi(dbrow[col]); col++;
|
||||||
stream_replay_buffer = atoi(dbrow[col]); col++;
|
stream_replay_buffer = atoi(dbrow[col]); col++;
|
||||||
alarm_frame_count = atoi(dbrow[col]); col++;
|
alarm_frame_count = atoi(dbrow[col]); col++;
|
||||||
|
@ -634,16 +637,16 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
image_buffer_count, image_size, (image_buffer_count*image_size),
|
image_buffer_count, image_size, (image_buffer_count*image_size),
|
||||||
mem_size);
|
mem_size);
|
||||||
|
|
||||||
Zone **zones = 0;
|
|
||||||
int n_zones = Zone::Load(this, zones);
|
|
||||||
this->AddZones(n_zones, zones);
|
|
||||||
this->AddPrivacyBitmask(zones);
|
|
||||||
|
|
||||||
// Should maybe store this for later use
|
// Should maybe store this for later use
|
||||||
std::string monitor_dir = stringtf("%s/%d", storage->Path(), id);
|
std::string monitor_dir = stringtf("%s/%d", storage->Path(), id);
|
||||||
LoadCamera();
|
LoadCamera();
|
||||||
|
|
||||||
if ( purpose != QUERY ) {
|
if ( purpose != QUERY ) {
|
||||||
|
Zone **zones = 0;
|
||||||
|
int n_zones = Zone::Load(this, zones);
|
||||||
|
this->AddZones(n_zones, zones);
|
||||||
|
this->AddPrivacyBitmask(zones);
|
||||||
|
|
||||||
if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) {
|
if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) {
|
||||||
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
|
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
|
||||||
}
|
}
|
||||||
|
@ -1125,12 +1128,11 @@ Monitor::~Monitor() {
|
||||||
disconnect();
|
disconnect();
|
||||||
} // end if mem_ptr
|
} // end if mem_ptr
|
||||||
|
|
||||||
if (analysis_it) {
|
// Will be free by packetqueue destructor
|
||||||
packetqueue.free_it(analysis_it);
|
analysis_it = nullptr;
|
||||||
analysis_it = nullptr;
|
decoder_it = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
for ( int i = 0; i < n_zones; i++ ) {
|
for (int i = 0; i < n_zones; i++) {
|
||||||
delete zones[i];
|
delete zones[i];
|
||||||
}
|
}
|
||||||
delete[] zones;
|
delete[] zones;
|
||||||
|
@ -1733,16 +1735,10 @@ void Monitor::UpdateCaptureFPS() {
|
||||||
last_fps_time = now_double;
|
last_fps_time = now_double;
|
||||||
last_capture_image_count = image_count;
|
last_capture_image_count = image_count;
|
||||||
|
|
||||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
std::string sql = stringtf(
|
||||||
// The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost,
|
"UPDATE Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u WHERE MonitorId=%u",
|
||||||
// and nothing else will update the status until zmc restarts. Since we are successfully capturing we can
|
new_capture_fps, new_capture_bandwidth, id);
|
||||||
// assume that we are connected
|
dbQueue.push(sql.c_str());
|
||||||
snprintf(sql, sizeof(sql),
|
|
||||||
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth,Status) "
|
|
||||||
"VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE "
|
|
||||||
"CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected'",
|
|
||||||
id, new_capture_fps, new_capture_bandwidth, new_capture_fps, new_capture_bandwidth);
|
|
||||||
dbQueue.push(sql);
|
|
||||||
} // now != last_fps_time
|
} // now != last_fps_time
|
||||||
} // end if report fps
|
} // end if report fps
|
||||||
} // void Monitor::UpdateCaptureFPS()
|
} // void Monitor::UpdateCaptureFPS()
|
||||||
|
@ -1802,22 +1798,17 @@ void Monitor::UpdateAnalysisFPS() {
|
||||||
// If there isn't then we keep pre-event + alarm frames. = pre_event_count
|
// If there isn't then we keep pre-event + alarm frames. = pre_event_count
|
||||||
bool Monitor::Analyse() {
|
bool Monitor::Analyse() {
|
||||||
|
|
||||||
if ( !Enabled() ) {
|
|
||||||
Warning("Shouldn't be doing Analyse when not Enabled");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( !analysis_it )
|
|
||||||
analysis_it = packetqueue.get_video_it(true);
|
|
||||||
|
|
||||||
// if have event, send frames until we find a video packet, at which point do analysis. Adaptive skip should only affect which frames we do analysis on.
|
// if have event, send frames until we find a video packet, at which point do analysis. Adaptive skip should only affect which frames we do analysis on.
|
||||||
|
|
||||||
// get_analysis_packet will lock the packet and may wait if analysis_it is at the end
|
// get_analysis_packet will lock the packet and may wait if analysis_it is at the end
|
||||||
ZMPacket *snap = packetqueue.get_packet(analysis_it);
|
ZMLockedPacket *packet_lock = packetqueue.get_packet(analysis_it);
|
||||||
if ( !snap ) return false;
|
if (!packet_lock) return false;
|
||||||
|
ZMPacket *snap = packet_lock->packet_;
|
||||||
|
|
||||||
// Is it possible for snap->score to be ! -1 ? Not if everything is working correctly
|
// Is it possible for snap->score to be ! -1 ? Not if everything is working correctly
|
||||||
if ( snap->score != -1 ) {
|
if (snap->score != -1) {
|
||||||
snap->unlock();
|
delete packet_lock;
|
||||||
packetqueue.increment_it(analysis_it);
|
packetqueue.increment_it(analysis_it);
|
||||||
Error("skipping because score was %d", snap->score);
|
Error("skipping because score was %d", snap->score);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1837,25 +1828,24 @@ bool Monitor::Analyse() {
|
||||||
std::lock_guard<std::mutex> lck(event_mutex);
|
std::lock_guard<std::mutex> lck(event_mutex);
|
||||||
|
|
||||||
// if we have been told to be OFF, then we are off and don't do any processing.
|
// if we have been told to be OFF, then we are off and don't do any processing.
|
||||||
if ( trigger_data->trigger_state != TriggerState::TRIGGER_OFF ) {
|
if (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) {
|
||||||
Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state));
|
Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state));
|
||||||
int score = 0;
|
int score = 0;
|
||||||
// Ready means that we have captured the warmup # of frames
|
// Ready means that we have captured the warmup # of frames
|
||||||
if ( !Ready() ) {
|
if (!Ready()) {
|
||||||
Debug(3, "Not ready?");
|
Debug(3, "Not ready?");
|
||||||
snap->unlock();
|
delete packet_lock;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(4, "Ready");
|
|
||||||
std::string cause;
|
std::string cause;
|
||||||
Event::StringSetMap noteSetMap;
|
Event::StringSetMap noteSetMap;
|
||||||
|
|
||||||
// Specifically told to be on. Setting the score here will trigger the alarm.
|
// Specifically told to be on. Setting the score here will trigger the alarm.
|
||||||
if ( trigger_data->trigger_state == TriggerState::TRIGGER_ON ) {
|
if (trigger_data->trigger_state == TriggerState::TRIGGER_ON) {
|
||||||
score += trigger_data->trigger_score;
|
score += trigger_data->trigger_score;
|
||||||
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
|
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
|
||||||
if ( !event ) {
|
if (!event) {
|
||||||
cause += trigger_data->trigger_cause;
|
cause += trigger_data->trigger_cause;
|
||||||
}
|
}
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
|
@ -1863,12 +1853,13 @@ bool Monitor::Analyse() {
|
||||||
noteSetMap[trigger_data->trigger_cause] = noteSet;
|
noteSetMap[trigger_data->trigger_cause] = noteSet;
|
||||||
} // end if trigger_on
|
} // end if trigger_on
|
||||||
|
|
||||||
if ( signal_change ) {
|
// FIXME this snap might not be the one that caused the signal change. Need to store that in the packet.
|
||||||
|
if (signal_change) {
|
||||||
Debug(2, "Signal change, new signal is %d", signal);
|
Debug(2, "Signal change, new signal is %d", signal);
|
||||||
const char *signalText = "Unknown";
|
const char *signalText = "Unknown";
|
||||||
if ( !signal ) {
|
if (!signal) {
|
||||||
signalText = "Lost";
|
signalText = "Lost";
|
||||||
if ( event ) {
|
if (event) {
|
||||||
Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, analysis_image_count, event->Id());
|
Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, analysis_image_count, event->Id());
|
||||||
closeEvent();
|
closeEvent();
|
||||||
last_section_mod = 0;
|
last_section_mod = 0;
|
||||||
|
@ -1877,9 +1868,8 @@ bool Monitor::Analyse() {
|
||||||
signalText = "Reacquired";
|
signalText = "Reacquired";
|
||||||
score += 100;
|
score += 100;
|
||||||
}
|
}
|
||||||
if ( !event ) {
|
if (!event) {
|
||||||
if ( cause.length() )
|
if (cause.length()) cause += ", ";
|
||||||
cause += ", ";
|
|
||||||
cause += SIGNAL_CAUSE;
|
cause += SIGNAL_CAUSE;
|
||||||
}
|
}
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
|
@ -1887,64 +1877,25 @@ bool Monitor::Analyse() {
|
||||||
noteSetMap[SIGNAL_CAUSE] = noteSet;
|
noteSetMap[SIGNAL_CAUSE] = noteSet;
|
||||||
shared_data->state = state = IDLE;
|
shared_data->state = state = IDLE;
|
||||||
shared_data->active = signal;
|
shared_data->active = signal;
|
||||||
if ( (function == MODECT or function == MOCORD) and snap->image )
|
if ((function == MODECT or function == MOCORD) and snap->image)
|
||||||
ref_image.Assign(*(snap->image));
|
ref_image.Assign(*(snap->image));
|
||||||
}// else
|
}// else
|
||||||
|
|
||||||
if (signal) {
|
if (signal) {
|
||||||
if (snap->image or (snap->codec_type == AVMEDIA_TYPE_VIDEO)) {
|
if (snap->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
struct timeval *timestamp = snap->timestamp;
|
|
||||||
|
|
||||||
if ( Active() and (function == MODECT or function == MOCORD) and snap->image ) {
|
|
||||||
Debug(3, "signal and active and modect");
|
|
||||||
Event::StringSet zoneSet;
|
|
||||||
|
|
||||||
int motion_score = last_motion_score;
|
|
||||||
|
|
||||||
if ( analysis_fps_limit ) {
|
|
||||||
double capture_fps = get_capture_fps();
|
|
||||||
motion_frame_skip = capture_fps / analysis_fps_limit;
|
|
||||||
Debug(1, "Recalculating motion_frame_skip (%d) = capture_fps(%f) / analysis_fps(%f)",
|
|
||||||
motion_frame_skip, capture_fps, analysis_fps_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !(analysis_image_count % (motion_frame_skip+1)) ) {
|
|
||||||
if ( snap->image ) {
|
|
||||||
// Get new score.
|
|
||||||
motion_score = DetectMotion(*(snap->image), zoneSet);
|
|
||||||
|
|
||||||
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
|
|
||||||
score, last_motion_score, motion_score);
|
|
||||||
} else {
|
|
||||||
Warning("No image in snap");
|
|
||||||
}
|
|
||||||
// Why are we updating the last_motion_score too?
|
|
||||||
last_motion_score = motion_score;
|
|
||||||
motion_frame_count += 1;
|
|
||||||
} else {
|
|
||||||
Debug(1, "Skipped motion detection");
|
|
||||||
}
|
|
||||||
if (motion_score) {
|
|
||||||
score += motion_score;
|
|
||||||
if (cause.length()) cause += ", ";
|
|
||||||
cause += MOTION_CAUSE;
|
|
||||||
noteSetMap[MOTION_CAUSE] = zoneSet;
|
|
||||||
} // end if motion_score
|
|
||||||
} // end if active and doing motion detection
|
|
||||||
|
|
||||||
// Check to see if linked monitors are triggering.
|
// Check to see if linked monitors are triggering.
|
||||||
if (n_linked_monitors > 0) {
|
if (n_linked_monitors > 0) {
|
||||||
Debug(4, "Checking linked monitors");
|
Debug(1, "Checking linked monitors");
|
||||||
// FIXME improve logic here
|
// FIXME improve logic here
|
||||||
bool first_link = true;
|
bool first_link = true;
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
for ( int i = 0; i < n_linked_monitors; i++ ) {
|
for ( int i = 0; i < n_linked_monitors; i++ ) {
|
||||||
// TODO: Shouldn't we try to connect?
|
// TODO: Shouldn't we try to connect?
|
||||||
if ( linked_monitors[i]->isConnected() ) {
|
if ( linked_monitors[i]->isConnected() ) {
|
||||||
Debug(4, "Linked monitor %d %s is connected",
|
Debug(1, "Linked monitor %d %s is connected",
|
||||||
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
||||||
if ( linked_monitors[i]->hasAlarmed() ) {
|
if ( linked_monitors[i]->hasAlarmed() ) {
|
||||||
Debug(4, "Linked monitor %d %s is alarmed",
|
Debug(1, "Linked monitor %d %s is alarmed",
|
||||||
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
||||||
if ( !event ) {
|
if ( !event ) {
|
||||||
if ( first_link ) {
|
if ( first_link ) {
|
||||||
|
@ -1957,7 +1908,7 @@ bool Monitor::Analyse() {
|
||||||
noteSet.insert(linked_monitors[i]->Name());
|
noteSet.insert(linked_monitors[i]->Name());
|
||||||
score += linked_monitors[i]->lastFrameScore(); // 50;
|
score += linked_monitors[i]->lastFrameScore(); // 50;
|
||||||
} else {
|
} else {
|
||||||
Debug(4, "Linked monitor %d %s is not alarmed",
|
Debug(1, "Linked monitor %d %s is not alarmed",
|
||||||
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
linked_monitors[i]->Id(), linked_monitors[i]->Name());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1969,16 +1920,72 @@ bool Monitor::Analyse() {
|
||||||
noteSetMap[LINKED_CAUSE] = noteSet;
|
noteSetMap[LINKED_CAUSE] = noteSet;
|
||||||
} // end if linked_monitors
|
} // end if linked_monitors
|
||||||
|
|
||||||
|
if ( decoding_enabled ) {
|
||||||
|
while (!snap->image and !snap->decoded and !zm_terminate) {
|
||||||
|
// Need to wait for the decoder thread.
|
||||||
|
Debug(1, "Waiting for decode");
|
||||||
|
packet_lock->wait();
|
||||||
|
if (!snap->image and snap->decoded) {
|
||||||
|
Debug(1, "No image but was decoded, giving up");
|
||||||
|
delete packet_lock;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // end while ! decoded
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval *timestamp = snap->timestamp;
|
||||||
|
|
||||||
|
if (Active() and (function == MODECT or function == MOCORD)) {
|
||||||
|
Debug(3, "signal and active and modect");
|
||||||
|
Event::StringSet zoneSet;
|
||||||
|
|
||||||
|
int motion_score = last_motion_score;
|
||||||
|
|
||||||
|
if ( analysis_fps_limit ) {
|
||||||
|
double capture_fps = get_capture_fps();
|
||||||
|
motion_frame_skip = capture_fps / analysis_fps_limit;
|
||||||
|
Debug(1, "Recalculating motion_frame_skip (%d) = capture_fps(%f) / analysis_fps(%f)",
|
||||||
|
motion_frame_skip, capture_fps, analysis_fps_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(analysis_image_count % (motion_frame_skip+1))) {
|
||||||
|
if (snap->image) {
|
||||||
|
// Get new score.
|
||||||
|
motion_score = DetectMotion(*(snap->image), zoneSet);
|
||||||
|
|
||||||
|
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
|
||||||
|
score, last_motion_score, motion_score);
|
||||||
|
motion_frame_count += 1;
|
||||||
|
} else {
|
||||||
|
Debug(1, "No image in snap, codec likely not ready");
|
||||||
|
}
|
||||||
|
// Why are we updating the last_motion_score too?
|
||||||
|
last_motion_score = motion_score;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Skipped motion detection");
|
||||||
|
}
|
||||||
|
if (motion_score) {
|
||||||
|
score += motion_score;
|
||||||
|
if (cause.length()) cause += ", ";
|
||||||
|
cause += MOTION_CAUSE;
|
||||||
|
noteSetMap[MOTION_CAUSE] = zoneSet;
|
||||||
|
} // end if motion_score
|
||||||
|
} // end if active and doing motion detection
|
||||||
|
|
||||||
if (function == RECORD or function == MOCORD) {
|
if (function == RECORD or function == MOCORD) {
|
||||||
// If doing record, check to see if we need to close the event or not.
|
// If doing record, check to see if we need to close the event or not.
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
Debug(2, "Have event %" PRIu64 " in mocord", event->Id());
|
Debug(2, "Have event %" PRIu64 " in mocord", event->Id());
|
||||||
if (section_length
|
|
||||||
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
|
|
||||||
&& ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) )
|
|
||||||
) {
|
|
||||||
|
|
||||||
|
if (section_length &&
|
||||||
|
( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
|
||||||
|
&& (
|
||||||
|
( (function == MOCORD) && (event_close_mode != CLOSE_TIME) )
|
||||||
|
||
|
||||||
|
( (function == RECORD) && (event_close_mode == CLOSE_TIME) )
|
||||||
|
|| ! ( timestamp->tv_sec % section_length )
|
||||||
|
)
|
||||||
|
) {
|
||||||
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d",
|
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d",
|
||||||
name, image_count, event->Id(),
|
name, image_count, event->Id(),
|
||||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||||
|
@ -1999,22 +2006,33 @@ bool Monitor::Analyse() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// This gets a lock on the starting packet
|
// This gets a lock on the starting packet
|
||||||
ZMPacket *starting_packet = packetqueue.get_packet(start_it);
|
|
||||||
|
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||||
|
ZMPacket *starting_packet = nullptr;
|
||||||
|
if ( *start_it != snap_it ) {
|
||||||
|
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||||
|
if (!starting_packet_lock) return false;
|
||||||
|
starting_packet = starting_packet_lock->packet_;
|
||||||
|
} else {
|
||||||
|
starting_packet = snap;
|
||||||
|
}
|
||||||
|
|
||||||
event = new Event(this, *(starting_packet->timestamp), "Continuous", noteSetMap);
|
event = new Event(this, *(starting_packet->timestamp), "Continuous", noteSetMap);
|
||||||
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||||
while ( starting_packet and (*start_it) != snap_it ) {
|
while ( starting_packet and ((*start_it) != snap_it) ) {
|
||||||
event->AddPacket(starting_packet);
|
event->AddPacket(starting_packet);
|
||||||
// Have added the packet, don't want to unlock it until we have locked the next
|
// Have added the packet, don't want to unlock it until we have locked the next
|
||||||
|
|
||||||
packetqueue.increment_it(start_it);
|
packetqueue.increment_it(start_it);
|
||||||
if ( (*start_it) == snap_it ) {
|
if ( (*start_it) == snap_it ) {
|
||||||
starting_packet->unlock();
|
if (starting_packet_lock) delete starting_packet_lock;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ZMPacket *p = packetqueue.get_packet(start_it);
|
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||||
starting_packet->unlock();
|
delete starting_packet_lock;
|
||||||
starting_packet = p;
|
if (!lp) return false;
|
||||||
|
starting_packet_lock = lp;
|
||||||
|
starting_packet = lp->packet_;
|
||||||
}
|
}
|
||||||
packetqueue.free_it(start_it);
|
packetqueue.free_it(start_it);
|
||||||
delete start_it;
|
delete start_it;
|
||||||
|
@ -2030,9 +2048,7 @@ bool Monitor::Analyse() {
|
||||||
for ( int i=0; i < n_zones; i++ ) {
|
for ( int i=0; i < n_zones; i++ ) {
|
||||||
if ( zones[i]->Alarmed() ) {
|
if ( zones[i]->Alarmed() ) {
|
||||||
alarm_cause += std::string(zones[i]->Label());
|
alarm_cause += std::string(zones[i]->Label());
|
||||||
if ( i < n_zones-1 ) {
|
if (i < n_zones-1) alarm_cause += ",";
|
||||||
alarm_cause += ",";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
alarm_cause = cause+" "+alarm_cause;
|
alarm_cause = cause+" "+alarm_cause;
|
||||||
|
@ -2085,7 +2101,16 @@ bool Monitor::Analyse() {
|
||||||
snap_it,
|
snap_it,
|
||||||
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
|
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
|
||||||
);
|
);
|
||||||
ZMPacket *starting_packet = *(*start_it);
|
|
||||||
|
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||||
|
ZMPacket *starting_packet = nullptr;
|
||||||
|
if ( *start_it != snap_it ) {
|
||||||
|
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||||
|
if (!starting_packet_lock) return false;
|
||||||
|
starting_packet = starting_packet_lock->packet_;
|
||||||
|
} else {
|
||||||
|
starting_packet = snap;
|
||||||
|
}
|
||||||
|
|
||||||
event = new Event(this, *(starting_packet->timestamp), cause, noteSetMap);
|
event = new Event(this, *(starting_packet->timestamp), cause, noteSetMap);
|
||||||
shared_data->last_event_id = event->Id();
|
shared_data->last_event_id = event->Id();
|
||||||
|
@ -2099,12 +2124,18 @@ bool Monitor::Analyse() {
|
||||||
|
|
||||||
packetqueue.increment_it(start_it);
|
packetqueue.increment_it(start_it);
|
||||||
if ( (*start_it) == snap_it ) {
|
if ( (*start_it) == snap_it ) {
|
||||||
starting_packet->unlock();
|
if (starting_packet_lock) delete starting_packet_lock;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ZMPacket *p = packetqueue.get_packet(start_it);
|
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||||
starting_packet->unlock();
|
delete starting_packet_lock;
|
||||||
starting_packet = p;
|
if (!lp) {
|
||||||
|
// Shutting down event will be closed by ~Monitor()
|
||||||
|
// Perhaps we shouldn't do this.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
starting_packet_lock = lp;
|
||||||
|
starting_packet = lp->packet_;
|
||||||
}
|
}
|
||||||
packetqueue.free_it(start_it);
|
packetqueue.free_it(start_it);
|
||||||
delete start_it;
|
delete start_it;
|
||||||
|
@ -2138,7 +2169,7 @@ bool Monitor::Analyse() {
|
||||||
Debug(1, "Staying in %s", State_Strings[state].c_str());
|
Debug(1, "Staying in %s", State_Strings[state].c_str());
|
||||||
|
|
||||||
}
|
}
|
||||||
if ( state == ALARM ) {
|
if (state == ALARM) {
|
||||||
last_alarm_count = analysis_image_count;
|
last_alarm_count = analysis_image_count;
|
||||||
} // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT
|
} // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT
|
||||||
} else { // no score?
|
} else { // no score?
|
||||||
|
@ -2146,7 +2177,7 @@ bool Monitor::Analyse() {
|
||||||
if (state == ALARM) {
|
if (state == ALARM) {
|
||||||
Info("%s: %03d - Gone into alert state", name, analysis_image_count);
|
Info("%s: %03d - Gone into alert state", name, analysis_image_count);
|
||||||
shared_data->state = state = ALERT;
|
shared_data->state = state = ALERT;
|
||||||
} else if ( state == ALERT ) {
|
} else if (state == ALERT) {
|
||||||
if (
|
if (
|
||||||
( analysis_image_count-last_alarm_count > post_event_count )
|
( analysis_image_count-last_alarm_count > post_event_count )
|
||||||
&&
|
&&
|
||||||
|
@ -2164,7 +2195,7 @@ bool Monitor::Analyse() {
|
||||||
shared_data->state = state = TAPE;
|
shared_data->state = state = TAPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( state == PREALARM ) {
|
} else if (state == PREALARM) {
|
||||||
// Back to IDLE
|
// Back to IDLE
|
||||||
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
|
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2172,19 +2203,19 @@ bool Monitor::Analyse() {
|
||||||
State_Strings[state].c_str(), analysis_image_count, last_alarm_count, post_event_count,
|
State_Strings[state].c_str(), analysis_image_count, last_alarm_count, post_event_count,
|
||||||
timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length);
|
timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length);
|
||||||
}
|
}
|
||||||
if ( Event::PreAlarmCount() )
|
if (Event::PreAlarmCount())
|
||||||
Event::EmptyPreAlarmFrames();
|
Event::EmptyPreAlarmFrames();
|
||||||
} // end if score or not
|
} // end if score or not
|
||||||
|
|
||||||
snap->score = score;
|
snap->score = score;
|
||||||
|
|
||||||
if ( state == PREALARM ) {
|
if (state == PREALARM) {
|
||||||
// Generate analysis images if necessary
|
// Generate analysis images if necessary
|
||||||
if ( (savejpegs > 1) and snap->image ) {
|
if ((savejpegs > 1) and snap->image) {
|
||||||
for ( int i = 0; i < n_zones; i++ ) {
|
for (int i = 0; i < n_zones; i++) {
|
||||||
if ( zones[i]->Alarmed() ) {
|
if (zones[i]->Alarmed()) {
|
||||||
if ( zones[i]->AlarmImage() ) {
|
if (zones[i]->AlarmImage()) {
|
||||||
if ( ! snap->analysis_image )
|
if (!snap->analysis_image)
|
||||||
snap->analysis_image = new Image(*(snap->image));
|
snap->analysis_image = new Image(*(snap->image));
|
||||||
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
|
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
|
||||||
}
|
}
|
||||||
|
@ -2195,38 +2226,42 @@ bool Monitor::Analyse() {
|
||||||
// incremement pre alarm image count
|
// incremement pre alarm image count
|
||||||
//have_pre_alarmed_frames ++;
|
//have_pre_alarmed_frames ++;
|
||||||
Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr);
|
Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr);
|
||||||
} else if ( state == ALARM ) {
|
} else if (state == ALARM) {
|
||||||
if ( ( savejpegs > 1 ) and snap->image ) {
|
if ((savejpegs > 1 ) and snap->image) {
|
||||||
for ( int i = 0; i < n_zones; i++ ) {
|
for (int i = 0; i < n_zones; i++) {
|
||||||
if ( zones[i]->Alarmed() ) {
|
if (zones[i]->Alarmed()) {
|
||||||
if ( zones[i]->AlarmImage() ) {
|
if (zones[i]->AlarmImage()) {
|
||||||
if ( ! snap->analysis_image )
|
if (!snap->analysis_image)
|
||||||
snap->analysis_image = new Image(*(snap->image));
|
snap->analysis_image = new Image(*(snap->image));
|
||||||
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
|
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
|
||||||
}
|
}
|
||||||
if ( config.record_event_stats )
|
if (config.record_event_stats)
|
||||||
zones[i]->RecordStats(event);
|
zones[i]->RecordStats(event);
|
||||||
} // end if zone is alarmed
|
} // end if zone is alarmed
|
||||||
} // end foreach zone
|
} // end foreach zone
|
||||||
}
|
}
|
||||||
if ( noteSetMap.size() > 0 )
|
if (event) {
|
||||||
event->updateNotes(noteSetMap);
|
if (noteSetMap.size() > 0)
|
||||||
if ( section_length
|
event->updateNotes(noteSetMap);
|
||||||
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
|
if ( section_length
|
||||||
&& ! (image_count % fps_report_interval)
|
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
|
||||||
) {
|
&& ! (image_count % fps_report_interval)
|
||||||
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %d - %d = %d >= %d",
|
) {
|
||||||
name, image_count, event->Id(),
|
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %d - %d = %d >= %d",
|
||||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
name, image_count, event->Id(),
|
||||||
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||||
section_length
|
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||||
);
|
section_length
|
||||||
closeEvent();
|
);
|
||||||
event = new Event(this, *timestamp, cause, noteSetMap);
|
closeEvent();
|
||||||
shared_data->last_event_id = event->Id();
|
event = new Event(this, *timestamp, cause, noteSetMap);
|
||||||
//set up video store data
|
shared_data->last_event_id = event->Id();
|
||||||
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
//set up video store data
|
||||||
video_store_data->recording = event->StartTime();
|
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||||
|
video_store_data->recording = event->StartTime();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error("ALARM but no event");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ( state == ALERT ) {
|
} else if ( state == ALERT ) {
|
||||||
|
@ -2255,27 +2290,18 @@ bool Monitor::Analyse() {
|
||||||
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
||||||
|
|
||||||
if (event) event->AddPacket(snap);
|
if (event) event->AddPacket(snap);
|
||||||
#if 0
|
|
||||||
if (snap->packet.stream_index == video_stream_id) {
|
// In the case where people have pre-alarm frames, the web ui will generate the frame images
|
||||||
if (video_fifo) {
|
// from the mp4. So no one will notice anyways.
|
||||||
if ( snap->keyframe ) {
|
if (snap->image and (videowriter == PASSTHROUGH) and !savejpegs) {
|
||||||
// avcodec strips out important nals that describe the stream and
|
Debug(1, "Deleting image data for %d", snap->image_index);
|
||||||
// stick them in extradata. Need to send them along with keyframes
|
// Don't need raw images anymore
|
||||||
AVStream *stream = camera->getVideoStream();
|
delete snap->image;
|
||||||
video_fifo->write(
|
snap->image = nullptr;
|
||||||
static_cast<unsigned char *>(stream->codecpar->extradata),
|
|
||||||
stream->codecpar->extradata_size);
|
|
||||||
}
|
|
||||||
video_fifo->writePacket(*snap);
|
|
||||||
}
|
|
||||||
} else if (snap->packet.stream_index == audio_stream_id) {
|
|
||||||
if (audio_fifo)
|
|
||||||
audio_fifo->writePacket(*snap);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// popPacket will have placed a second lock on snap, so release it here.
|
// popPacket will have placed a second lock on snap, so release it here.
|
||||||
snap->unlock();
|
delete packet_lock;
|
||||||
|
|
||||||
if ( snap->image_index > 0 ) {
|
if ( snap->image_index > 0 ) {
|
||||||
// Only do these if it's a video packet.
|
// Only do these if it's a video packet.
|
||||||
|
@ -2507,7 +2533,6 @@ int Monitor::Capture() {
|
||||||
gettimeofday(packet->timestamp, nullptr);
|
gettimeofday(packet->timestamp, nullptr);
|
||||||
shared_data->zmc_heartbeat_time = packet->timestamp->tv_sec;
|
shared_data->zmc_heartbeat_time = packet->timestamp->tv_sec;
|
||||||
|
|
||||||
Image* capture_image = image_buffer[index].image;
|
|
||||||
int captureResult = 0;
|
int captureResult = 0;
|
||||||
|
|
||||||
if ( deinterlacing_value == 4 ) {
|
if ( deinterlacing_value == 4 ) {
|
||||||
|
@ -2526,7 +2551,7 @@ int Monitor::Capture() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
captureResult = camera->Capture(*packet);
|
captureResult = camera->Capture(*packet);
|
||||||
Debug(4, "Back from capture result=%d image %d", captureResult, image_count);
|
Debug(4, "Back from capture result=%d image count %d", captureResult, image_count);
|
||||||
|
|
||||||
if ( captureResult < 0 ) {
|
if ( captureResult < 0 ) {
|
||||||
Debug(2, "failed capture");
|
Debug(2, "failed capture");
|
||||||
|
@ -2536,7 +2561,7 @@ int Monitor::Capture() {
|
||||||
Rgb signalcolor;
|
Rgb signalcolor;
|
||||||
/* HTML colour code is actually BGR in memory, we want RGB */
|
/* HTML colour code is actually BGR in memory, we want RGB */
|
||||||
signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR);
|
signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR);
|
||||||
capture_image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
|
Image *capture_image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
|
||||||
capture_image->Fill(signalcolor);
|
capture_image->Fill(signalcolor);
|
||||||
shared_data->signal = false;
|
shared_data->signal = false;
|
||||||
shared_data->last_write_index = index;
|
shared_data->last_write_index = index;
|
||||||
|
@ -2550,6 +2575,12 @@ int Monitor::Capture() {
|
||||||
// Don't want to do analysis on it, but we won't due to signal
|
// Don't want to do analysis on it, but we won't due to signal
|
||||||
return -1;
|
return -1;
|
||||||
} else if ( captureResult > 0 ) {
|
} else if ( captureResult > 0 ) {
|
||||||
|
// If we captured, let's assume signal, Decode will detect further
|
||||||
|
if (!decoding_enabled) {
|
||||||
|
shared_data->last_write_index = index;
|
||||||
|
shared_data->last_write_time = packet->timestamp->tv_sec;
|
||||||
|
shared_data->signal = true;
|
||||||
|
}
|
||||||
Debug(2, "Have packet stream_index:%d ?= videostream_id:(%d) q.vpktcount(%d) event?(%d) ",
|
Debug(2, "Have packet stream_index:%d ?= videostream_id:(%d) q.vpktcount(%d) event?(%d) ",
|
||||||
packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ) );
|
packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ) );
|
||||||
|
|
||||||
|
@ -2592,6 +2623,7 @@ int Monitor::Capture() {
|
||||||
return 1;
|
return 1;
|
||||||
} // end if audio
|
} // end if audio
|
||||||
|
|
||||||
|
#if 0
|
||||||
if ( !packet->image ) {
|
if ( !packet->image ) {
|
||||||
if ( packet->packet.size and !packet->in_frame ) {
|
if ( packet->packet.size and !packet->in_frame ) {
|
||||||
if ( !decoding_enabled ) {
|
if ( !decoding_enabled ) {
|
||||||
|
@ -2678,6 +2710,7 @@ int Monitor::Capture() {
|
||||||
shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true;
|
shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true;
|
||||||
shared_data->last_write_index = index;
|
shared_data->last_write_index = index;
|
||||||
shared_data->last_write_time = packet->timestamp->tv_sec;
|
shared_data->last_write_time = packet->timestamp->tv_sec;
|
||||||
|
#endif
|
||||||
image_count++;
|
image_count++;
|
||||||
|
|
||||||
// Will only be queued if there are iterators allocated in the queue.
|
// Will only be queued if there are iterators allocated in the queue.
|
||||||
|
@ -2710,13 +2743,108 @@ int Monitor::Capture() {
|
||||||
return captureResult;
|
return captureResult;
|
||||||
} // end Monitor::Capture
|
} // end Monitor::Capture
|
||||||
|
|
||||||
|
bool Monitor::Decode() {
|
||||||
|
|
||||||
|
ZMLockedPacket *packet_lock = packetqueue.get_packet(decoder_it);
|
||||||
|
if (!packet_lock) return false;
|
||||||
|
ZMPacket *packet = packet_lock->packet_;
|
||||||
|
packetqueue.increment_it(decoder_it);
|
||||||
|
if (packet->codec_type != AVMEDIA_TYPE_VIDEO) {
|
||||||
|
delete packet_lock;
|
||||||
|
return true; // Don't need decode
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
if ((!packet->image) and packet->packet.size and !packet->in_frame) {
|
||||||
|
// Allocate the image first so that it can be used by hwaccel
|
||||||
|
// We don't actually care about camera colours, pixel order etc. We care about the desired settings
|
||||||
|
//
|
||||||
|
//capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
|
||||||
|
ret = packet->decode(camera->getVideoCodecContext());
|
||||||
|
if (ret > 0) {
|
||||||
|
if (packet->in_frame and !packet->image) {
|
||||||
|
packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder());
|
||||||
|
packet->get_image();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(1, "No packet.size(%d) or packet->in_frame(%p). Not decoding", packet->packet.size, packet->in_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Image* capture_image = nullptr;
|
||||||
|
unsigned int index = image_count % image_buffer_count;
|
||||||
|
|
||||||
|
if (packet->image) {
|
||||||
|
capture_image = packet->image;
|
||||||
|
|
||||||
|
/* Deinterlacing */
|
||||||
|
if ( deinterlacing_value ) {
|
||||||
|
if ( deinterlacing_value == 1 ) {
|
||||||
|
capture_image->Deinterlace_Discard();
|
||||||
|
} else if ( deinterlacing_value == 2 ) {
|
||||||
|
capture_image->Deinterlace_Linear();
|
||||||
|
} else if ( deinterlacing_value == 3 ) {
|
||||||
|
capture_image->Deinterlace_Blend();
|
||||||
|
} else if ( deinterlacing_value == 4 ) {
|
||||||
|
capture_image->Deinterlace_4Field(next_buffer.image, (deinterlacing>>8)&0xff);
|
||||||
|
} else if ( deinterlacing_value == 5 ) {
|
||||||
|
capture_image->Deinterlace_Blend_CustomRatio((deinterlacing>>8)&0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( orientation != ROTATE_0 ) {
|
||||||
|
Debug(2, "Doing rotation");
|
||||||
|
switch ( orientation ) {
|
||||||
|
case ROTATE_0 :
|
||||||
|
// No action required
|
||||||
|
break;
|
||||||
|
case ROTATE_90 :
|
||||||
|
case ROTATE_180 :
|
||||||
|
case ROTATE_270 :
|
||||||
|
capture_image->Rotate((orientation-1)*90);
|
||||||
|
break;
|
||||||
|
case FLIP_HORI :
|
||||||
|
case FLIP_VERT :
|
||||||
|
capture_image->Flip(orientation==FLIP_HORI);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // end if have rotation
|
||||||
|
|
||||||
|
if (privacy_bitmask) {
|
||||||
|
Debug(1, "Applying privacy");
|
||||||
|
capture_image->MaskPrivacy(privacy_bitmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.timestamp_on_capture) {
|
||||||
|
Debug(1, "Timestampprivacy");
|
||||||
|
TimestampImage(packet->image, packet->timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ref_image.Buffer()) {
|
||||||
|
// First image, so assign it to ref image
|
||||||
|
Debug(1, "Assigning ref image %dx%d size: %d", width, height, camera->ImageSize());
|
||||||
|
ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(),
|
||||||
|
packet->image->Buffer(), camera->ImageSize());
|
||||||
|
}
|
||||||
|
image_buffer[index].image->Assign(*(packet->image));
|
||||||
|
*(image_buffer[index].timestamp) = *(packet->timestamp);
|
||||||
|
} // end if have image
|
||||||
|
packet->decoded = true;
|
||||||
|
|
||||||
|
shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true;
|
||||||
|
shared_data->last_write_index = index;
|
||||||
|
shared_data->last_write_time = packet->timestamp->tv_sec;
|
||||||
|
delete packet_lock;
|
||||||
|
return true;
|
||||||
|
} // end bool Monitor::Decode()
|
||||||
|
|
||||||
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] )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Expand the strftime macros first
|
// Expand the strftime macros first
|
||||||
char label_time_text[256];
|
char label_time_text[256];
|
||||||
strftime(label_time_text, sizeof(label_time_text), label_format, localtime(&ts_time->tv_sec));
|
tm ts_tm = {};
|
||||||
|
strftime(label_time_text, sizeof(label_time_text), label_format, localtime_r(&ts_time->tv_sec, &ts_tm));
|
||||||
char label_text[1024];
|
char label_text[1024];
|
||||||
const char *s_ptr = label_time_text;
|
const char *s_ptr = label_time_text;
|
||||||
char *d_ptr = label_text;
|
char *d_ptr = label_text;
|
||||||
|
@ -3026,6 +3154,18 @@ int Monitor::PrimeCapture() {
|
||||||
audio_fifo = new Fifo(shared_data->audio_fifo_path, true);
|
audio_fifo = new Fifo(shared_data->audio_fifo_path, true);
|
||||||
}
|
}
|
||||||
} // end if rtsp_server
|
} // end if rtsp_server
|
||||||
|
|
||||||
|
if (decoding_enabled) {
|
||||||
|
if (!decoder_it) decoder_it = packetqueue.get_video_it(false);
|
||||||
|
if (!decoder) decoder = new DecoderThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!analysis_it) analysis_it = packetqueue.get_video_it(false);
|
||||||
|
if (!analysis_thread) {
|
||||||
|
Debug(1, "Starting an analysis thread for monitor (%d)", id);
|
||||||
|
analysis_thread = new AnalysisThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Failed to prime %d", ret);
|
Debug(2, "Failed to prime %d", ret);
|
||||||
}
|
}
|
||||||
|
@ -3035,13 +3175,24 @@ int Monitor::PrimeCapture() {
|
||||||
int Monitor::PreCapture() const { return camera->PreCapture(); }
|
int Monitor::PreCapture() const { return camera->PreCapture(); }
|
||||||
int Monitor::PostCapture() const { return camera->PostCapture(); }
|
int Monitor::PostCapture() const { return camera->PostCapture(); }
|
||||||
int Monitor::Close() {
|
int Monitor::Close() {
|
||||||
|
// Because the stream indexes may change we have to clear out the packetqueue
|
||||||
|
if (decoder) decoder->Stop();
|
||||||
|
if (analysis_thread) analysis_thread->Stop();
|
||||||
|
packetqueue.clear();
|
||||||
|
if (decoder) {
|
||||||
|
delete decoder;
|
||||||
|
decoder = nullptr;
|
||||||
|
}
|
||||||
|
if (analysis_thread) {
|
||||||
|
delete analysis_thread;
|
||||||
|
analysis_thread = nullptr;
|
||||||
|
}
|
||||||
std::lock_guard<std::mutex> lck(event_mutex);
|
std::lock_guard<std::mutex> lck(event_mutex);
|
||||||
if (event) {
|
if (event) {
|
||||||
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id());
|
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id());
|
||||||
closeEvent();
|
closeEvent();
|
||||||
}
|
}
|
||||||
if (camera) camera->Close();
|
if (camera) camera->Close();
|
||||||
packetqueue.clear();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3051,25 +3202,25 @@ Monitor::Orientation Monitor::getOrientation() const { return orientation; }
|
||||||
// So this should be done as the first task in the analysis thread startup.
|
// So this should be done as the first task in the analysis thread startup.
|
||||||
// This function is deprecated.
|
// This function is deprecated.
|
||||||
void Monitor::get_ref_image() {
|
void Monitor::get_ref_image() {
|
||||||
ZMPacket *snap = nullptr;
|
ZMLockedPacket *snap_lock = nullptr;
|
||||||
|
|
||||||
if ( !analysis_it )
|
if ( !analysis_it )
|
||||||
analysis_it = packetqueue.get_video_it(true);
|
analysis_it = packetqueue.get_video_it(true);
|
||||||
|
|
||||||
while (
|
while (
|
||||||
(
|
(
|
||||||
!( snap = packetqueue.get_packet(analysis_it))
|
!( snap_lock = packetqueue.get_packet(analysis_it))
|
||||||
or
|
or
|
||||||
( snap->codec_type != AVMEDIA_TYPE_VIDEO )
|
( snap_lock->packet_->codec_type != AVMEDIA_TYPE_VIDEO )
|
||||||
or
|
or
|
||||||
! snap->image
|
! snap_lock->packet_->image
|
||||||
)
|
)
|
||||||
and !zm_terminate) {
|
and !zm_terminate) {
|
||||||
|
|
||||||
Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)",
|
Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)",
|
||||||
shared_data->last_write_index, shared_data->last_write_time);
|
shared_data->last_write_index, shared_data->last_write_time);
|
||||||
if ( snap and ! snap->image ) {
|
if ( snap_lock and ! snap_lock->packet_->image ) {
|
||||||
snap->unlock();
|
delete snap_lock;
|
||||||
// can't analyse it anyways, incremement
|
// can't analyse it anyways, incremement
|
||||||
packetqueue.increment_it(analysis_it);
|
packetqueue.increment_it(analysis_it);
|
||||||
}
|
}
|
||||||
|
@ -3078,6 +3229,7 @@ void Monitor::get_ref_image() {
|
||||||
if ( zm_terminate )
|
if ( zm_terminate )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ZMPacket *snap = snap_lock->packet_;
|
||||||
Debug(1, "get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p",
|
Debug(1, "get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p",
|
||||||
snap->packet.stream_index, video_stream_id, snap->image_index, snap->image );
|
snap->packet.stream_index, video_stream_id, snap->image_index, snap->image );
|
||||||
// Might not have been decoded yet FIXME
|
// Might not have been decoded yet FIXME
|
||||||
|
@ -3088,7 +3240,7 @@ void Monitor::get_ref_image() {
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Have no ref image about to unlock");
|
Debug(2, "Have no ref image about to unlock");
|
||||||
}
|
}
|
||||||
snap->unlock();
|
delete snap_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Group *> Monitor::Groups() {
|
std::vector<Group *> Monitor::Groups() {
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
#include "zm_define.h"
|
#include "zm_define.h"
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
|
#include "zm_analysis_thread.h"
|
||||||
|
#include "zm_decoder_thread.h"
|
||||||
#include "zm_event.h"
|
#include "zm_event.h"
|
||||||
#include "zm_fifo.h"
|
#include "zm_fifo.h"
|
||||||
#include "zm_image.h"
|
#include "zm_image.h"
|
||||||
|
@ -375,7 +377,9 @@ protected:
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
PacketQueue packetqueue;
|
PacketQueue packetqueue;
|
||||||
packetqueue_iterator *analysis_it;
|
packetqueue_iterator *analysis_it;
|
||||||
|
AnalysisThread *analysis_thread;
|
||||||
|
packetqueue_iterator *decoder_it;
|
||||||
|
DecoderThread *decoder;
|
||||||
|
|
||||||
int n_zones;
|
int n_zones;
|
||||||
Zone **zones;
|
Zone **zones;
|
||||||
|
@ -411,6 +415,8 @@ public:
|
||||||
if ( shared_data && shared_data->valid ) {
|
if ( shared_data && shared_data->valid ) {
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday(&now, nullptr);
|
gettimeofday(&now, nullptr);
|
||||||
|
Debug(3, "Shared data is valid, checking heartbeat %u - %u = %d < %f",
|
||||||
|
now.tv_sec, shared_data->zmc_heartbeat_time, (now.tv_sec - shared_data->zmc_heartbeat_time), config.watch_max_delay);
|
||||||
if ((now.tv_sec - shared_data->zmc_heartbeat_time) < config.watch_max_delay)
|
if ((now.tv_sec - shared_data->zmc_heartbeat_time) < config.watch_max_delay)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -426,6 +432,7 @@ public:
|
||||||
}
|
}
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
inline CameraType GetType() const { return type; }
|
||||||
inline Function GetFunction() const { return function; }
|
inline Function GetFunction() const { return function; }
|
||||||
inline PacketQueue * GetPacketQueue() { return &packetqueue; }
|
inline PacketQueue * GetPacketQueue() { return &packetqueue; }
|
||||||
inline bool Enabled() const {
|
inline bool Enabled() const {
|
||||||
|
@ -438,10 +445,6 @@ public:
|
||||||
}
|
}
|
||||||
inline const char *EventPrefix() const { return event_prefix; }
|
inline const char *EventPrefix() const { return event_prefix; }
|
||||||
inline bool Ready() const {
|
inline bool Ready() const {
|
||||||
if ( function <= MONITOR ) {
|
|
||||||
Error("Should not be calling Ready if the function doesn't include motion detection");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( image_count >= ready_count ) {
|
if ( image_count >= ready_count ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -549,6 +552,7 @@ public:
|
||||||
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
||||||
bool CheckSignal( const Image *image );
|
bool CheckSignal( const Image *image );
|
||||||
bool Analyse();
|
bool Analyse();
|
||||||
|
bool Decode();
|
||||||
void DumpImage( Image *dump_image ) const;
|
void DumpImage( Image *dump_image ) const;
|
||||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||||
bool closeEvent();
|
bool closeEvent();
|
||||||
|
|
|
@ -467,28 +467,35 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
||||||
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
||||||
|
|
||||||
void MonitorStream::runStream() {
|
void MonitorStream::runStream() {
|
||||||
if ( type == STREAM_SINGLE ) {
|
if (type == STREAM_SINGLE) {
|
||||||
// Not yet migrated over to stream class
|
// Not yet migrated over to stream class
|
||||||
SingleImage(scale);
|
if (checkInitialised())
|
||||||
|
SingleImage(scale);
|
||||||
|
else
|
||||||
|
sendTextFrame("Unable to send image");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
openComms();
|
openComms();
|
||||||
|
|
||||||
if ( type == STREAM_JPEG )
|
if (type == STREAM_JPEG)
|
||||||
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
|
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
|
||||||
|
|
||||||
if ( !checkInitialised() ) {
|
while (!(loadMonitor(monitor_id) || zm_terminate)) {
|
||||||
Error("Not initialized");
|
sendTextFrame("Not connected");
|
||||||
while ( !(loadMonitor(monitor_id) || zm_terminate) ) {
|
if (connkey)
|
||||||
sendTextFrame("Not connected");
|
checkCommandQueue();
|
||||||
if ( connkey )
|
sleep(1);
|
||||||
checkCommandQueue();
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
if ( zm_terminate )
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
if (zm_terminate) return;
|
||||||
|
while (!checkInitialised() and !zm_terminate) {
|
||||||
|
sendTextFrame("Unable to stream");
|
||||||
|
if (connkey)
|
||||||
|
checkCommandQueue();
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
if (zm_terminate) return;
|
||||||
|
|
||||||
updateFrameRate(monitor->GetFPS());
|
updateFrameRate(monitor->GetFPS());
|
||||||
|
|
||||||
|
@ -562,7 +569,7 @@ void MonitorStream::runStream() {
|
||||||
capture_fps = capture_max_fps;
|
capture_fps = capture_max_fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( !zm_terminate ) {
|
while (!zm_terminate) {
|
||||||
bool got_command = false;
|
bool got_command = false;
|
||||||
if ( feof(stdout) ) {
|
if ( feof(stdout) ) {
|
||||||
Debug(2, "feof stdout");
|
Debug(2, "feof stdout");
|
||||||
|
@ -570,7 +577,7 @@ void MonitorStream::runStream() {
|
||||||
} else if ( ferror(stdout) ) {
|
} else if ( ferror(stdout) ) {
|
||||||
Debug(2, "ferror stdout");
|
Debug(2, "ferror stdout");
|
||||||
break;
|
break;
|
||||||
} else if ( !monitor->ShmValid() ) {
|
} else if (!monitor->ShmValid()) {
|
||||||
Debug(2, "monitor not valid.... maybe we should wait until it comes back.");
|
Debug(2, "monitor not valid.... maybe we should wait until it comes back.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -856,7 +863,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;
|
||||||
while ( monitor->shared_data->last_write_index >= monitor->image_buffer_count ) {
|
while ((monitor->shared_data->last_write_index >= monitor->image_buffer_count) and !zm_terminate) {
|
||||||
Debug(1, "Waiting for capture to begin");
|
Debug(1, "Waiting for capture to begin");
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ ZMPacket::ZMPacket() :
|
||||||
score(-1),
|
score(-1),
|
||||||
codec_type(AVMEDIA_TYPE_UNKNOWN),
|
codec_type(AVMEDIA_TYPE_UNKNOWN),
|
||||||
image_index(-1),
|
image_index(-1),
|
||||||
codec_imgsize(0)
|
codec_imgsize(0),
|
||||||
|
decoded(0)
|
||||||
{
|
{
|
||||||
av_init_packet(&packet);
|
av_init_packet(&packet);
|
||||||
packet.size = 0; // So we can detect whether it has been filled.
|
packet.size = 0; // So we can detect whether it has been filled.
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define ZM_PACKET_H
|
#define ZM_PACKET_H
|
||||||
|
|
||||||
#include "zm_logger.h"
|
#include "zm_logger.h"
|
||||||
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -36,7 +37,9 @@ class Image;
|
||||||
class ZMPacket {
|
class ZMPacket {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
std::recursive_mutex mutex;
|
std::mutex mutex_;
|
||||||
|
std::condition_variable condition_;
|
||||||
|
|
||||||
int keyframe;
|
int keyframe;
|
||||||
AVStream *stream; // Input stream
|
AVStream *stream; // Input stream
|
||||||
AVPacket packet; // Input packet, undecoded
|
AVPacket packet; // Input packet, undecoded
|
||||||
|
@ -51,6 +54,7 @@ class ZMPacket {
|
||||||
int image_index;
|
int image_index;
|
||||||
int codec_imgsize;
|
int codec_imgsize;
|
||||||
int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q
|
int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q
|
||||||
|
bool decoded;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AVPacket *av_packet() { return &packet; }
|
AVPacket *av_packet() { return &packet; }
|
||||||
|
@ -65,21 +69,45 @@ class ZMPacket {
|
||||||
explicit ZMPacket(ZMPacket &packet);
|
explicit ZMPacket(ZMPacket &packet);
|
||||||
ZMPacket();
|
ZMPacket();
|
||||||
~ZMPacket();
|
~ZMPacket();
|
||||||
void lock() {
|
|
||||||
Debug(4,"Locking packet %d", this->image_index);
|
AVFrame *get_out_frame(const AVCodecContext *ctx);
|
||||||
mutex.lock();
|
|
||||||
Debug(4,"packet %d locked", this->image_index);
|
|
||||||
};
|
|
||||||
bool trylock() {
|
|
||||||
Debug(4,"TryLocking packet %d", this->image_index);
|
|
||||||
return mutex.try_lock();
|
|
||||||
};
|
|
||||||
void unlock() {
|
|
||||||
Debug(4,"packet %d unlocked", this->image_index);
|
|
||||||
mutex.unlock();
|
|
||||||
};
|
|
||||||
AVFrame *get_out_frame( const AVCodecContext *ctx );
|
|
||||||
int get_codec_imgsize() { return codec_imgsize; };
|
int get_codec_imgsize() { return codec_imgsize; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ZMLockedPacket {
|
||||||
|
public:
|
||||||
|
ZMPacket *packet_;
|
||||||
|
std::unique_lock<std::mutex> lck_;
|
||||||
|
|
||||||
|
ZMLockedPacket(ZMPacket *p) :
|
||||||
|
packet_(p),
|
||||||
|
lck_(packet_->mutex_, std::defer_lock) {
|
||||||
|
}
|
||||||
|
~ZMLockedPacket() {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock() {
|
||||||
|
Debug(4, "locking packet %d", packet_->image_index);
|
||||||
|
lck_.lock();
|
||||||
|
Debug(4, "packet %d locked", packet_->image_index);
|
||||||
|
};
|
||||||
|
bool trylock() {
|
||||||
|
Debug(4, "TryLocking packet %d", packet_->image_index);
|
||||||
|
return lck_.try_lock();
|
||||||
|
};
|
||||||
|
void unlock() {
|
||||||
|
Debug(4, "packet %d unlocked", packet_->image_index);
|
||||||
|
lck_.unlock();
|
||||||
|
packet_->condition_.notify_all();
|
||||||
|
};
|
||||||
|
|
||||||
|
void wait() {
|
||||||
|
Debug(4, "packet %d waiting", packet_->image_index);
|
||||||
|
// We already have a lock, but it's a recursive mutex.. so this may be ok
|
||||||
|
packet_->condition_.wait(lck_);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* ZM_PACKET_H */
|
#endif /* ZM_PACKET_H */
|
||||||
|
|
|
@ -31,7 +31,8 @@ PacketQueue::PacketQueue():
|
||||||
max_video_packet_count(-1),
|
max_video_packet_count(-1),
|
||||||
max_stream_id(-1),
|
max_stream_id(-1),
|
||||||
packet_counts(nullptr),
|
packet_counts(nullptr),
|
||||||
deleting(false)
|
deleting(false),
|
||||||
|
keep_keyframes(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +57,11 @@ int PacketQueue::addStream() {
|
||||||
|
|
||||||
PacketQueue::~PacketQueue() {
|
PacketQueue::~PacketQueue() {
|
||||||
clear();
|
clear();
|
||||||
if ( packet_counts ) {
|
if (packet_counts) {
|
||||||
delete[] packet_counts;
|
delete[] packet_counts;
|
||||||
packet_counts = nullptr;
|
packet_counts = nullptr;
|
||||||
}
|
}
|
||||||
while ( !iterators.empty() ) {
|
while (!iterators.empty()) {
|
||||||
packetqueue_iterator *it = iterators.front();
|
packetqueue_iterator *it = iterators.front();
|
||||||
iterators.pop_front();
|
iterators.pop_front();
|
||||||
delete it;
|
delete it;
|
||||||
|
@ -87,7 +88,7 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) {
|
||||||
|
|
||||||
pktQueue.push_back(add_packet);
|
pktQueue.push_back(add_packet);
|
||||||
packet_counts[add_packet->packet.stream_index] += 1;
|
packet_counts[add_packet->packet.stream_index] += 1;
|
||||||
Debug(1, "packet counts for %d is %d",
|
Debug(2, "packet counts for %d is %d",
|
||||||
add_packet->packet.stream_index,
|
add_packet->packet.stream_index,
|
||||||
packet_counts[add_packet->packet.stream_index]);
|
packet_counts[add_packet->packet.stream_index]);
|
||||||
|
|
||||||
|
@ -120,56 +121,87 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) {
|
||||||
//
|
//
|
||||||
// So start at the beginning, counting video packets until the next keyframe.
|
// So start at the beginning, counting video packets until the next keyframe.
|
||||||
// Then if deleting those packets doesn't break 1 and 2, then go ahead and delete them.
|
// Then if deleting those packets doesn't break 1 and 2, then go ahead and delete them.
|
||||||
if ( ! (
|
if (keep_keyframes and ! (
|
||||||
add_packet->packet.stream_index == video_stream_id
|
add_packet->packet.stream_index == video_stream_id
|
||||||
and
|
and
|
||||||
add_packet->keyframe
|
add_packet->keyframe
|
||||||
and
|
and
|
||||||
(packet_counts[video_stream_id] > max_video_packet_count)
|
(packet_counts[video_stream_id] > max_video_packet_count)
|
||||||
and
|
and
|
||||||
*(pktQueue.begin()) != add_packet
|
*(pktQueue.begin()) != add_packet
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Debug(3, "stream index %d ?= video_stream_id %d, keyframe %d, counts %d > max %d at begin %d",
|
Debug(3, "stream index %d ?= video_stream_id %d, keyframe %d, keep_keyframes %d, counts %d > max %d at begin %d",
|
||||||
add_packet->packet.stream_index, video_stream_id, add_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count,
|
add_packet->packet.stream_index, video_stream_id, add_packet->keyframe, keep_keyframes, packet_counts[video_stream_id], max_video_packet_count,
|
||||||
( *(pktQueue.begin()) != add_packet )
|
( *(pktQueue.begin()) != add_packet )
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::unique_lock<std::mutex> lck(mutex);
|
std::unique_lock<std::mutex> lck(mutex);
|
||||||
|
if (!keep_keyframes) {
|
||||||
|
// If not doing passthrough, we don't care about starting with a keyframe so logic is simpler
|
||||||
|
while ((*pktQueue.begin() != add_packet) and (packet_counts[video_stream_id] > max_video_packet_count)) {
|
||||||
|
ZMPacket *zm_packet = *pktQueue.begin();
|
||||||
|
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||||
|
if (!lp->trylock()) break;
|
||||||
|
delete lp;
|
||||||
|
|
||||||
|
pktQueue.pop_front();
|
||||||
|
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||||
|
Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%d",
|
||||||
|
zm_packet->packet.stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count, pktQueue.size());
|
||||||
|
delete zm_packet;
|
||||||
|
} // end while
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ananlysis_it isn't at the end, we need to keep that many additional packets
|
||||||
|
int tail_count = 0;
|
||||||
|
if (pktQueue.back() != add_packet) {
|
||||||
|
Debug(1, "Ours is not the back");
|
||||||
|
packetqueue_iterator it = pktQueue.end();
|
||||||
|
--it;
|
||||||
|
while (*it != add_packet) {
|
||||||
|
if ((*it)->packet.stream_index == video_stream_id)
|
||||||
|
++tail_count;
|
||||||
|
--it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug(1, "Tail count is %d", tail_count);
|
||||||
|
|
||||||
packetqueue_iterator it = pktQueue.begin();
|
packetqueue_iterator it = pktQueue.begin();
|
||||||
packetqueue_iterator next_front = pktQueue.begin();
|
packetqueue_iterator next_front = pktQueue.begin();
|
||||||
int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking
|
int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking
|
||||||
|
|
||||||
|
|
||||||
// First packet is special because we know it is a video keyframe and only need to check for lock
|
// First packet is special because we know it is a video keyframe and only need to check for lock
|
||||||
ZMPacket *zm_packet = *it;
|
ZMPacket *zm_packet = *it;
|
||||||
if ( zm_packet->trylock() ) {
|
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||||
|
if (lp->trylock()) {
|
||||||
++it;
|
++it;
|
||||||
zm_packet->unlock();
|
delete lp;
|
||||||
|
|
||||||
// Since we have many packets in the queue, we should NOT be pointing at end so don't need to test for that
|
// Since we have many packets in the queue, we should NOT be pointing at end so don't need to test for that
|
||||||
while ( *it != add_packet ) {
|
while (*it != add_packet) {
|
||||||
zm_packet = *it;
|
zm_packet = *it;
|
||||||
if ( !zm_packet->trylock() ) {
|
lp = new ZMLockedPacket(zm_packet);
|
||||||
break;
|
if (!lp->trylock()) break;
|
||||||
}
|
delete lp;
|
||||||
zm_packet->unlock();
|
|
||||||
|
|
||||||
if ( is_there_an_iterator_pointing_to_packet(zm_packet) ) {
|
if (is_there_an_iterator_pointing_to_packet(zm_packet)) {
|
||||||
Warning("Found iterator at beginning of queue. Some thread isn't keeping up");
|
Warning("Found iterator at beginning of queue. Some thread isn't keeping up");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( zm_packet->packet.stream_index == video_stream_id ) {
|
if (zm_packet->packet.stream_index == video_stream_id) {
|
||||||
if ( zm_packet->keyframe ) {
|
if (zm_packet->keyframe) {
|
||||||
Debug(3, "Have a video keyframe so setting next front to it");
|
Debug(3, "Have a video keyframe so setting next front to it");
|
||||||
next_front = it;
|
next_front = it;
|
||||||
}
|
}
|
||||||
++video_packets_to_delete;
|
++video_packets_to_delete;
|
||||||
Debug(4, "Counted %d video packets. Which would leave %d in packetqueue",
|
Debug(4, "Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
|
||||||
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete);
|
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete, tail_count);
|
||||||
if (packet_counts[video_stream_id] - video_packets_to_delete <= max_video_packet_count) {
|
if (packet_counts[video_stream_id] - video_packets_to_delete <= max_video_packet_count + tail_count) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +232,7 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) {
|
||||||
return;
|
return;
|
||||||
} // end voidPacketQueue::clearPackets(ZMPacket* zm_packet)
|
} // end voidPacketQueue::clearPackets(ZMPacket* zm_packet)
|
||||||
|
|
||||||
ZMPacket* PacketQueue::popPacket( ) {
|
ZMLockedPacket* PacketQueue::popPacket( ) {
|
||||||
Debug(4, "pktQueue size %d", pktQueue.size());
|
Debug(4, "pktQueue size %d", pktQueue.size());
|
||||||
if ( pktQueue.empty() ) {
|
if ( pktQueue.empty() ) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -222,14 +254,15 @@ ZMPacket* PacketQueue::popPacket( ) {
|
||||||
}
|
}
|
||||||
} // end foreach iterator
|
} // end foreach iterator
|
||||||
|
|
||||||
zm_packet->lock();
|
ZMLockedPacket *lp = new ZMLockedPacket (zm_packet);
|
||||||
|
lp->lock();
|
||||||
|
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||||
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
|
|
||||||
return zm_packet;
|
return lp;
|
||||||
} // popPacket
|
} // popPacket
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,15 +352,17 @@ unsigned int PacketQueue::clear(unsigned int frames_to_keep, int stream_id) {
|
||||||
|
|
||||||
void PacketQueue::clear() {
|
void PacketQueue::clear() {
|
||||||
deleting = true;
|
deleting = true;
|
||||||
|
condition.notify_all();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lck(mutex);
|
std::unique_lock<std::mutex> lck(mutex);
|
||||||
|
|
||||||
while (!pktQueue.empty()) {
|
while (!pktQueue.empty()) {
|
||||||
ZMPacket *packet = pktQueue.front();
|
ZMPacket *packet = pktQueue.front();
|
||||||
// Someone might have this packet, but not for very long and since we have locked the queue they won't be able to get another one
|
// Someone might have this packet, but not for very long and since we have locked the queue they won't be able to get another one
|
||||||
packet->lock();
|
ZMLockedPacket *lp = new ZMLockedPacket(packet);
|
||||||
|
lp->lock();
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
packet->unlock();
|
delete lp;
|
||||||
delete packet;
|
delete packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,8 +487,8 @@ int PacketQueue::packet_count(int stream_id) {
|
||||||
|
|
||||||
|
|
||||||
// Returns a packet. Packet will be locked
|
// Returns a packet. Packet will be locked
|
||||||
ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
|
ZMLockedPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
|
||||||
if ( deleting or zm_terminate )
|
if (deleting or zm_terminate)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Debug(4, "Locking in get_packet using it %p queue end? %d, packet %p",
|
Debug(4, "Locking in get_packet using it %p queue end? %d, packet %p",
|
||||||
|
@ -467,7 +502,7 @@ ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
|
||||||
Debug(2, "waiting. Queue size %d it == end? %d", pktQueue.size(), (*it == pktQueue.end()));
|
Debug(2, "waiting. Queue size %d it == end? %d", pktQueue.size(), (*it == pktQueue.end()));
|
||||||
condition.wait(lck);
|
condition.wait(lck);
|
||||||
}
|
}
|
||||||
if ( deleting or zm_terminate )
|
if (deleting or zm_terminate)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Debug(4, "get_packet using it %p queue end? %d, packet %p",
|
Debug(4, "get_packet using it %p queue end? %d, packet %p",
|
||||||
|
@ -477,14 +512,21 @@ ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
|
||||||
Error("Null p?!");
|
Error("Null p?!");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
ZMLockedPacket *lp = new ZMLockedPacket(p);
|
||||||
Debug(3, "get_packet %p image_index: %d, about to lock packet", p, p->image_index);
|
Debug(3, "get_packet %p image_index: %d, about to lock packet", p, p->image_index);
|
||||||
while ( !(zm_terminate or deleting) and !p->trylock() ) {
|
while (!(zm_terminate or deleting) and !lp->trylock()) {
|
||||||
Debug(3, "waiting. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) );
|
Debug(3, "waiting on index %d. Queue size %d it == end? %d",
|
||||||
|
p->image_index, pktQueue.size(), ( *it == pktQueue.end() ) );
|
||||||
|
ZM_DUMP_PACKET(p->packet, "");
|
||||||
condition.wait(lck);
|
condition.wait(lck);
|
||||||
}
|
}
|
||||||
|
if (deleting or zm_terminate) {
|
||||||
|
// packet may have been deleted so we can't delete the lp FIXME
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
Debug(2, "Locked packet, unlocking packetqueue mutex");
|
Debug(2, "Locked packet, unlocking packetqueue mutex");
|
||||||
return p;
|
return lp;
|
||||||
} // end ZMPacket *PacketQueue::get_packet(it)
|
} // end ZMLockedPacket *PacketQueue::get_packet(it)
|
||||||
|
|
||||||
bool PacketQueue::increment_it(packetqueue_iterator *it) {
|
bool PacketQueue::increment_it(packetqueue_iterator *it) {
|
||||||
Debug(2, "Incrementing %p, queue size %d, end? %d", it, pktQueue.size(), ((*it) == pktQueue.end()));
|
Debug(2, "Incrementing %p, queue size %d, end? %d", it, pktQueue.size(), ((*it) == pktQueue.end()));
|
||||||
|
@ -599,7 +641,7 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
|
||||||
|
|
||||||
if ( wait ) {
|
if ( wait ) {
|
||||||
while ( ((! pktQueue.size()) or (*it == pktQueue.end())) and !zm_terminate and !deleting ) {
|
while ( ((! pktQueue.size()) or (*it == pktQueue.end())) and !zm_terminate and !deleting ) {
|
||||||
Debug(2, "waiting. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) );
|
Debug(2, "waiting for packets in queue. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) );
|
||||||
condition.wait(lck);
|
condition.wait(lck);
|
||||||
*it = pktQueue.begin();
|
*it = pktQueue.begin();
|
||||||
}
|
}
|
||||||
|
@ -612,14 +654,14 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
|
||||||
|
|
||||||
while ( *it != pktQueue.end() ) {
|
while ( *it != pktQueue.end() ) {
|
||||||
ZMPacket *zm_packet = *(*it);
|
ZMPacket *zm_packet = *(*it);
|
||||||
if ( !zm_packet ) {
|
if (!zm_packet) {
|
||||||
Error("Null zmpacket in queue!?");
|
Error("Null zmpacket in queue!?");
|
||||||
free_it(it);
|
free_it(it);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
Debug(1, "Packet keyframe %d for stream %d, so returning the it to it",
|
Debug(1, "Packet keyframe %d for stream %d, so returning the it to it",
|
||||||
zm_packet->keyframe, zm_packet->packet.stream_index);
|
zm_packet->keyframe, zm_packet->packet.stream_index);
|
||||||
if ( zm_packet->keyframe and ( zm_packet->packet.stream_index == video_stream_id ) ) {
|
if (zm_packet->keyframe and ( zm_packet->packet.stream_index == video_stream_id )) {
|
||||||
Debug(1, "Found a keyframe for stream %d, so returning the it to it", video_stream_id);
|
Debug(1, "Found a keyframe for stream %d, so returning the it to it", video_stream_id);
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
@ -627,7 +669,7 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
|
||||||
}
|
}
|
||||||
Debug(1, "DIdn't Found a keyframe for stream %d, so returning the it to it", video_stream_id);
|
Debug(1, "DIdn't Found a keyframe for stream %d, so returning the it to it", video_stream_id);
|
||||||
return it;
|
return it;
|
||||||
}
|
} // get video_it
|
||||||
|
|
||||||
void PacketQueue::free_it(packetqueue_iterator *it) {
|
void PacketQueue::free_it(packetqueue_iterator *it) {
|
||||||
for (
|
for (
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class ZMPacket;
|
class ZMPacket;
|
||||||
|
class ZMLockedPacket;
|
||||||
|
|
||||||
typedef std::list<ZMPacket *>::iterator packetqueue_iterator;
|
typedef std::list<ZMPacket *>::iterator packetqueue_iterator;
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ class PacketQueue {
|
||||||
int max_stream_id;
|
int max_stream_id;
|
||||||
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
|
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
|
||||||
bool deleting;
|
bool deleting;
|
||||||
|
bool keep_keyframes;
|
||||||
std::list<packetqueue_iterator *> iterators;
|
std::list<packetqueue_iterator *> iterators;
|
||||||
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
|
@ -50,9 +52,10 @@ class PacketQueue {
|
||||||
|
|
||||||
int addStream();
|
int addStream();
|
||||||
void setMaxVideoPackets(int p);
|
void setMaxVideoPackets(int p);
|
||||||
|
void setKeepKeyframes(bool k) { keep_keyframes = k; };
|
||||||
|
|
||||||
bool queuePacket(ZMPacket* packet);
|
bool queuePacket(ZMPacket* packet);
|
||||||
ZMPacket * popPacket();
|
ZMLockedPacket * popPacket();
|
||||||
bool popVideoPacket(ZMPacket* packet);
|
bool popVideoPacket(ZMPacket* packet);
|
||||||
bool popAudioPacket(ZMPacket* packet);
|
bool popAudioPacket(ZMPacket* packet);
|
||||||
unsigned int clear(unsigned int video_frames_to_keep, int stream_id);
|
unsigned int clear(unsigned int video_frames_to_keep, int stream_id);
|
||||||
|
@ -68,7 +71,7 @@ class PacketQueue {
|
||||||
|
|
||||||
bool increment_it(packetqueue_iterator *it);
|
bool increment_it(packetqueue_iterator *it);
|
||||||
bool increment_it(packetqueue_iterator *it, int stream_id);
|
bool increment_it(packetqueue_iterator *it, int stream_id);
|
||||||
ZMPacket *get_packet(packetqueue_iterator *);
|
ZMLockedPacket *get_packet(packetqueue_iterator *);
|
||||||
packetqueue_iterator *get_video_it(bool wait);
|
packetqueue_iterator *get_video_it(bool wait);
|
||||||
packetqueue_iterator *get_stream_it(int stream_id);
|
packetqueue_iterator *get_stream_it(int stream_id);
|
||||||
void free_it(packetqueue_iterator *);
|
void free_it(packetqueue_iterator *);
|
||||||
|
|
|
@ -202,7 +202,7 @@ int RemoteCameraHttp::SendRequest() {
|
||||||
* > 0 is the # of bytes read.
|
* > 0 is the # of bytes read.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
int RemoteCameraHttp::ReadData(Buffer &buffer, unsigned int bytes_expected) {
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(sd, &rfds);
|
FD_SET(sd, &rfds);
|
||||||
|
@ -210,44 +210,44 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
||||||
struct timeval temp_timeout = timeout;
|
struct timeval temp_timeout = timeout;
|
||||||
|
|
||||||
int n_found = select(sd+1, &rfds, nullptr, nullptr, &temp_timeout);
|
int n_found = select(sd+1, &rfds, nullptr, nullptr, &temp_timeout);
|
||||||
if( n_found == 0 ) {
|
if (n_found == 0) {
|
||||||
Debug( 1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec );
|
Debug(1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec);
|
||||||
int error = 0;
|
int error = 0;
|
||||||
socklen_t len = sizeof (error);
|
socklen_t len = sizeof(error);
|
||||||
int retval = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||||
if(retval != 0 ) {
|
if (retval != 0) {
|
||||||
Debug( 1, "error getting socket error code %s", strerror(retval) );
|
Debug(1, "error getting socket error code %s", strerror(retval));
|
||||||
}
|
}
|
||||||
if (error != 0 ) {
|
if (error != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
|
// Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
|
||||||
//Disconnect();
|
//Disconnect();
|
||||||
return 0;
|
return 0;
|
||||||
} else if ( n_found < 0) {
|
} else if (n_found < 0) {
|
||||||
Error("Select error: %s", strerror(errno));
|
Error("Select error: %s", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int total_bytes_to_read = 0;
|
unsigned int total_bytes_to_read = 0;
|
||||||
|
|
||||||
if ( bytes_expected ) {
|
if (bytes_expected) {
|
||||||
total_bytes_to_read = bytes_expected;
|
total_bytes_to_read = bytes_expected;
|
||||||
} else {
|
} else {
|
||||||
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) {
|
if (ioctl(sd, FIONREAD, &total_bytes_to_read) < 0) {
|
||||||
Error( "Can't ioctl(): %s", strerror(errno) );
|
Error("Can't ioctl(): %s", strerror(errno) );
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( total_bytes_to_read == 0 ) {
|
if (total_bytes_to_read == 0) {
|
||||||
if ( mode == SINGLE_IMAGE ) {
|
if (mode == SINGLE_IMAGE) {
|
||||||
int error = 0;
|
int error = 0;
|
||||||
socklen_t len = sizeof (error);
|
socklen_t len = sizeof(error);
|
||||||
int retval = getsockopt( sd, SOL_SOCKET, SO_ERROR, &error, &len );
|
int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||||
if ( retval != 0 ) {
|
if (retval != 0) {
|
||||||
Debug( 1, "error getting socket error code %s", strerror(retval) );
|
Debug(1, "error getting socket error code %s", strerror(retval));
|
||||||
}
|
}
|
||||||
if ( error != 0 ) {
|
if (error != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close.
|
// Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close.
|
||||||
|
@ -261,38 +261,38 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily.
|
// There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily.
|
||||||
if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) {
|
if (total_bytes_to_read > ZM_NETWORK_BUFSIZ) {
|
||||||
total_bytes_to_read = ZM_NETWORK_BUFSIZ;
|
total_bytes_to_read = ZM_NETWORK_BUFSIZ;
|
||||||
Debug(4, "Just getting 32K" );
|
Debug(4, "Just getting 32K");
|
||||||
} else {
|
} else {
|
||||||
Debug(4, "Just getting %d", total_bytes_to_read );
|
Debug(4, "Just getting %d", total_bytes_to_read);
|
||||||
}
|
}
|
||||||
} // end if bytes_expected or not
|
} // end if bytes_expected or not
|
||||||
Debug( 4, "Expecting %d bytes", total_bytes_to_read );
|
Debug(4, "Expecting %d bytes", total_bytes_to_read);
|
||||||
|
|
||||||
int total_bytes_read = 0;
|
int total_bytes_read = 0;
|
||||||
do {
|
do {
|
||||||
int bytes_read = buffer.read_into( sd, total_bytes_to_read );
|
int bytes_read = buffer.read_into(sd, total_bytes_to_read);
|
||||||
if ( bytes_read < 0 ) {
|
if (bytes_read < 0) {
|
||||||
Error( "Read error: %s", strerror(errno) );
|
Error("Read error: %s", strerror(errno));
|
||||||
return( -1 );
|
return -1;
|
||||||
} else if ( bytes_read == 0 ) {
|
} else if (bytes_read == 0) {
|
||||||
Debug( 2, "Socket closed" );
|
Debug(2, "Socket closed");
|
||||||
//Disconnect(); // Disconnect is done outside of ReadData now.
|
//Disconnect(); // Disconnect is done outside of ReadData now.
|
||||||
return( -1 );
|
return -1;
|
||||||
} else if ( (unsigned int)bytes_read < total_bytes_to_read ) {
|
} else if ((unsigned int)bytes_read < total_bytes_to_read) {
|
||||||
Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read );
|
Debug(1, "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read);
|
||||||
return( -1 );
|
} else {
|
||||||
|
Debug(3, "Read %d bytes", bytes_read);
|
||||||
}
|
}
|
||||||
Debug( 3, "Read %d bytes", bytes_read );
|
|
||||||
total_bytes_read += bytes_read;
|
total_bytes_read += bytes_read;
|
||||||
total_bytes_to_read -= bytes_read;
|
total_bytes_to_read -= bytes_read;
|
||||||
} while ( total_bytes_to_read );
|
} while (total_bytes_to_read);
|
||||||
|
|
||||||
Debug(4, buffer);
|
Debug(4, "buffer size: %d", static_cast<int>(buffer));
|
||||||
|
|
||||||
return total_bytes_read;
|
return total_bytes_read;
|
||||||
}
|
} // end readData
|
||||||
|
|
||||||
int RemoteCameraHttp::GetData() {
|
int RemoteCameraHttp::GetData() {
|
||||||
time_t start_time = time(nullptr);
|
time_t start_time = time(nullptr);
|
||||||
|
@ -497,36 +497,36 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( content_length ) {
|
if (content_length) {
|
||||||
while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) {
|
while (((long)buffer.size() < content_length ) and !zm_terminate) {
|
||||||
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
|
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length);
|
||||||
int bytes_read = GetData();
|
int bytes_read = ReadData(buffer, content_length - buffer.size());
|
||||||
|
|
||||||
if ( bytes_read < 0 ) {
|
if (bytes_read < 0) {
|
||||||
Error( "Unable to read content" );
|
Error("Unable to read content");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
bytes += bytes_read;
|
bytes += bytes_read;
|
||||||
}
|
}
|
||||||
Debug( 3, "Got end of image by length, content-length = %d", content_length );
|
Debug(3, "Got end of image by length, content-length = %d", content_length);
|
||||||
} else {
|
} else {
|
||||||
while ( !content_length ) {
|
while (!content_length) {
|
||||||
buffer_len = GetData();
|
buffer_len = GetData();
|
||||||
if ( buffer_len < 0 ) {
|
if (buffer_len < 0) {
|
||||||
Error( "Unable to read content" );
|
Error("Unable to read content");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
bytes += buffer_len;
|
bytes += buffer_len;
|
||||||
static RegExpr *content_expr = 0;
|
static RegExpr *content_expr = 0;
|
||||||
if ( mode == MULTI_IMAGE ) {
|
if (mode == MULTI_IMAGE) {
|
||||||
if ( !content_expr ) {
|
if (!content_expr) {
|
||||||
char content_pattern[256] = "";
|
char content_pattern[256] = "";
|
||||||
snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary );
|
snprintf(content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary);
|
||||||
content_expr = new RegExpr( content_pattern, PCRE_DOTALL );
|
content_expr = new RegExpr(content_pattern, PCRE_DOTALL);
|
||||||
}
|
}
|
||||||
if ( content_expr->Match( buffer, buffer.size() ) == 2 ) {
|
if (content_expr->Match( buffer, buffer.size()) == 2) {
|
||||||
content_length = content_expr->MatchLength( 1 );
|
content_length = content_expr->MatchLength( 1 );
|
||||||
Debug( 3, "Got end of image by pattern, content-length = %d", content_length );
|
Debug(3, "Got end of image by pattern, content-length = %d", content_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,7 +623,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
case HEADERCONT :
|
case HEADERCONT :
|
||||||
{
|
{
|
||||||
buffer_len = GetData();
|
buffer_len = GetData();
|
||||||
if ( buffer_len < 0 ) {
|
if (buffer_len < 0) {
|
||||||
Error("Unable to read header");
|
Error("Unable to read header");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -634,13 +634,13 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
int header_len = buffer.size();
|
int header_len = buffer.size();
|
||||||
bool all_headers = false;
|
bool all_headers = false;
|
||||||
|
|
||||||
while ( true ) {
|
while (true and !zm_terminate) {
|
||||||
int crlf_len = memspn(header_ptr, "\r\n", header_len);
|
int crlf_len = memspn(header_ptr, "\r\n", header_len);
|
||||||
if ( n_headers ) {
|
if (n_headers) {
|
||||||
if (
|
if (
|
||||||
(crlf_len == 2 && !strncmp(header_ptr, "\n\n", crlf_len ))
|
(crlf_len == 2 && !strncmp(header_ptr, "\n\n", crlf_len))
|
||||||
||
|
||
|
||||||
(crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len ))
|
(crlf_len == 4 && !strncmp(header_ptr, "\r\n\r\n", crlf_len))
|
||||||
) {
|
) {
|
||||||
Debug(3, "Have double linefeed, done headers");
|
Debug(3, "Have double linefeed, done headers");
|
||||||
*header_ptr = '\0';
|
*header_ptr = '\0';
|
||||||
|
@ -650,8 +650,8 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( crlf_len ) {
|
if (crlf_len) {
|
||||||
if ( header_len == crlf_len ) {
|
if (header_len == crlf_len) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
*header_ptr = '\0';
|
*header_ptr = '\0';
|
||||||
|
@ -661,22 +661,22 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(6, "%s", header_ptr);
|
Debug(6, "%s", header_ptr);
|
||||||
if ( (crlf = mempbrk(header_ptr, "\r\n", header_len)) ) {
|
if ((crlf = mempbrk(header_ptr, "\r\n", header_len))) {
|
||||||
//headers[n_headers++] = header_ptr;
|
//headers[n_headers++] = header_ptr;
|
||||||
n_headers++;
|
n_headers++;
|
||||||
|
|
||||||
if ( !http_header && (strncasecmp(header_ptr, http_match, http_match_len) == 0) ) {
|
if (!http_header && (strncasecmp(header_ptr, http_match, http_match_len) == 0)) {
|
||||||
http_header = header_ptr+http_match_len;
|
http_header = header_ptr+http_match_len;
|
||||||
Debug( 6, "Got http header '%s'", header_ptr );
|
Debug(6, "Got http header '%s'", header_ptr);
|
||||||
} else if ( !connection_header && (strncasecmp(header_ptr, connection_match, connection_match_len) == 0) ) {
|
} else if ( !connection_header && (strncasecmp(header_ptr, connection_match, connection_match_len) == 0) ) {
|
||||||
connection_header = header_ptr+connection_match_len;
|
connection_header = header_ptr+connection_match_len;
|
||||||
Debug( 6, "Got connection header '%s'", header_ptr );
|
Debug(6, "Got connection header '%s'", header_ptr);
|
||||||
} else if ( !content_length_header && (strncasecmp(header_ptr, content_length_match, content_length_match_len) == 0) ) {
|
} else if ( !content_length_header && (strncasecmp(header_ptr, content_length_match, content_length_match_len) == 0) ) {
|
||||||
content_length_header = header_ptr+content_length_match_len;
|
content_length_header = header_ptr+content_length_match_len;
|
||||||
Debug( 6, "Got content length header '%s'", header_ptr );
|
Debug(6, "Got content length header '%s'", header_ptr);
|
||||||
} else if ( !authenticate_header && (strncasecmp(header_ptr, authenticate_match, authenticate_match_len) == 0) ) {
|
} else if ( !authenticate_header && (strncasecmp(header_ptr, authenticate_match, authenticate_match_len) == 0) ) {
|
||||||
authenticate_header = header_ptr;
|
authenticate_header = header_ptr;
|
||||||
Debug( 6, "Got authenticate header '%s'", header_ptr );
|
Debug(6, "Got authenticate header '%s'", header_ptr);
|
||||||
} else if ( !content_type_header && (strncasecmp(header_ptr, content_type_match, content_type_match_len) == 0) ) {
|
} else if ( !content_type_header && (strncasecmp(header_ptr, content_type_match, content_type_match_len) == 0) ) {
|
||||||
content_type_header = header_ptr+content_type_match_len;
|
content_type_header = header_ptr+content_type_match_len;
|
||||||
Debug(6, "Got content type header '%s'", header_ptr);
|
Debug(6, "Got content type header '%s'", header_ptr);
|
||||||
|
@ -691,10 +691,10 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
}
|
}
|
||||||
} // end while search for headers
|
} // end while search for headers
|
||||||
|
|
||||||
if ( all_headers ) {
|
if (all_headers) {
|
||||||
char *start_ptr, *end_ptr;
|
char *start_ptr, *end_ptr;
|
||||||
|
|
||||||
if ( !http_header ) {
|
if (!http_header) {
|
||||||
Error("Unable to extract HTTP status from header");
|
Error("Unable to extract HTTP status from header");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -703,7 +703,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
end_ptr = start_ptr+strspn(start_ptr, "10.");
|
end_ptr = start_ptr+strspn(start_ptr, "10.");
|
||||||
|
|
||||||
// FIXME Why are we memsetting every time? Can we not do it once?
|
// FIXME Why are we memsetting every time? Can we not do it once?
|
||||||
memset(http_version, 0, sizeof(http_version));
|
//memset(http_version, 0, sizeof(http_version));
|
||||||
strncpy(http_version, start_ptr, end_ptr-start_ptr);
|
strncpy(http_version, start_ptr, end_ptr-start_ptr);
|
||||||
|
|
||||||
start_ptr = end_ptr;
|
start_ptr = end_ptr;
|
||||||
|
@ -718,12 +718,12 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
start_ptr += strspn(start_ptr, " ");
|
start_ptr += strspn(start_ptr, " ");
|
||||||
strcpy(status_mesg, start_ptr);
|
strcpy(status_mesg, start_ptr);
|
||||||
|
|
||||||
if ( status == 401 ) {
|
if (status == 401) {
|
||||||
if ( mNeedAuth ) {
|
if (mNeedAuth) {
|
||||||
Error("Failed authentication");
|
Error("Failed authentication");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if ( !authenticate_header ) {
|
if (!authenticate_header) {
|
||||||
Error("Failed authentication, but don't have an authentication header.");
|
Error("Failed authentication, but don't have an authentication header.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -732,7 +732,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
Debug(2, "Checking for digest auth in %s", authenticate_header);
|
Debug(2, "Checking for digest auth in %s", authenticate_header);
|
||||||
|
|
||||||
mAuthenticator->checkAuthResponse(Header);
|
mAuthenticator->checkAuthResponse(Header);
|
||||||
if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) {
|
if (mAuthenticator->auth_method() == zm::AUTH_DIGEST) {
|
||||||
Debug(2, "Need Digest Authentication");
|
Debug(2, "Need Digest Authentication");
|
||||||
request = stringtf("GET %s HTTP/%s\r\n", path.c_str(), config.http_version);
|
request = stringtf("GET %s HTTP/%s\r\n", path.c_str(), config.http_version);
|
||||||
request += stringtf("User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION);
|
request += stringtf("User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION);
|
||||||
|
@ -747,26 +747,26 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Need some other kind of Authentication");
|
Debug(2, "Need some other kind of Authentication");
|
||||||
}
|
}
|
||||||
} else if ( status < 200 || status > 299 ) {
|
} else if (status < 200 || status > 299) {
|
||||||
Error("Invalid response status %s: %s", status_code, status_mesg);
|
Error("Invalid response status %s: %s", status_code, status_mesg);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Debug(3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version);
|
Debug(3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version);
|
||||||
|
|
||||||
if ( connection_header ) {
|
if (connection_header) {
|
||||||
memset(connection_type, 0, sizeof(connection_type));
|
memset(connection_type, 0, sizeof(connection_type));
|
||||||
start_ptr = connection_header + strspn(connection_header, " ");
|
start_ptr = connection_header + strspn(connection_header, " ");
|
||||||
// FIXME Should we not use strncpy?
|
// FIXME Should we not use strncpy?
|
||||||
strcpy(connection_type, start_ptr);
|
strcpy(connection_type, start_ptr);
|
||||||
Debug(3, "Got connection '%s'", connection_type);
|
Debug(3, "Got connection '%s'", connection_type);
|
||||||
}
|
}
|
||||||
if ( content_length_header ) {
|
if (content_length_header) {
|
||||||
start_ptr = content_length_header + strspn(content_length_header, " ");
|
start_ptr = content_length_header + strspn(content_length_header, " ");
|
||||||
content_length = atoi( start_ptr );
|
content_length = atoi(start_ptr);
|
||||||
Debug(3, "Got content length '%d'", content_length);
|
Debug(3, "Got content length '%d'", content_length);
|
||||||
}
|
}
|
||||||
if ( content_type_header ) {
|
if (content_type_header) {
|
||||||
memset(content_type, 0, sizeof(content_type));
|
//memset(content_type, 0, sizeof(content_type));
|
||||||
start_ptr = content_type_header + strspn(content_type_header, " ");
|
start_ptr = content_type_header + strspn(content_type_header, " ");
|
||||||
if ( (end_ptr = strchr(start_ptr, ';')) ) {
|
if ( (end_ptr = strchr(start_ptr, ';')) ) {
|
||||||
strncpy(content_type, start_ptr, end_ptr-start_ptr);
|
strncpy(content_type, start_ptr, end_ptr-start_ptr);
|
||||||
|
@ -774,7 +774,7 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
|
|
||||||
start_ptr = end_ptr + strspn(end_ptr, "; ");
|
start_ptr = end_ptr + strspn(end_ptr, "; ");
|
||||||
|
|
||||||
if ( strncasecmp(start_ptr, boundary_match, boundary_match_len) == 0 ) {
|
if (strncasecmp(start_ptr, boundary_match, boundary_match_len) == 0) {
|
||||||
start_ptr += boundary_match_len;
|
start_ptr += boundary_match_len;
|
||||||
start_ptr += strspn(start_ptr, "-");
|
start_ptr += strspn(start_ptr, "-");
|
||||||
content_boundary_len = sprintf(content_boundary, "--%s", start_ptr);
|
content_boundary_len = sprintf(content_boundary, "--%s", start_ptr);
|
||||||
|
@ -788,24 +788,24 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
}
|
}
|
||||||
} // end if content_type_header
|
} // end if content_type_header
|
||||||
|
|
||||||
if ( !strcasecmp(content_type, "image/jpeg") || !strcasecmp(content_type, "image/jpg") ) {
|
if (!strcasecmp(content_type, "image/jpeg") || !strcasecmp(content_type, "image/jpg")) {
|
||||||
// Single image
|
// Single image
|
||||||
mode = SINGLE_IMAGE;
|
mode = SINGLE_IMAGE;
|
||||||
format = JPEG;
|
format = JPEG;
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
} else if ( !strcasecmp(content_type, "image/x-rgb") ) {
|
} else if (!strcasecmp(content_type, "image/x-rgb")) {
|
||||||
// Single image
|
// Single image
|
||||||
mode = SINGLE_IMAGE;
|
mode = SINGLE_IMAGE;
|
||||||
format = X_RGB;
|
format = X_RGB;
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
} else if ( !strcasecmp(content_type, "image/x-rgbz") ) {
|
} else if (!strcasecmp(content_type, "image/x-rgbz")) {
|
||||||
// Single image
|
// Single image
|
||||||
mode = SINGLE_IMAGE;
|
mode = SINGLE_IMAGE;
|
||||||
format = X_RGBZ;
|
format = X_RGBZ;
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
} else if ( !strcasecmp(content_type, "multipart/x-mixed-replace") ) {
|
} else if (!strcasecmp(content_type, "multipart/x-mixed-replace")) {
|
||||||
// Image stream, so start processing
|
// Image stream, so start processing
|
||||||
if ( !content_boundary[0] ) {
|
if (!content_boundary[0]) {
|
||||||
Error("No content boundary found in header '%s'", content_type_header);
|
Error("No content boundary found in header '%s'", content_type_header);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -817,8 +817,8 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
//// MPEG stream, coming soon!
|
//// MPEG stream, coming soon!
|
||||||
//}
|
//}
|
||||||
else {
|
else {
|
||||||
Error( "Unrecognised content type '%s'", content_type );
|
Error("Unrecognised content type '%s'", content_type);
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Unable to extract entire header from stream, continuing");
|
Debug(3, "Unable to extract entire header from stream, continuing");
|
||||||
|
@ -844,24 +844,24 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
int subheader_len = buffer.size();
|
int subheader_len = buffer.size();
|
||||||
bool all_headers = false;
|
bool all_headers = false;
|
||||||
|
|
||||||
while( true ) {
|
while (true and !zm_terminate) {
|
||||||
int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len );
|
int crlf_len = memspn(subheader_ptr, "\r\n", subheader_len);
|
||||||
if ( n_subheaders ) {
|
if (n_subheaders) {
|
||||||
if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) {
|
if ( (crlf_len == 2 && !strncmp(subheader_ptr, "\n\n", crlf_len)) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) {
|
||||||
*subheader_ptr = '\0';
|
*subheader_ptr = '\0';
|
||||||
subheader_ptr += crlf_len;
|
subheader_ptr += crlf_len;
|
||||||
subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
|
subheader_len -= buffer.consume(subheader_ptr-(char *)buffer);
|
||||||
all_headers = true;
|
all_headers = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( crlf_len ) {
|
if (crlf_len) {
|
||||||
if ( subheader_len == crlf_len ) {
|
if (subheader_len == crlf_len) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
*subheader_ptr = '\0';
|
*subheader_ptr = '\0';
|
||||||
subheader_ptr += crlf_len;
|
subheader_ptr += crlf_len;
|
||||||
subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
|
subheader_len -= buffer.consume(subheader_ptr-(char *)buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -905,29 +905,29 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( all_headers && boundary_header ) {
|
if (all_headers && boundary_header) {
|
||||||
char *start_ptr/*, *end_ptr*/;
|
char *start_ptr/*, *end_ptr*/;
|
||||||
|
|
||||||
Debug( 3, "Got boundary '%s'", boundary_header );
|
Debug(3, "Got boundary '%s'", boundary_header);
|
||||||
|
|
||||||
if ( subcontent_length_header[0] ) {
|
if (subcontent_length_header[0]) {
|
||||||
start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " );
|
start_ptr = subcontent_length_header + strspn(subcontent_length_header, " ");
|
||||||
content_length = atoi( start_ptr );
|
content_length = atoi(start_ptr);
|
||||||
Debug( 3, "Got subcontent length '%d'", content_length );
|
Debug(3, "Got subcontent length '%d'", content_length);
|
||||||
}
|
}
|
||||||
if ( subcontent_type_header[0] ) {
|
if (subcontent_type_header[0]) {
|
||||||
memset( content_type, 0, sizeof(content_type) );
|
//memset(content_type, 0, sizeof(content_type));
|
||||||
start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " );
|
start_ptr = subcontent_type_header + strspn(subcontent_type_header, " ");
|
||||||
strcpy( content_type, start_ptr );
|
strcpy(content_type, start_ptr);
|
||||||
Debug( 3, "Got subcontent type '%s'", content_type );
|
Debug(3, "Got subcontent type '%s'", content_type);
|
||||||
}
|
}
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
} else {
|
} else {
|
||||||
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
Debug(3, "Unable to extract subheader from stream, retrying");
|
||||||
buffer_len = GetData();
|
buffer_len = GetData();
|
||||||
if ( buffer_len < 0 ) {
|
if (buffer_len < 0) {
|
||||||
Error( "Unable to read subheader" );
|
Error("Unable to read subheader");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
bytes += buffer_len;
|
bytes += buffer_len;
|
||||||
state = SUBHEADERCONT;
|
state = SUBHEADERCONT;
|
||||||
|
@ -942,90 +942,90 @@ int RemoteCameraHttp::GetResponse() {
|
||||||
*semicolon = '\0';
|
*semicolon = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) {
|
if (!strcasecmp(content_type, "image/jpeg") || !strcasecmp(content_type, "image/jpg")) {
|
||||||
format = JPEG;
|
format = JPEG;
|
||||||
} else if ( !strcasecmp( content_type, "image/x-rgb" ) ) {
|
} else if (!strcasecmp(content_type, "image/x-rgb")) {
|
||||||
format = X_RGB;
|
format = X_RGB;
|
||||||
} else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) {
|
} else if (!strcasecmp(content_type, "image/x-rgbz")) {
|
||||||
format = X_RGBZ;
|
format = X_RGBZ;
|
||||||
} else {
|
} else {
|
||||||
Error( "Found unsupported content type '%s'", content_type );
|
Error("Found unsupported content type '%s'", content_type);
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an early test for jpeg content, so we can bail early
|
// This is an early test for jpeg content, so we can bail early
|
||||||
if ( format == JPEG && buffer.size() >= 2 ) {
|
if (format == JPEG and buffer.size() >= 2) {
|
||||||
if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) {
|
if (buffer[0] != 0xff or buffer[1] != 0xd8) {
|
||||||
Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] );
|
Error("Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1]);
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( content_length ) {
|
if (content_length) {
|
||||||
while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) {
|
while (((long)buffer.size() < content_length) and !zm_terminate) {
|
||||||
Debug(4, "getting more data");
|
Debug(4, "getting more data");
|
||||||
int bytes_read = GetData();
|
int bytes_read = ReadData(buffer, content_length-buffer.size());
|
||||||
if ( bytes_read < 0 ) {
|
if (bytes_read < 0) {
|
||||||
Error("Unable to read content");
|
Error("Unable to read content");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
bytes += bytes_read;
|
bytes += bytes_read;
|
||||||
}
|
}
|
||||||
Debug( 3, "Got end of image by length, content-length = %d", content_length );
|
Debug(3, "Got end of image by length, content-length = %d", content_length);
|
||||||
} else {
|
} else {
|
||||||
// Read until we find the end of image or the stream closes.
|
// Read until we find the end of image or the stream closes.
|
||||||
while ( !content_length && !zm_terminate ) {
|
while (!content_length && !zm_terminate) {
|
||||||
Debug(4, "!content_length, ReadData");
|
Debug(4, "!content_length, ReadData");
|
||||||
buffer_len = ReadData( buffer );
|
buffer_len = ReadData(buffer);
|
||||||
if ( buffer_len < 0 ) {
|
if (buffer_len < 0) {
|
||||||
Error( "Unable to read content" );
|
Error("Unable to read content");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
bytes += buffer_len;
|
bytes += buffer_len;
|
||||||
int buffer_size = buffer.size();
|
int buffer_size = buffer.size();
|
||||||
if ( buffer_len ) {
|
if (buffer_len) {
|
||||||
// Got some data
|
// Got some data
|
||||||
|
|
||||||
if ( mode == MULTI_IMAGE ) {
|
if (mode == MULTI_IMAGE) {
|
||||||
// Look for the boundary marker, determine content length using it's position
|
// Look for the boundary marker, determine content length using it's position
|
||||||
if ( char *start_ptr = (char *)memstr( (char *)buffer, "\r\n--", buffer_size ) ) {
|
if (char *start_ptr = (char *)memstr( (char *)buffer, "\r\n--", buffer_size)) {
|
||||||
content_length = start_ptr - (char *)buffer;
|
content_length = start_ptr - (char *)buffer;
|
||||||
Debug( 2, "Got end of image by pattern (crlf--), content-length = %d", content_length );
|
Debug(2, "Got end of image by pattern (crlf--), content-length = %d", content_length);
|
||||||
} else {
|
} else {
|
||||||
Debug( 2, "Did not find end of image by patten (crlf--) yet, content-length = %d", content_length );
|
Debug(2, "Did not find end of image by patten (crlf--) yet, content-length = %d", content_length);
|
||||||
}
|
}
|
||||||
} // end if MULTI_IMAGE
|
} // end if MULTI_IMAGE
|
||||||
} else {
|
} else {
|
||||||
content_length = buffer_size;
|
content_length = buffer_size;
|
||||||
Debug( 2, "Got end of image by closure, content-length = %d", content_length );
|
Debug(2, "Got end of image by closure, content-length = %d", content_length);
|
||||||
if ( mode == SINGLE_IMAGE ) {
|
if (mode == SINGLE_IMAGE) {
|
||||||
char *end_ptr = (char *)buffer+buffer_size;
|
char *end_ptr = (char *)buffer+buffer_size;
|
||||||
|
|
||||||
// strip off any last line feeds
|
// strip off any last line feeds
|
||||||
while( *end_ptr == '\r' || *end_ptr == '\n' ) {
|
while (*end_ptr == '\r' || *end_ptr == '\n') {
|
||||||
content_length--;
|
content_length--;
|
||||||
end_ptr--;
|
end_ptr--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( end_ptr != ((char *)buffer+buffer_size) ) {
|
if (end_ptr != ((char *)buffer+buffer_size)) {
|
||||||
Debug( 2, "Trimmed end of image, new content-length = %d", content_length );
|
Debug(2, "Trimmed end of image, new content-length = %d", content_length);
|
||||||
}
|
}
|
||||||
} // end if SINGLE_IMAGE
|
} // end if SINGLE_IMAGE
|
||||||
} // end if read some data
|
} // end if read some data
|
||||||
} // end while ! content_length
|
} // end while ! content_length
|
||||||
} // end if content_length
|
} // end if content_length
|
||||||
|
|
||||||
if ( mode == SINGLE_IMAGE ) {
|
if (mode == SINGLE_IMAGE) {
|
||||||
state = HEADER;
|
state = HEADER;
|
||||||
Disconnect();
|
Disconnect();
|
||||||
} else {
|
} else {
|
||||||
state = SUBHEADER;
|
state = SUBHEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( format == JPEG && buffer.size() >= 2 ) {
|
if (format == JPEG && buffer.size() >= 2) {
|
||||||
if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) {
|
if (buffer[0] != 0xff || buffer[1] != 0xd8) {
|
||||||
Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] );
|
Error("Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1]);
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,14 +187,16 @@ int main(int argc, char *argv[]) {
|
||||||
for (size_t i = 0; i < monitors.size(); i++) {
|
for (size_t i = 0; i < monitors.size(); i++) {
|
||||||
std::shared_ptr<Monitor> monitor = monitors[i];
|
std::shared_ptr<Monitor> monitor = monitors[i];
|
||||||
|
|
||||||
if (!(monitor->ShmValid() or monitor->connect())) {
|
if (!monitor->ShmValid()) {
|
||||||
Warning("Couldn't connect to monitor %d", monitor->Id());
|
monitor->disconnect();
|
||||||
if (sessions[i]) {
|
if (!monitor->connect()) {
|
||||||
rtspServer->RemoveSession(sessions[i]->GetMediaSessionId());
|
Warning("Couldn't connect to monitor %d", monitor->Id());
|
||||||
sessions[i] = nullptr;
|
if (sessions[i]) {
|
||||||
}
|
rtspServer->RemoveSession(sessions[i]->GetMediaSessionId());
|
||||||
monitor->disconnect();
|
sessions[i] = nullptr;
|
||||||
continue;
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sessions[i]) {
|
if (!sessions[i]) {
|
||||||
|
|
|
@ -46,6 +46,9 @@ class ZM_RtspServer_Authenticator : public xop::Authenticator {
|
||||||
if (query.has("jwt_token")) {
|
if (query.has("jwt_token")) {
|
||||||
const QueryParameter *jwt_token = query.get("jwt_token");
|
const QueryParameter *jwt_token = query.get("jwt_token");
|
||||||
user = zmLoadTokenUser(jwt_token->firstValue().c_str(), false);
|
user = zmLoadTokenUser(jwt_token->firstValue().c_str(), false);
|
||||||
|
} else if (query.has("token")) {
|
||||||
|
const QueryParameter *jwt_token = query.get("token");
|
||||||
|
user = zmLoadTokenUser(jwt_token->firstValue().c_str(), false);
|
||||||
} else if (strcmp(config.auth_relay, "none") == 0) {
|
} else if (strcmp(config.auth_relay, "none") == 0) {
|
||||||
if (query.has("username")) {
|
if (query.has("username")) {
|
||||||
std::string username = query.get("username")->firstValue();
|
std::string username = query.get("username")->firstValue();
|
||||||
|
|
|
@ -83,6 +83,7 @@ std::list< std::pair<unsigned char*, size_t> > H264_ZoneMinderFifoSource::splitF
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
|
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
|
||||||
|
if (!bufSize) break;
|
||||||
|
|
||||||
buffer = this->extractFrame(&buffer[size], bufSize, size);
|
buffer = this->extractFrame(&buffer[size], bufSize, size);
|
||||||
} // end while buffer
|
} // end while buffer
|
||||||
|
|
|
@ -84,7 +84,7 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(4, "%s bytes read %d bytes, buffer size %u", m_fifo.c_str(), bytes_read, m_buffer.size());
|
Debug(1, "%s bytes read %d bytes, buffer size %u", m_fifo.c_str(), bytes_read, m_buffer.size());
|
||||||
while (m_buffer.size()) {
|
while (m_buffer.size()) {
|
||||||
unsigned int data_size = 0;
|
unsigned int data_size = 0;
|
||||||
int64_t pts;
|
int64_t pts;
|
||||||
|
@ -96,14 +96,14 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
header_end = (unsigned char *)memchr(header_start, '\n', m_buffer.tail()-header_start);
|
header_end = (unsigned char *)memchr(header_start, '\n', m_buffer.tail()-header_start);
|
||||||
if (!header_end) {
|
if (!header_end) {
|
||||||
// Must not have enough data. So... keep all.
|
// Must not have enough data. So... keep all.
|
||||||
Debug(1, "Didn't find newline");
|
Debug(1, "Didn't find newline buffer size is %d", m_buffer.size());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int header_size = header_end-header_start;
|
unsigned int header_size = header_end-header_start;
|
||||||
char *header = new char[header_size+1];
|
char *header = new char[header_size+1];
|
||||||
|
strncpy(header, reinterpret_cast<const char *>(header_start), header_size);
|
||||||
header[header_size] = '\0';
|
header[header_size] = '\0';
|
||||||
strncpy(header, reinterpret_cast<const char *>(header_start), header_end-header_start);
|
|
||||||
|
|
||||||
char *content_length_ptr = strchr(header, ' ');
|
char *content_length_ptr = strchr(header, ' ');
|
||||||
if (!content_length_ptr) {
|
if (!content_length_ptr) {
|
||||||
|
@ -125,12 +125,12 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
pts_ptr ++;
|
pts_ptr ++;
|
||||||
data_size = atoi(content_length_ptr);
|
data_size = atoi(content_length_ptr);
|
||||||
pts = strtoll(pts_ptr, nullptr, 10);
|
pts = strtoll(pts_ptr, nullptr, 10);
|
||||||
|
Debug(4, "ZM Packet %s header_size %d packet size %u pts %" PRId64, header, header_size, data_size, pts);
|
||||||
delete[] header;
|
delete[] header;
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "ZM header not found %s.",m_buffer.head());
|
Debug(1, "ZM header not found in %d of buffer:%s.", m_buffer.size(), m_buffer.head());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Debug(4, "ZM Packet size %u pts %" PRId64, data_size, pts);
|
|
||||||
if (header_start != m_buffer) {
|
if (header_start != m_buffer) {
|
||||||
Debug(4, "ZM Packet didn't start at beginning of buffer %u. %c%c",
|
Debug(4, "ZM Packet didn't start at beginning of buffer %u. %c%c",
|
||||||
header_start-m_buffer.head(), m_buffer[0], m_buffer[1]);
|
header_start-m_buffer.head(), m_buffer[0], m_buffer[1]);
|
||||||
|
@ -142,21 +142,21 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
int bytes_needed = data_size - (m_buffer.size() - header_size);
|
int bytes_needed = data_size - (m_buffer.size() - header_size);
|
||||||
if (bytes_needed > 0) {
|
if (bytes_needed > 0) {
|
||||||
Debug(4, "Need another %d bytes. Trying to read them", bytes_needed);
|
Debug(4, "Need another %d bytes. Trying to read them", bytes_needed);
|
||||||
int bytes_read = m_buffer.read_into(m_fd, bytes_needed, {1,0});
|
int bytes_read = m_buffer.read_into(m_fd, bytes_needed);
|
||||||
if ( bytes_read != bytes_needed ) {
|
if ( bytes_read != bytes_needed ) {
|
||||||
Debug(4, "Failed to read another %d bytes.", bytes_needed);
|
Debug(1, "Failed to read another %d bytes, got %d.", bytes_needed, bytes_read);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsigned char *packet_start = m_buffer.head() + header_size;
|
m_buffer.consume(header_size);
|
||||||
|
unsigned char *packet_start = m_buffer.head();
|
||||||
size_t bytes_remaining = data_size;
|
size_t bytes_remaining = data_size;
|
||||||
std::list< std::pair<unsigned char*, size_t> > framesList = this->splitFrames(packet_start, bytes_remaining);
|
std::list< std::pair<unsigned char*, size_t> > framesList = this->splitFrames(packet_start, bytes_remaining);
|
||||||
Debug(3, "Got %d frames, consuming %d bytes, remaining %d", framesList.size(), header_size + data_size, bytes_remaining);
|
Debug(3, "Got %d frames, consuming %d bytes, remaining %d", framesList.size(), header_size + data_size, bytes_remaining);
|
||||||
m_buffer.consume(header_size + data_size);
|
m_buffer.consume(data_size);
|
||||||
while (framesList.size()) {
|
while (framesList.size()) {
|
||||||
std::pair<unsigned char*, size_t> nal = framesList.front();
|
std::pair<unsigned char*, size_t> nal = framesList.front();
|
||||||
framesList.pop_front();
|
framesList.pop_front();
|
||||||
|
|
||||||
PushFrame(nal.first, nal.second, pts);
|
PushFrame(nal.first, nal.second, pts);
|
||||||
}
|
}
|
||||||
} // end while m_buffer.size()
|
} // end while m_buffer.size()
|
||||||
|
|
|
@ -40,7 +40,7 @@ StreamBase::~StreamBase() {
|
||||||
bool StreamBase::loadMonitor(int p_monitor_id) {
|
bool StreamBase::loadMonitor(int p_monitor_id) {
|
||||||
monitor_id = p_monitor_id;
|
monitor_id = p_monitor_id;
|
||||||
|
|
||||||
if ( !(monitor = Monitor::Load(monitor_id, false, Monitor::QUERY)) ) {
|
if ( !(monitor or (monitor = Monitor::Load(monitor_id, false, Monitor::QUERY))) ) {
|
||||||
Error("Unable to load monitor id %d for streaming", monitor_id);
|
Error("Unable to load monitor id %d for streaming", monitor_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ bool StreamBase::loadMonitor(int p_monitor_id) {
|
||||||
|
|
||||||
if ( !monitor->connect() ) {
|
if ( !monitor->connect() ) {
|
||||||
Error("Unable to connect to monitor id %d for streaming", monitor_id);
|
Error("Unable to connect to monitor id %d for streaming", monitor_id);
|
||||||
|
monitor->disconnect();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +72,10 @@ bool StreamBase::checkInitialised() {
|
||||||
Error("Monitor shm is not connected");
|
Error("Monitor shm is not connected");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ((monitor->GetType() == Monitor::FFMPEG) and !monitor->DecodingEnabled() ) {
|
||||||
|
Error("Monitor is not decoding.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -245,18 +245,19 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
|
||||||
const char *pass = dbrow[2];
|
const char *pass = dbrow[2];
|
||||||
|
|
||||||
time_t our_now = now;
|
time_t our_now = now;
|
||||||
|
tm now_tm = {};
|
||||||
for ( unsigned int i = 0; i < hours; i++, our_now -= 3600 ) {
|
for ( unsigned int i = 0; i < hours; i++, our_now -= 3600 ) {
|
||||||
struct tm *now_tm = localtime(&our_now);
|
localtime_r(&our_now, &now_tm);
|
||||||
|
|
||||||
snprintf(auth_key, sizeof(auth_key)-1, "%s%s%s%s%d%d%d%d",
|
snprintf(auth_key, sizeof(auth_key)-1, "%s%s%s%s%d%d%d%d",
|
||||||
config.auth_hash_secret,
|
config.auth_hash_secret,
|
||||||
user,
|
user,
|
||||||
pass,
|
pass,
|
||||||
remote_addr,
|
remote_addr,
|
||||||
now_tm->tm_hour,
|
now_tm.tm_hour,
|
||||||
now_tm->tm_mday,
|
now_tm.tm_mday,
|
||||||
now_tm->tm_mon,
|
now_tm.tm_mon,
|
||||||
now_tm->tm_year);
|
now_tm.tm_year);
|
||||||
|
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char *)auth_key, strlen(auth_key), md5sum);
|
MD5((unsigned char *)auth_key, strlen(auth_key), md5sum);
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include "zm_config.h"
|
#include "zm_config.h"
|
||||||
#include "zm_logger.h"
|
#include "zm_logger.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdarg>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fcntl.h> /* Definition of AT_* constants */
|
#include <fcntl.h> /* Definition of AT_* constants */
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -43,7 +43,7 @@ std::string trimSet(std::string str, std::string trimset) {
|
||||||
// Trim Both leading and trailing sets
|
// Trim Both leading and trailing sets
|
||||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||||
|
|
||||||
// if all spaces or empty return an empty string
|
// if all spaces or empty return an empty string
|
||||||
if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
|
if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
|
||||||
return std::string("");
|
return std::string("");
|
||||||
|
@ -148,7 +148,7 @@ const std::string base64Encode(const std::string &inString) {
|
||||||
selection = remainder | (*inPtr >> 4);
|
selection = remainder | (*inPtr >> 4);
|
||||||
remainder = (*inPtr++ & 0x0f) << 2;
|
remainder = (*inPtr++ & 0x0f) << 2;
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
|
|
||||||
if ( *inPtr ) {
|
if ( *inPtr ) {
|
||||||
selection = remainder | (*inPtr >> 6);
|
selection = remainder | (*inPtr >> 6);
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
|
@ -175,7 +175,7 @@ int split(const char* string, const char delim, std::vector<std::string>& items)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
std::string str(string);
|
std::string str(string);
|
||||||
|
|
||||||
while ( true ) {
|
while ( true ) {
|
||||||
size_t pos = str.find(delim);
|
size_t pos = str.find(delim);
|
||||||
items.push_back(str.substr(0, pos));
|
items.push_back(str.substr(0, pos));
|
||||||
|
@ -242,7 +242,7 @@ void hwcaps_detect() {
|
||||||
} else {
|
} else {
|
||||||
sse_version = 0;
|
sse_version = 0;
|
||||||
Debug(1, "Detected a x86\\x86-64 processor");
|
Debug(1, "Detected a x86\\x86-64 processor");
|
||||||
}
|
}
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
// ARM processor in 32bit mode
|
// ARM processor in 32bit mode
|
||||||
// To see if it supports NEON, we need to get that information from the kernel
|
// To see if it supports NEON, we need to get that information from the kernel
|
||||||
|
@ -279,7 +279,7 @@ void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
||||||
"sse2_copy_iter:\n\t"
|
"sse2_copy_iter:\n\t"
|
||||||
"movdqa (%0),%%xmm0\n\t"
|
"movdqa (%0),%%xmm0\n\t"
|
||||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||||
|
@ -328,16 +328,17 @@ void timespec_diff(struct timespec *start, struct timespec *end, struct timespec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *timeval_to_string( struct timeval tv ) {
|
std::string TimevalToString(timeval tv) {
|
||||||
time_t nowtime;
|
tm now = {};
|
||||||
struct tm *nowtm;
|
std::array<char, 26> tm_buf = {};
|
||||||
static char tmbuf[20], buf[28];
|
|
||||||
|
|
||||||
nowtime = tv.tv_sec;
|
localtime_r(&tv.tv_sec, &now);
|
||||||
nowtm = localtime(&nowtime);
|
size_t tm_buf_len = strftime(tm_buf.data(), tm_buf.size(), "%Y-%m-%d %H:%M:%S", &now);
|
||||||
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
|
if (tm_buf_len == 0) {
|
||||||
snprintf(buf, sizeof buf-1, "%s.%06ld", tmbuf, tv.tv_usec);
|
return "";
|
||||||
return buf;
|
}
|
||||||
|
|
||||||
|
return stringtf("%s.%06ld", tm_buf.data(), tv.tv_usec);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UriDecode( const std::string &encoded ) {
|
std::string UriDecode( const std::string &encoded ) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ void hwcaps_detect();
|
||||||
extern unsigned int sse_version;
|
extern unsigned int sse_version;
|
||||||
extern unsigned int neonversion;
|
extern unsigned int neonversion;
|
||||||
|
|
||||||
char *timeval_to_string( struct timeval tv );
|
std::string TimevalToString(timeval tv);
|
||||||
std::string UriDecode( const std::string &encoded );
|
std::string UriDecode( const std::string &encoded );
|
||||||
void touch( const char *pathname );
|
void touch( const char *pathname );
|
||||||
|
|
||||||
|
|
|
@ -984,7 +984,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
||||||
);
|
);
|
||||||
} else if ( !zm_packet->in_frame ) {
|
} else if ( !zm_packet->in_frame ) {
|
||||||
Debug(4, "Have no in_frame");
|
Debug(4, "Have no in_frame");
|
||||||
if ( zm_packet->packet.size ) {
|
if (zm_packet->packet.size and !zm_packet->decoded) {
|
||||||
Debug(4, "Decoding");
|
Debug(4, "Decoding");
|
||||||
if ( !zm_packet->decode(video_in_ctx) ) {
|
if ( !zm_packet->decode(video_in_ctx) ) {
|
||||||
Debug(2, "unable to decode yet.");
|
Debug(2, "unable to decode yet.");
|
||||||
|
@ -1216,29 +1216,6 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
|
||||||
return 0;
|
return 0;
|
||||||
} // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt)
|
} // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt)
|
||||||
|
|
||||||
int VideoStore::write_packets(PacketQueue &queue) {
|
|
||||||
// Need to write out all the frames from the last keyframe?
|
|
||||||
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe.
|
|
||||||
unsigned int packet_count = 0;
|
|
||||||
ZMPacket *queued_packet;
|
|
||||||
|
|
||||||
while ( ( queued_packet = queue.popPacket() ) ) {
|
|
||||||
AVPacket *avp = queued_packet->av_packet();
|
|
||||||
|
|
||||||
packet_count += 1;
|
|
||||||
//Write the packet to our video store
|
|
||||||
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)",
|
|
||||||
avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, queue.size() );
|
|
||||||
int ret = this->writePacket( queued_packet );
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
//Less than zero and we skipped a frame
|
|
||||||
}
|
|
||||||
delete queued_packet;
|
|
||||||
} // end while packets in the packetqueue
|
|
||||||
Debug(2, "Wrote %d queued packets", packet_count );
|
|
||||||
return packet_count;
|
|
||||||
} // end int VideoStore::write_packets( PacketQueue &queue ) {
|
|
||||||
|
|
||||||
int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) {
|
int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) {
|
||||||
pkt->pos = -1;
|
pkt->pos = -1;
|
||||||
pkt->stream_index = stream->index;
|
pkt->stream_index = stream->index;
|
||||||
|
|
59
src/zmc.cpp
59
src/zmc.cpp
|
@ -54,7 +54,6 @@ possible, this should run at more or less constant speed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_analysis_thread.h"
|
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
#include "zm_db.h"
|
#include "zm_db.h"
|
||||||
#include "zm_define.h"
|
#include "zm_define.h"
|
||||||
|
@ -272,8 +271,6 @@ int main(int argc, char *argv[]) {
|
||||||
} // end foreach monitor
|
} // end foreach monitor
|
||||||
if (zm_terminate) break;
|
if (zm_terminate) break;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<AnalysisThread>> analysis_threads = std::vector<std::unique_ptr<AnalysisThread>>();
|
|
||||||
|
|
||||||
int *capture_delays = new int[monitors.size()];
|
int *capture_delays = new int[monitors.size()];
|
||||||
int *alarm_capture_delays = new int[monitors.size()];
|
int *alarm_capture_delays = new int[monitors.size()];
|
||||||
struct timeval * last_capture_times = new struct timeval[monitors.size()];
|
struct timeval * last_capture_times = new struct timeval[monitors.size()];
|
||||||
|
@ -284,12 +281,6 @@ int main(int argc, char *argv[]) {
|
||||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||||
Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)",
|
Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)",
|
||||||
capture_delays[i], alarm_capture_delays[i]);
|
capture_delays[i], alarm_capture_delays[i]);
|
||||||
|
|
||||||
Monitor::Function function = monitors[0]->GetFunction();
|
|
||||||
if (function != Monitor::MONITOR) {
|
|
||||||
Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id());
|
|
||||||
analysis_threads.emplace_back(ZM::make_unique<AnalysisThread>(monitors[i]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
@ -320,29 +311,31 @@ int main(int argc, char *argv[]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gettimeofday(&now, nullptr);
|
|
||||||
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate.
|
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate.
|
||||||
int delay = (monitors[i]->GetState() == Monitor::ALARM) ? alarm_capture_delays[i] : capture_delays[i];
|
int delay = (monitors[i]->GetState() == Monitor::ALARM) ? alarm_capture_delays[i] : capture_delays[i];
|
||||||
if (delay && last_capture_times[i].tv_sec) {
|
if (delay) {
|
||||||
// DT_PREC_3 means that the value will be in thousands of a second
|
gettimeofday(&now, nullptr);
|
||||||
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_6);
|
if (last_capture_times[i].tv_sec) {
|
||||||
|
// DT_PREC_3 means that the value will be in thousands of a second
|
||||||
|
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_6);
|
||||||
|
|
||||||
// You have to add back in the previous sleep time
|
// You have to add back in the previous sleep time
|
||||||
sleep_time = delay - (delta_time.delta - sleep_time);
|
sleep_time = delay - (delta_time.delta - sleep_time);
|
||||||
Debug(4, "Sleep time is %d from now:%d.%d last:%d.%d delta %d delay: %d",
|
Debug(4, "Sleep time is %d from now:%d.%d last:%d.%d delta %d delay: %d",
|
||||||
sleep_time,
|
sleep_time,
|
||||||
now.tv_sec, now.tv_usec,
|
now.tv_sec, now.tv_usec,
|
||||||
last_capture_times[i].tv_sec, last_capture_times[i].tv_usec,
|
last_capture_times[i].tv_sec, last_capture_times[i].tv_usec,
|
||||||
delta_time.delta,
|
delta_time.delta,
|
||||||
delay
|
delay
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sleep_time > 0) {
|
if (sleep_time > 0) {
|
||||||
Debug(4, "usleeping (%d)", sleep_time);
|
Debug(4, "usleeping (%d)", sleep_time);
|
||||||
usleep(sleep_time);
|
usleep(sleep_time);
|
||||||
}
|
}
|
||||||
} // end if has a last_capture time
|
} // end if has a last_capture time
|
||||||
last_capture_times[i] = now;
|
last_capture_times[i] = now;
|
||||||
|
} // end if delay
|
||||||
} // end foreach n_monitors
|
} // end foreach n_monitors
|
||||||
|
|
||||||
if ((result < 0) or zm_reload) {
|
if ((result < 0) or zm_reload) {
|
||||||
|
@ -351,16 +344,10 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
} // end while ! zm_terminate and connected
|
} // end while ! zm_terminate and connected
|
||||||
|
|
||||||
for (std::unique_ptr<AnalysisThread> &analysis_thread: analysis_threads)
|
|
||||||
analysis_thread->Stop();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < monitors.size(); i++) {
|
for (size_t i = 0; i < monitors.size(); i++) {
|
||||||
monitors[i]->Close();
|
monitors[i]->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Killoff the analysis threads. Don't need them spinning while we try to reconnect
|
|
||||||
analysis_threads.clear();
|
|
||||||
|
|
||||||
delete [] alarm_capture_delays;
|
delete [] alarm_capture_delays;
|
||||||
delete [] capture_delays;
|
delete [] capture_delays;
|
||||||
delete [] last_capture_times;
|
delete [] last_capture_times;
|
||||||
|
@ -385,7 +372,7 @@ int main(int argc, char *argv[]) {
|
||||||
for (std::shared_ptr<Monitor> &monitor : monitors) {
|
for (std::shared_ptr<Monitor> &monitor : monitors) {
|
||||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='NotRunning'",
|
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
|
||||||
monitor->Id());
|
monitor->Id());
|
||||||
zmDbDo(sql);
|
zmDbDo(sql);
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,8 +241,9 @@ int main(int argc, const char *argv[], char **envp) {
|
||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
char date_string[64];
|
char date_string[64];
|
||||||
|
tm now_tm = {};
|
||||||
strftime(date_string, sizeof(date_string)-1,
|
strftime(date_string, sizeof(date_string)-1,
|
||||||
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
"%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&now, &now_tm));
|
||||||
|
|
||||||
fputs("Last-Modified: ", stdout);
|
fputs("Last-Modified: ", stdout);
|
||||||
fputs(date_string, stdout);
|
fputs(date_string, stdout);
|
||||||
|
|
|
@ -506,8 +506,10 @@ int main(int argc, char *argv[]) {
|
||||||
struct timeval timestamp = monitor->GetTimestamp(image_idx);
|
struct timeval timestamp = monitor->GetTimestamp(image_idx);
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
char timestamp_str[64] = "None";
|
char timestamp_str[64] = "None";
|
||||||
if ( timestamp.tv_sec )
|
if ( timestamp.tv_sec ) {
|
||||||
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime(×tamp.tv_sec));
|
tm tm_info = {};
|
||||||
|
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime_r(×tamp.tv_sec, &tm_info));
|
||||||
|
}
|
||||||
if ( image_idx == -1 )
|
if ( image_idx == -1 )
|
||||||
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
|
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
|
||||||
else
|
else
|
||||||
|
|
|
@ -117,6 +117,17 @@ commonprep () {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -e "build/RtspServer" ]; then
|
||||||
|
echo "Found existing RtspServer..."
|
||||||
|
else
|
||||||
|
echo "Cloning RtspServer ..."
|
||||||
|
git clone https://github.com/ZoneMinder/RtspServer build/RtspServer
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "ERROR: RtspServer clone failed..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Uncompress the submodule tarballs and move them into place
|
# Uncompress the submodule tarballs and move them into place
|
||||||
|
@ -137,6 +148,13 @@ movecrud () {
|
||||||
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior
|
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior
|
||||||
mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
|
mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
|
||||||
fi
|
fi
|
||||||
|
if [ -e "dep/RtspServer/CMakeLists.txt" ]; then
|
||||||
|
echo "RtspServer already installed..."
|
||||||
|
else
|
||||||
|
echo "Copying RtspServer..."
|
||||||
|
rm -r dep/RtspServer
|
||||||
|
cp -Rpd build/RtspServer dep/RtspServer
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# previsouly part of installzm.sh
|
# previsouly part of installzm.sh
|
||||||
|
@ -347,7 +365,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
|
||||||
setdebpkgname
|
setdebpkgname
|
||||||
movecrud
|
movecrud
|
||||||
|
|
||||||
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "buster" ]; then
|
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "groovy" ] || [ "${DIST}" == "hirsuit" ] || [ "${DIST}" == "buster" ]; then
|
||||||
ln -sfT distros/ubuntu2004 debian
|
ln -sfT distros/ubuntu2004 debian
|
||||||
elif [ "${DIST}" == "beowulf" ]; then
|
elif [ "${DIST}" == "beowulf" ]; then
|
||||||
ln -sfT distros/beowulf debian
|
ln -sfT distros/beowulf debian
|
||||||
|
|
|
@ -344,7 +344,7 @@ class MonitorsController extends AppController {
|
||||||
public function daemonControl($id, $command, $daemon=null) {
|
public function daemonControl($id, $command, $daemon=null) {
|
||||||
// Need to see if it is local or remote
|
// Need to see if it is local or remote
|
||||||
$monitor = $this->Monitor->find('first', array(
|
$monitor = $this->Monitor->find('first', array(
|
||||||
'fields' => array('Type', 'Function', 'Device', 'ServerId'),
|
'fields' => array('Id', 'Type', 'Function', 'Device', 'ServerId'),
|
||||||
'conditions' => array('Id' => $id)
|
'conditions' => array('Id' => $id)
|
||||||
));
|
));
|
||||||
$monitor = $monitor['Monitor'];
|
$monitor = $monitor['Monitor'];
|
||||||
|
|
|
@ -83,7 +83,7 @@ class Monitor extends ZM_Object {
|
||||||
'LabelX' => 0,
|
'LabelX' => 0,
|
||||||
'LabelY' => 0,
|
'LabelY' => 0,
|
||||||
'LabelSize' => 1,
|
'LabelSize' => 1,
|
||||||
'ImageBufferCount' => 20,
|
'ImageBufferCount' => 3,
|
||||||
'WarmupCount' => 0,
|
'WarmupCount' => 0,
|
||||||
'PreEventCount' => 5,
|
'PreEventCount' => 5,
|
||||||
'PostEventCount' => 5,
|
'PostEventCount' => 5,
|
||||||
|
|
|
@ -5,6 +5,7 @@ require_once('database.php');
|
||||||
$object_cache = array();
|
$object_cache = array();
|
||||||
|
|
||||||
class ZM_Object {
|
class ZM_Object {
|
||||||
|
protected $_last_error;
|
||||||
|
|
||||||
public function __construct($IdOrRow = NULL) {
|
public function __construct($IdOrRow = NULL) {
|
||||||
$class = get_class($this);
|
$class = get_class($this);
|
||||||
|
@ -38,6 +39,7 @@ class ZM_Object {
|
||||||
|
|
||||||
public function __call($fn, array $args){
|
public function __call($fn, array $args){
|
||||||
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
|
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
|
||||||
|
|
||||||
if ( count($args) ) {
|
if ( count($args) ) {
|
||||||
if ( $type == 'set' and is_array($args[0]) ) {
|
if ( $type == 'set' and is_array($args[0]) ) {
|
||||||
$this->{$fn} = implode(',', $args[0]);
|
$this->{$fn} = implode(',', $args[0]);
|
||||||
|
@ -50,7 +52,15 @@ class ZM_Object {
|
||||||
$this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]);
|
$this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->{$fn} = $args[0];
|
if ( $args[0] == '' and array_key_exists($fn, $this->defaults) ) {
|
||||||
|
if ( is_array($this->defaults[$fn]) ) {
|
||||||
|
$this->{$fn} = $this->defaults[$fn]['default'];
|
||||||
|
} else {
|
||||||
|
$this->{$fn} = $this->defaults[$fn];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->{$fn} = $args[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +184,7 @@ class ZM_Object {
|
||||||
$this->$field($value);
|
$this->$field($value);
|
||||||
} else {
|
} else {
|
||||||
if ( is_array($value) ) {
|
if ( is_array($value) ) {
|
||||||
# perhaps should turn into a comma-separated string
|
# perhaps should turn into a comma-separated string
|
||||||
$this->{$field} = implode(',', $value);
|
$this->{$field} = implode(',', $value);
|
||||||
} else if ( is_string($value) ) {
|
} else if ( is_string($value) ) {
|
||||||
if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) {
|
if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) {
|
||||||
|
@ -185,8 +195,14 @@ class ZM_Object {
|
||||||
} else {
|
} else {
|
||||||
$this->{$field} = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
|
$this->{$field} = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
|
||||||
}
|
}
|
||||||
|
} else if ( $value == '' and array_key_exists($field, $this->defaults) ) {
|
||||||
|
if ( is_array($this->defaults[$field]) ) {
|
||||||
|
$this->{$field} = $this->defaults[$field]['default'];
|
||||||
|
} else {
|
||||||
|
$this->{$field} = $this->defaults[$field];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->{$field} = trim($value);
|
$this->{$field} = $value;
|
||||||
}
|
}
|
||||||
} else if ( is_integer($value) ) {
|
} else if ( is_integer($value) ) {
|
||||||
$this->{$field} = $value;
|
$this->{$field} = $value;
|
||||||
|
@ -207,8 +223,8 @@ class ZM_Object {
|
||||||
public function changes($new_values, $defaults=null) {
|
public function changes($new_values, $defaults=null) {
|
||||||
$changes = array();
|
$changes = array();
|
||||||
|
|
||||||
if ( $defaults ) {
|
if ($defaults) {
|
||||||
foreach ( $defaults as $field => $type ) {
|
foreach ($defaults as $field => $type) {
|
||||||
if ( isset($new_values[$field]) ) {
|
if ( isset($new_values[$field]) ) {
|
||||||
# Will have already been handled above
|
# Will have already been handled above
|
||||||
continue;
|
continue;
|
||||||
|
@ -247,47 +263,40 @@ class ZM_Object {
|
||||||
} else if ( $this->$field() != $value ) {
|
} else if ( $this->$field() != $value ) {
|
||||||
$changes[$field] = $value;
|
$changes[$field] = $value;
|
||||||
}
|
}
|
||||||
} else if ( property_exists($this, $field) ) {
|
} else if (property_exists($this, $field)) {
|
||||||
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
||||||
if ( $type == 'set' ) {
|
if ($type == 'set') {
|
||||||
$old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array());
|
$old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array());
|
||||||
$new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array());
|
$new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array());
|
||||||
|
|
||||||
$diff = array_recursive_diff($old_value, $new_value);
|
$diff = array_recursive_diff($old_value, $new_value);
|
||||||
if ( count($diff) ) {
|
if (count($diff)) $changes[$field] = $new_value;
|
||||||
$changes[$field] = $new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Input might be a command separated string, or an array
|
# Input might be a command separated string, or an array
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) {
|
if (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp'])) {
|
||||||
if ( is_array($this->defaults[$field]['filter_regexp']) ) {
|
if (is_array($this->defaults[$field]['filter_regexp'])) {
|
||||||
foreach ( $this->defaults[$field]['filter_regexp'] as $regexp ) {
|
foreach ($this->defaults[$field]['filter_regexp'] as $regexp) {
|
||||||
$value = preg_replace($regexp, '', trim($value));
|
$value = preg_replace($regexp, '', trim($value));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
|
$value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( $this->{$field} != $value ) {
|
if ($this->{$field} != $value) $changes[$field] = $value;
|
||||||
$changes[$field] = $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if ( array_key_exists($field, $this->defaults) ) {
|
} else if (array_key_exists($field, $this->defaults)) {
|
||||||
if ( is_array($this->defaults[$field]) and isset($this->defaults[$field]['default']) ) {
|
if (is_array($this->defaults[$field]) and isset($this->defaults[$field]['default'])) {
|
||||||
$default = $this->defaults[$field]['default'];
|
$default = $this->defaults[$field]['default'];
|
||||||
} else {
|
} else {
|
||||||
$default = $this->defaults[$field];
|
$default = $this->defaults[$field];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $default != $value ) {
|
if ($default != $value) $changes[$field] = $value;
|
||||||
$changes[$field] = $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} # end foreach newvalue
|
} # end foreach newvalue
|
||||||
|
|
||||||
|
|
||||||
return $changes;
|
return $changes;
|
||||||
} # end public function changes
|
} # end public function changes
|
||||||
|
|
||||||
|
@ -302,14 +311,20 @@ class ZM_Object {
|
||||||
# Set defaults. Note that we only replace "" with null, not other values
|
# Set defaults. Note that we only replace "" with null, not other values
|
||||||
# because for example if we want to clear TimestampFormat, we clear it, but the default is a string value
|
# because for example if we want to clear TimestampFormat, we clear it, but the default is a string value
|
||||||
foreach ( $this->defaults as $field => $default ) {
|
foreach ( $this->defaults as $field => $default ) {
|
||||||
if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) {
|
if (!property_exists($this, $field)) {
|
||||||
if ( is_array($default) ) {
|
if (is_array($default)) {
|
||||||
$this->{$field} = $default['default'];
|
$this->{$field} = $default['default'];
|
||||||
} else if ( $default == null ) {
|
} else {
|
||||||
|
$this->{$field} = $default;
|
||||||
|
}
|
||||||
|
} else if ($this->{$field} === '') {
|
||||||
|
if (is_array($default)) {
|
||||||
|
$this->{$field} = $default['default'];
|
||||||
|
} else if ($default == null) {
|
||||||
$this->{$field} = $default;
|
$this->{$field} = $default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} # end foreach default
|
||||||
|
|
||||||
$fields = array_filter(
|
$fields = array_filter(
|
||||||
$this->defaults,
|
$this->defaults,
|
||||||
|
@ -329,8 +344,7 @@ class ZM_Object {
|
||||||
$sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?';
|
$sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?';
|
||||||
$values = array_map(function($field){ return $this->{$field};}, $fields);
|
$values = array_map(function($field){ return $this->{$field};}, $fields);
|
||||||
$values[] = $this->{'Id'};
|
$values[] = $this->{'Id'};
|
||||||
if ( dbQuery($sql, $values) )
|
if (dbQuery($sql, $values)) return true;
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
unset($fields['Id']);
|
unset($fields['Id']);
|
||||||
|
|
||||||
|
@ -339,15 +353,16 @@ class ZM_Object {
|
||||||
') VALUES ('.
|
') VALUES ('.
|
||||||
implode(', ', array_map(function($field){return $this->$field() == 'NOW()' ? 'NOW()' : '?';}, $fields)).')';
|
implode(', ', array_map(function($field){return $this->$field() == 'NOW()' ? 'NOW()' : '?';}, $fields)).')';
|
||||||
|
|
||||||
$values = array_values(array_map(
|
# For some reason comparing 0 to 'NOW()' returns false; So we do this.
|
||||||
function($field){return $this->$field();},
|
$filtered = array_filter($fields, function($field){ return ( (!$this->$field()) or ($this->$field() != 'NOW()'));});
|
||||||
array_filter($fields, function($field){ return $this->$field() != 'NOW()';})
|
$mapped = array_map(function($field){return $this->$field();}, $filtered);
|
||||||
));
|
$values = array_values($mapped);
|
||||||
if ( dbQuery($sql, $values) ) {
|
if (dbQuery($sql, $values)) {
|
||||||
$this->{'Id'} = dbInsertId();
|
$this->{'Id'} = dbInsertId();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->_last_error = dbError($sql);
|
||||||
return false;
|
return false;
|
||||||
} // end function save
|
} // end function save
|
||||||
|
|
||||||
|
@ -421,5 +436,8 @@ class ZM_Object {
|
||||||
public function remove_from_cache() {
|
public function remove_from_cache() {
|
||||||
return ZM_Object::_remove_from_cache(get_class(), $this);
|
return ZM_Object::_remove_from_cache(get_class(), $this);
|
||||||
}
|
}
|
||||||
|
public function get_last_error() {
|
||||||
|
return $this->_last_error;
|
||||||
|
}
|
||||||
} # end class Object
|
} # end class Object
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -40,10 +40,12 @@ if ( $action == 'function' ) {
|
||||||
$newFunction = validStr($_REQUEST['newFunction']);
|
$newFunction = validStr($_REQUEST['newFunction']);
|
||||||
# Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0
|
# Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0
|
||||||
$newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
|
$newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
|
||||||
|
$newDecodingEnabled = ( !isset($_REQUEST['newDecodingEnabled']) or $_REQUEST['newDecodingEnabled'] != '1' ) ? '0' : '1';
|
||||||
$oldFunction = $monitor->Function();
|
$oldFunction = $monitor->Function();
|
||||||
$oldEnabled = $monitor->Enabled();
|
$oldEnabled = $monitor->Enabled();
|
||||||
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
|
$oldDecodingEnabled = $monitor->DecodingEnabled();
|
||||||
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled));
|
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled || $newDecodingEnabled != $oldDecodingEnabled ) {
|
||||||
|
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled, 'DecodingEnabled'=>$newDecodingEnabled));
|
||||||
|
|
||||||
if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) {
|
if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) {
|
||||||
$monitor->zmcControl(($newFunction != 'None') ? 'restart' : 'stop');
|
$monitor->zmcControl(($newFunction != 'None') ? 'restart' : 'stop');
|
||||||
|
|
|
@ -18,77 +18,72 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( $action == 'Save' ) {
|
global $error_message;
|
||||||
if ( canEdit('System') ) {
|
|
||||||
if ( !empty($_REQUEST['uid']) ) {
|
|
||||||
$dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid']));
|
|
||||||
} else {
|
|
||||||
$dbUser = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$types = array();
|
if ($action == 'Save') {
|
||||||
if ( isset($_REQUEST['newUser']['MonitorIds']) and is_array($_REQUEST['newUser']['MonitorIds']) )
|
require_once('includes/User.php');
|
||||||
|
$uid = isset($_REQUEST['uid']) ? validInt($_REQUEST['uid']) : 0;
|
||||||
|
$dbUser = new ZM\User($uid);
|
||||||
|
|
||||||
|
if (canEdit('System')) {
|
||||||
|
# Need to check for uniqueness of Username
|
||||||
|
$user_with_my_username = ZM\User::find_one(array('Username'=>$_REQUEST['newUser']['Username']));
|
||||||
|
if ($user_with_my_username and
|
||||||
|
( ( $uid and ($user_with_my_username->Id() != $uid) ) or !$uid)
|
||||||
|
) {
|
||||||
|
$error_message = 'There already exists a user with this Username<br/>';
|
||||||
|
unset($_REQUEST['redirect']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
# What other tests should we do?
|
||||||
|
|
||||||
|
if (isset($_REQUEST['newUser']['MonitorIds']) and is_array($_REQUEST['newUser']['MonitorIds']))
|
||||||
$_REQUEST['newUser']['MonitorIds'] = implode(',', $_REQUEST['newUser']['MonitorIds']);
|
$_REQUEST['newUser']['MonitorIds'] = implode(',', $_REQUEST['newUser']['MonitorIds']);
|
||||||
if ( !$_REQUEST['newUser']['Password'] )
|
if (!empty($_REQUEST['newUser']['Password'])) {
|
||||||
|
$_REQUEST['newUser']['Password'] = password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT);
|
||||||
|
} else {
|
||||||
unset($_REQUEST['newUser']['Password']);
|
unset($_REQUEST['newUser']['Password']);
|
||||||
|
|
||||||
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
|
|
||||||
|
|
||||||
if ( isset($_REQUEST['newUser']['Password']) ) {
|
|
||||||
if ( function_exists('password_hash') ) {
|
|
||||||
$pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"';
|
|
||||||
} else {
|
|
||||||
$pass_hash = ' PASSWORD('.dbEscape($_REQUEST['newUser']['Password']).') ';
|
|
||||||
ZM\Info('Cannot use bcrypt as you are using PHP < 5.3');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $_REQUEST['newUser']['Password'] ) {
|
|
||||||
$changes['Password'] = 'Password = '.$pass_hash;
|
|
||||||
} else {
|
|
||||||
unset($changes['Password']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$changes = $dbUser->changes($_REQUEST['newUser']);
|
||||||
|
ZM\Debug("Changes: " . print_r($changes, true));
|
||||||
|
|
||||||
if ( count($changes) ) {
|
if (count($changes)) {
|
||||||
if ( !empty($_REQUEST['uid']) ) {
|
if (!$dbUser->save($changes)) {
|
||||||
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid']));
|
$error_message = $dbUser->get_last_error();
|
||||||
# If we are updating the logged in user, then update our session user data.
|
unset($_REQUEST['redirect']);
|
||||||
if ( $user and ( $dbUser['Username'] == $user['Username'] ) ) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($uid) {
|
||||||
|
if ($user and ($dbUser->Username() == $user['Username'])) {
|
||||||
# We are the logged in user, need to update the $user object and generate a new auth_hash
|
# We are the logged in user, need to update the $user object and generate a new auth_hash
|
||||||
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?';
|
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?';
|
||||||
$user = dbFetchOne($sql, NULL, array($_REQUEST['uid']));
|
$user = dbFetchOne($sql, NULL, array($uid));
|
||||||
|
|
||||||
# Have to update auth hash in session
|
# Have to update auth hash in session
|
||||||
zm_session_start();
|
zm_session_start();
|
||||||
generateAuthHash(ZM_AUTH_HASH_IPS, true);
|
generateAuthHash(ZM_AUTH_HASH_IPS, true);
|
||||||
session_write_close();
|
session_write_close();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
|
|
||||||
}
|
}
|
||||||
} # end if changes
|
} # end if changes
|
||||||
} else if ( ZM_USER_SELF_EDIT and ( $_REQUEST['uid'] == $user['Id'] ) ) {
|
} else if (ZM_USER_SELF_EDIT and ($uid == $user['Id'])) {
|
||||||
$uid = $user['Id'];
|
if (!empty($_REQUEST['newUser']['Password'])) {
|
||||||
|
$_REQUEST['newUser']['Password'] = password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT);
|
||||||
$dbUser = dbFetchOne('SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid));
|
|
||||||
|
|
||||||
$types = array();
|
|
||||||
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
|
|
||||||
|
|
||||||
if ( function_exists('password_hash') ) {
|
|
||||||
$pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"';
|
|
||||||
} else {
|
} else {
|
||||||
$pass_hash = ' PASSWORD('.dbEscape($_REQUEST['newUser']['Password']).') ';
|
unset($_REQUEST['newUser']['Password']);
|
||||||
ZM\Info ('Cannot use bcrypt as you are using PHP < 5.3');
|
|
||||||
}
|
}
|
||||||
|
$fields = array('Password'=>'', 'Language'=>'', 'HomeView'=>'');
|
||||||
|
ZM\Debug("changes: ".print_r(array_intersect_key($_REQUEST['newUser'], $fields),true));
|
||||||
|
$changes = $dbUser->changes(array_intersect_key($_REQUEST['newUser'], $fields));
|
||||||
|
ZM\Debug("changes: ".print_r($changes, true));
|
||||||
|
|
||||||
if ( !empty($_REQUEST['newUser']['Password']) ) {
|
if (count($changes)) {
|
||||||
$changes['Password'] = 'Password = '.$pass_hash;
|
if (!$dbUser->save($changes)) {
|
||||||
} else {
|
$error_message = $dbUser->get_last_error();
|
||||||
unset($changes['Password']);
|
unset($_REQUEST['redirect']);
|
||||||
}
|
return;
|
||||||
if ( count($changes) ) {
|
}
|
||||||
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
|
|
||||||
|
|
||||||
# We are the logged in user, need to update the $user object and generate a new auth_hash
|
# We are the logged in user, need to update the $user object and generate a new auth_hash
|
||||||
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?';
|
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?';
|
||||||
|
|
|
@ -98,7 +98,7 @@ if ( isset($_GET['skin']) ) {
|
||||||
$skin = 'classic';
|
$skin = 'classic';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! is_dir("skins/$skin") ) {
|
if (!is_dir('skins/'.$skin) ) {
|
||||||
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
|
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
|
||||||
|
|
||||||
if ( !in_array($skin, $skins) ) {
|
if ( !in_array($skin, $skins) ) {
|
||||||
|
@ -117,10 +117,10 @@ if ( isset($_GET['css']) ) {
|
||||||
$css = 'classic';
|
$css = 'classic';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !is_dir("skins/$skin/css/$css") ) {
|
if (!is_dir("skins/$skin/css/$css")) {
|
||||||
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR));
|
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR));
|
||||||
if ( count($css_skins) ) {
|
if (count($css_skins)) {
|
||||||
if ( !in_array($css, $css_skins) ) {
|
if (!in_array($css, $css_skins)) {
|
||||||
ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]);
|
ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]);
|
||||||
$css = $css_skins[0];
|
$css = $css_skins[0];
|
||||||
} else {
|
} else {
|
||||||
|
@ -137,7 +137,7 @@ define('ZM_SKIN_PATH', "skins/$skin");
|
||||||
define('ZM_SKIN_NAME', $skin);
|
define('ZM_SKIN_NAME', $skin);
|
||||||
|
|
||||||
$skinBase = array(); // To allow for inheritance of skins
|
$skinBase = array(); // To allow for inheritance of skins
|
||||||
if ( !file_exists(ZM_SKIN_PATH) )
|
if (!file_exists(ZM_SKIN_PATH))
|
||||||
ZM\Fatal("Invalid skin '$skin'");
|
ZM\Fatal("Invalid skin '$skin'");
|
||||||
$skinBase[] = $skin;
|
$skinBase[] = $skin;
|
||||||
|
|
||||||
|
@ -183,9 +183,6 @@ $user = null;
|
||||||
if ( isset($_REQUEST['view']) )
|
if ( isset($_REQUEST['view']) )
|
||||||
$view = detaintPath($_REQUEST['view']);
|
$view = detaintPath($_REQUEST['view']);
|
||||||
|
|
||||||
if ( isset($_REQUEST['redirect']) )
|
|
||||||
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
|
|
||||||
|
|
||||||
# Add CSP Headers
|
# Add CSP Headers
|
||||||
$cspNonce = bin2hex(zm_random_bytes(16));
|
$cspNonce = bin2hex(zm_random_bytes(16));
|
||||||
|
|
||||||
|
@ -265,6 +262,8 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n
|
||||||
$request = null;
|
$request = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( isset($_REQUEST['redirect']) )
|
||||||
|
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
|
||||||
|
|
||||||
if ( $redirect ) {
|
if ( $redirect ) {
|
||||||
ZM\Debug("Redirecting to $redirect");
|
ZM\Debug("Redirecting to $redirect");
|
||||||
|
|
|
@ -16,3 +16,14 @@ tr.log-dbg td {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th[data-field="DateTime"],
|
||||||
|
th[data-field="Component"],
|
||||||
|
th[data-field="Code"],
|
||||||
|
th[data-field="Message"],
|
||||||
|
th[data-field="File"],
|
||||||
|
th[data-field="Line"] {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th[data-field="DateTime"] {
|
||||||
|
min-width: 135px;
|
||||||
|
}
|
||||||
|
|
|
@ -58,8 +58,8 @@ function processRows(rows) {
|
||||||
var emailed = row.Emailed == yesString ? emailedString : '';
|
var emailed = row.Emailed == yesString ? emailedString : '';
|
||||||
|
|
||||||
row.Id = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&page=1">' + eid + '</a>';
|
row.Id = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&page=1">' + eid + '</a>';
|
||||||
row.Name = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&page=1">' + row.Name + '</a>'
|
row.Name = '<a href="?view=event&eid=' + eid + filterQuery + sortQuery + '&page=1">' + row.Name + '</a>' +
|
||||||
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
|
'<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
|
||||||
if ( canEdit.Monitors ) row.Monitor = '<a href="?view=event&eid=' + eid + '">' + row.Monitor + '</a>';
|
if ( canEdit.Monitors ) row.Monitor = '<a href="?view=event&eid=' + eid + '">' + row.Monitor + '</a>';
|
||||||
if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
|
if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
|
||||||
if ( row.Notes.indexOf('detected:') >= 0 ) {
|
if ( row.Notes.indexOf('detected:') >= 0 ) {
|
||||||
|
|
|
@ -40,10 +40,8 @@ function validateForm(form) {
|
||||||
var have_endtime_term = false;
|
var have_endtime_term = false;
|
||||||
for ( var i = 0; i < rows.length; i++ ) {
|
for ( var i = 0; i < rows.length; i++ ) {
|
||||||
if (
|
if (
|
||||||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDateTime' )
|
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDateTime' ) ||
|
||||||
||
|
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndTime' ) ||
|
||||||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndTime' )
|
|
||||||
||
|
|
||||||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDate' )
|
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDate' )
|
||||||
) {
|
) {
|
||||||
have_endtime_term = true;
|
have_endtime_term = true;
|
||||||
|
|
|
@ -48,6 +48,7 @@ function updateMonitorDimensions(element) {
|
||||||
form.elements['newMonitor[Height]'].value = dimensions[1];
|
form.elements['newMonitor[Height]'].value = dimensions[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
update_estimated_ram_use();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,7 +271,9 @@ function reloadWebSite(ndx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function takeSnapshot() {
|
function takeSnapshot() {
|
||||||
monitor_ids = monitorData.map( monitor=> { return 'monitor_ids[]='+monitor.id; });
|
monitor_ids = monitorData.map((monitor)=>{
|
||||||
|
return 'monitor_ids[]='+monitor.id;
|
||||||
|
});
|
||||||
window.location = '?view=snapshot&action=create&'+monitor_ids.join('&');
|
window.location = '?view=snapshot&action=create&'+monitor_ids.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ function manageDelConfirmModalBtns() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPage() {
|
function initPage() {
|
||||||
|
|
||||||
// enable or disable buttons based on current selection and user rights
|
// enable or disable buttons based on current selection and user rights
|
||||||
/*
|
/*
|
||||||
renameBtn.prop('disabled', !canEdit.Events);
|
renameBtn.prop('disabled', !canEdit.Events);
|
||||||
|
|
|
@ -48,7 +48,6 @@ function ajaxRequest(params) {
|
||||||
|
|
||||||
function processRows(rows) {
|
function processRows(rows) {
|
||||||
$j.each(rows, function(ndx, row) {
|
$j.each(rows, function(ndx, row) {
|
||||||
|
|
||||||
var id = row.Id;
|
var id = row.Id;
|
||||||
row.Id = '<a href="?view=snapshot&id=' + id + '">' + id + '</a>';
|
row.Id = '<a href="?view=snapshot&id=' + id + '">' + id + '</a>';
|
||||||
row.Name = '<a href="?view=snapshot&id=' + id + '">' + row.Name + '</a>';
|
row.Name = '<a href="?view=snapshot&id=' + id + '">' + row.Name + '</a>';
|
||||||
|
@ -192,7 +191,7 @@ function initPage() {
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Manage the ARCHIVE button
|
// Manage the ARCHIVE button
|
||||||
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
|
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
|
||||||
var selections = getIdSelections();
|
var selections = getIdSelections();
|
||||||
|
@ -268,7 +267,7 @@ function initPage() {
|
||||||
})
|
})
|
||||||
.fail(logAjaxFail);
|
.fail(logAjaxFail);
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
// Manage the DELETE button
|
// Manage the DELETE button
|
||||||
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
|
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
|
||||||
if ( ! canEdit.Events ) {
|
if ( ! canEdit.Events ) {
|
||||||
|
|
|
@ -202,7 +202,7 @@ function toPercent(field, maxValue) {
|
||||||
field.value = 100;
|
field.value = 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.setAttribute('step', 0.01);
|
field.setAttribute('step', 'any');
|
||||||
field.setAttribute('max', 100);
|
field.setAttribute('max', 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,16 +37,14 @@ $mid = null;
|
||||||
$monitor = null;
|
$monitor = null;
|
||||||
if ( !empty($_REQUEST['mid']) ) {
|
if ( !empty($_REQUEST['mid']) ) {
|
||||||
$mid = validInt($_REQUEST['mid']);
|
$mid = validInt($_REQUEST['mid']);
|
||||||
|
|
||||||
$monitor = new ZM\Monitor($mid);
|
$monitor = new ZM\Monitor($mid);
|
||||||
if ( $monitor and ZM_OPT_X10 )
|
if ( $monitor and ZM_OPT_X10 )
|
||||||
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($mid));
|
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($mid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$monitor ) {
|
if ( !$monitor ) {
|
||||||
$nextId = getTableAutoInc('Monitors');
|
|
||||||
$monitor = new ZM\Monitor();
|
$monitor = new ZM\Monitor();
|
||||||
$monitor->Name(translate('Monitor').'-'.$nextId);
|
$monitor->Name(translate('Monitor').'-'.getTableAutoInc('Monitors'));
|
||||||
$monitor->WebColour(random_colour());
|
$monitor->WebColour(random_colour());
|
||||||
} # end if $_REQUEST['mid']
|
} # end if $_REQUEST['mid']
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,6 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as
|
||||||
$userMonitors[] = $monitors[$monitorId]['Name'];
|
$userMonitors[] = $monitors[$monitorId]['Name'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ZM\Debug("monitors: ".$user_row['Username'] . ' ' . $user_row['MonitorIds']. ' :' . print_r($userMonitors, true));
|
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="colUsername"><?php echo makeLink('?view=user&uid='.$user_row['Id'], validHtmlStr($user_row['Username']).($user['Username']==$user_row['Username']?'*':''), $canEdit) ?></td>
|
<td class="colUsername"><?php echo makeLink('?view=user&uid='.$user_row['Id'], validHtmlStr($user_row['Username']).($user['Username']==$user_row['Username']?'*':''), $canEdit) ?></td>
|
||||||
|
|
|
@ -25,7 +25,7 @@ if ( !canEdit('System') && !$selfEdit ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
require('includes/User.php');
|
require_once('includes/User.php');
|
||||||
|
|
||||||
if ( $_REQUEST['uid'] ) {
|
if ( $_REQUEST['uid'] ) {
|
||||||
if ( !($newUser = new ZM\User($_REQUEST['uid'])) ) {
|
if ( !($newUser = new ZM\User($_REQUEST['uid'])) ) {
|
||||||
|
@ -54,9 +54,9 @@ foreach ( dbFetchAll($sql) as $monitor ) {
|
||||||
$focusWindow = true;
|
$focusWindow = true;
|
||||||
|
|
||||||
xhtmlHeaders(__FILE__, translate('User').' - '.$newUser->Username());
|
xhtmlHeaders(__FILE__, translate('User').' - '.$newUser->Username());
|
||||||
|
echo getBodyTopHTML();
|
||||||
|
echo getNavBarHTML();
|
||||||
?>
|
?>
|
||||||
<body>
|
|
||||||
<?php echo getNavBarHTML() ?>
|
|
||||||
<div id="page">
|
<div id="page">
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<div class="float-left pl-3 pt-1">
|
<div class="float-left pl-3 pt-1">
|
||||||
|
|
|
@ -206,7 +206,7 @@ if ( count($other_zones) ) {
|
||||||
array('data-on-change'=>'applyZoneUnits', 'id'=>'newZone[Units]')
|
array('data-on-change'=>'applyZoneUnits', 'id'=>'newZone[Units]')
|
||||||
);
|
);
|
||||||
# Used later for number inputs
|
# Used later for number inputs
|
||||||
$step = $newZone['Units'] == 'Percent' ? ' step="0.01" max="100"' : '';
|
$step = $newZone['Units'] == 'Percent' ? ' step="any" max="100"' : '';
|
||||||
?>
|
?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
Loading…
Reference in New Issue