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
|
||||
INTERFACE
|
||||
-Wall
|
||||
-Wconditionally-supported
|
||||
-Wextra
|
||||
-Wformat-security
|
||||
-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");
|
||||
}
|
||||
|
||||
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 "
|
||||
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
||||
);
|
||||
|
@ -166,7 +174,7 @@ if ( $options{command} ) {
|
|||
my $win = $rin;
|
||||
my $ein = $win;
|
||||
my $timeout = MAX_COMMAND_WAIT;
|
||||
while ( 1 ) {
|
||||
while (!$zm_terminate) {
|
||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||
if ( $nfound > 0 ) {
|
||||
if ( vec($rout, fileno(SERVER), 1) ) {
|
||||
|
@ -202,7 +210,7 @@ if ( $options{command} ) {
|
|||
} else {
|
||||
Debug('Select timed out');
|
||||
}
|
||||
} # end while forever
|
||||
} # end while !$zm_terminate
|
||||
Info("Control server $id/$protocol exiting");
|
||||
unlink($sock_file);
|
||||
$control->close();
|
||||
|
|
|
@ -28,13 +28,20 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|||
|
||||
logInit();
|
||||
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');
|
||||
sleep(START_DELAY);
|
||||
|
||||
my $dbh = zmDbConnect();
|
||||
|
||||
while( 1 ) {
|
||||
while (!$zm_terminate) {
|
||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||
Info('Reconnecting to db');
|
||||
if ( !($dbh = zmDbConnect()) ) {
|
||||
|
@ -95,7 +102,7 @@ while( 1 ) {
|
|||
zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7));
|
||||
|
||||
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
||||
} # end while (1)
|
||||
} # end while (!$zm_terminate)
|
||||
|
||||
Info('Stats Daemon exiting');
|
||||
exit();
|
||||
|
|
|
@ -87,6 +87,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|||
|
||||
logInit();
|
||||
logSetSignal();
|
||||
my $zm_terminate = 0;
|
||||
sub TermHandler {
|
||||
Info('Received TERM, exiting');
|
||||
$zm_terminate = 1;
|
||||
}
|
||||
$SIG{TERM} = \&TermHandler;
|
||||
$SIG{INT} = \&TermHandler;
|
||||
|
||||
Info('Trigger daemon starting');
|
||||
|
||||
|
@ -118,7 +125,7 @@ my $win = $rin;
|
|||
my $ein = $win;
|
||||
my $timeout = SELECT_TIMEOUT;
|
||||
my %actions;
|
||||
while (1) {
|
||||
while (!$zm_terminate) {
|
||||
$rin = $base_rin;
|
||||
# Add the file descriptors of any spawned connections
|
||||
foreach my $fileno ( keys %spawned_connections ) {
|
||||
|
@ -312,7 +319,7 @@ while (1) {
|
|||
|
||||
# zmDbConnect will ping and reconnect if neccessary
|
||||
$dbh = zmDbConnect();
|
||||
} # end while ( 1 )
|
||||
} # end while (!$zm_terminate)
|
||||
Info('Trigger daemon exiting');
|
||||
exit;
|
||||
|
||||
|
|
|
@ -68,6 +68,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|||
|
||||
logInit();
|
||||
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');
|
||||
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)
|
||||
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
||||
|
||||
while( 1 ) {
|
||||
while (!$zm_terminate) {
|
||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||
if ( ! ( $dbh = zmDbConnect() ) ) {
|
||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||
|
@ -192,7 +199,7 @@ while( 1 ) {
|
|||
} # end foreach monitor
|
||||
|
||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||
} # end while (1)
|
||||
} # end while (!$zm_terminate)
|
||||
|
||||
Info("Watchdog exiting");
|
||||
exit();
|
||||
|
|
|
@ -16,6 +16,7 @@ set(ZM_BIN_SRC_FILES
|
|||
zm_crypt.cpp
|
||||
zm.cpp
|
||||
zm_db.cpp
|
||||
zm_decoder_thread.cpp
|
||||
zm_logger.cpp
|
||||
zm_event.cpp
|
||||
zm_eventstream.cpp
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#include "zm_analysis_thread.h"
|
||||
|
||||
#include "zm_monitor.h"
|
||||
#include "zm_signal.h"
|
||||
#include "zm_utils.h"
|
||||
|
||||
AnalysisThread::AnalysisThread(std::shared_ptr<Monitor> monitor) :
|
||||
monitor_(std::move(monitor)), terminate_(false) {
|
||||
//AnalysisThread::AnalysisThread(std::shared_ptr<Monitor> monitor) :
|
||||
AnalysisThread::AnalysisThread(Monitor* monitor) :
|
||||
monitor_(monitor), terminate_(false) {
|
||||
//monitor_(std::move(monitor)), terminate_(false) {
|
||||
thread_ = std::thread(&AnalysisThread::Run, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#ifndef ZM_ANALYSIS_THREAD_H
|
||||
#define ZM_ANALYSIS_THREAD_H
|
||||
|
||||
#include "zm_monitor.h"
|
||||
class Monitor;
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
class AnalysisThread {
|
||||
public:
|
||||
explicit AnalysisThread(std::shared_ptr<Monitor> monitor);
|
||||
explicit AnalysisThread(Monitor* monitor);
|
||||
//explicit AnalysisThread(std::shared_ptr<Monitor> monitor);
|
||||
~AnalysisThread();
|
||||
AnalysisThread(AnalysisThread &rhs) = delete;
|
||||
AnalysisThread(AnalysisThread &&rhs) = delete;
|
||||
|
@ -18,7 +19,8 @@ class AnalysisThread {
|
|||
private:
|
||||
void Run();
|
||||
|
||||
std::shared_ptr<Monitor> monitor_;
|
||||
Monitor* monitor_;
|
||||
//std::shared_ptr<Monitor> monitor_;
|
||||
std::atomic<bool> terminate_;
|
||||
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;
|
||||
createNotes(notes);
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, 0);
|
||||
timeval now = {};
|
||||
gettimeofday(&now, nullptr);
|
||||
|
||||
if ( !start_time.tv_sec ) {
|
||||
Warning("Event has zero time, setting to now");
|
||||
|
@ -80,12 +80,12 @@ Event::Event(
|
|||
} else if ( start_time.tv_sec > now.tv_sec ) {
|
||||
char buffer[26];
|
||||
char buffer_now[26];
|
||||
struct tm* tm_info;
|
||||
tm tm_info = {};
|
||||
|
||||
tm_info = localtime(&start_time.tv_sec);
|
||||
strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", tm_info);
|
||||
tm_info = localtime(&now.tv_sec);
|
||||
strftime(buffer_now, 26, "%Y:%m:%d %H:%M:%S", tm_info);
|
||||
localtime_r(&start_time.tv_sec, &tm_info);
|
||||
strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", &tm_info);
|
||||
localtime_r(&now.tv_sec, &tm_info);
|
||||
strftime(buffer_now, 26, "%Y:%m:%d %H:%M:%S", &tm_info);
|
||||
|
||||
Error(
|
||||
"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;
|
||||
}
|
||||
|
||||
struct tm *stime = localtime(&start_time.tv_sec);
|
||||
tm stime = {};
|
||||
localtime_r(&start_time.tv_sec, &stime);
|
||||
if ( scheme == Storage::DEEP ) {
|
||||
|
||||
int dt_parts[6];
|
||||
dt_parts[0] = stime->tm_year-100;
|
||||
dt_parts[1] = stime->tm_mon+1;
|
||||
dt_parts[2] = stime->tm_mday;
|
||||
dt_parts[3] = stime->tm_hour;
|
||||
dt_parts[4] = stime->tm_min;
|
||||
dt_parts[5] = stime->tm_sec;
|
||||
dt_parts[0] = stime.tm_year-100;
|
||||
dt_parts[1] = stime.tm_mon+1;
|
||||
dt_parts[2] = stime.tm_mday;
|
||||
dt_parts[3] = stime.tm_hour;
|
||||
dt_parts[4] = stime.tm_min;
|
||||
dt_parts[5] = stime.tm_sec;
|
||||
|
||||
std::string date_path;
|
||||
std::string time_path;
|
||||
|
@ -685,7 +686,7 @@ bool Event::SetPath(Storage *storage) {
|
|||
if ( i == 2 )
|
||||
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
|
||||
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 ) {
|
||||
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 ) ) {
|
||||
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
|
||||
|
|
|
@ -138,18 +138,20 @@ class Event {
|
|||
bool SetPath(Storage *storage);
|
||||
|
||||
public:
|
||||
static const char *getSubPath(struct tm *time) {
|
||||
static const char *getSubPath(tm time) {
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
time->tm_year-100, time->tm_mon+1, time->tm_mday,
|
||||
time->tm_hour, time->tm_min, time->tm_sec);
|
||||
time.tm_year-100, time.tm_mon+1, time.tm_mday,
|
||||
time.tm_hour, time.tm_min, time.tm_sec);
|
||||
return subpath;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -171,33 +171,35 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
const char *storage_path = storage->Path();
|
||||
|
||||
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] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
storage_path, event_data->monitor_id,
|
||||
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_year-100, event_time.tm_mon+1, event_time.tm_mday,
|
||||
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||
else
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
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_hour, event_time->tm_min, event_time->tm_sec);
|
||||
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);
|
||||
} 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] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
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);
|
||||
else
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
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);
|
||||
|
||||
} else {
|
||||
|
|
|
@ -200,10 +200,10 @@ int FfmpegCamera::Capture(ZMPacket &zm_packet) {
|
|||
) ) {
|
||||
// if audio stream is behind video stream, then read from audio, otherwise video
|
||||
mFormatContextPtr = mSecondFormatContext;
|
||||
Debug(1, "Using audio input");
|
||||
Debug(2, "Using audio input");
|
||||
} else {
|
||||
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),
|
||||
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);
|
||||
if (pipe_size == -1) {
|
||||
perror("get pipe size failed.");
|
||||
Error("get pipe size failed.");
|
||||
}
|
||||
Debug(1, "default pipe size: %ld\n", pipe_size);
|
||||
#endif
|
||||
|
@ -104,9 +104,9 @@ bool Fifo::close() {
|
|||
bool Fifo::writePacket(ZMPacket &packet) {
|
||||
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
|
||||
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) {
|
||||
Error("Problem during writing: %s", strerror(errno));
|
||||
} else {
|
||||
|
@ -121,6 +121,7 @@ bool Fifo::writePacket(ZMPacket &packet) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Fifo::writePacket(std::string filename, ZMPacket &packet) {
|
||||
bool on_blocking_abort = true;
|
||||
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];
|
||||
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
|
||||
unsigned char exiftimes[82] = {
|
||||
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 ) {
|
||||
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 ) {
|
||||
// Assume label is max 64, + ' - ' + 64 chars of time_text
|
||||
char text[132];
|
||||
|
|
|
@ -798,10 +798,10 @@ void LocalCamera::Initialise() {
|
|||
);
|
||||
|
||||
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 ) {
|
||||
Warning("Failed to set requested height");
|
||||
Warning("Failed to set requested height");
|
||||
}
|
||||
|
||||
/* Buggy driver paranoia. */
|
||||
|
@ -2087,8 +2087,11 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
|
|||
buffer_bytesused = v4l2_data.bufptr->bytesused;
|
||||
bytes += buffer_bytesused;
|
||||
|
||||
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",
|
||||
if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) > (width * height) ) {
|
||||
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);
|
||||
}
|
||||
} // end if v4l2
|
||||
|
|
|
@ -447,7 +447,8 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
|||
} else {
|
||||
#endif
|
||||
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);
|
||||
#if 0
|
||||
}
|
||||
|
@ -524,8 +525,8 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
|
|||
if (mLogFileFP) {
|
||||
fputs(logString, mLogFileFP);
|
||||
if (mFlush) fflush(mLogFileFP);
|
||||
} else {
|
||||
puts("Logging to file, but failed to open it\n");
|
||||
} else if (mTerminalLevel != NOLOG) {
|
||||
puts("Logging to file but failed to open it\n");
|
||||
}
|
||||
} // end if level <= mFileLevel
|
||||
|
||||
|
|
|
@ -395,6 +395,9 @@ Monitor::Monitor()
|
|||
storage(nullptr),
|
||||
videoStore(nullptr),
|
||||
analysis_it(nullptr),
|
||||
analysis_thread(nullptr),
|
||||
decoder_it(nullptr),
|
||||
decoder(nullptr),
|
||||
n_zones(0),
|
||||
zones(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++;
|
||||
|
||||
storage_id = atoi(dbrow[col]); col++;
|
||||
if ( storage )
|
||||
delete storage;
|
||||
if (storage) delete storage;
|
||||
storage = new Storage(storage_id);
|
||||
|
||||
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++;
|
||||
pre_event_count = atoi(dbrow[col]); col++;
|
||||
packetqueue.setMaxVideoPackets(pre_event_count);
|
||||
packetqueue.setKeepKeyframes(videowriter == PASSTHROUGH);
|
||||
post_event_count = atoi(dbrow[col]); col++;
|
||||
stream_replay_buffer = 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),
|
||||
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
|
||||
std::string monitor_dir = stringtf("%s/%d", storage->Path(), id);
|
||||
LoadCamera();
|
||||
|
||||
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 ) ) {
|
||||
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
|
||||
}
|
||||
|
@ -1125,12 +1128,11 @@ Monitor::~Monitor() {
|
|||
disconnect();
|
||||
} // end if mem_ptr
|
||||
|
||||
if (analysis_it) {
|
||||
packetqueue.free_it(analysis_it);
|
||||
analysis_it = nullptr;
|
||||
}
|
||||
// Will be free by packetqueue destructor
|
||||
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;
|
||||
|
@ -1733,16 +1735,10 @@ void Monitor::UpdateCaptureFPS() {
|
|||
last_fps_time = now_double;
|
||||
last_capture_image_count = image_count;
|
||||
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
// The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost,
|
||||
// and nothing else will update the status until zmc restarts. Since we are successfully capturing we can
|
||||
// assume that we are connected
|
||||
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);
|
||||
std::string sql = stringtf(
|
||||
"UPDATE Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u WHERE MonitorId=%u",
|
||||
new_capture_fps, new_capture_bandwidth, id);
|
||||
dbQueue.push(sql.c_str());
|
||||
} // now != last_fps_time
|
||||
} // end if report fps
|
||||
} // void Monitor::UpdateCaptureFPS()
|
||||
|
@ -1802,22 +1798,17 @@ void Monitor::UpdateAnalysisFPS() {
|
|||
// If there isn't then we keep pre-event + alarm frames. = pre_event_count
|
||||
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.
|
||||
|
||||
// get_analysis_packet will lock the packet and may wait if analysis_it is at the end
|
||||
ZMPacket *snap = packetqueue.get_packet(analysis_it);
|
||||
if ( !snap ) return false;
|
||||
ZMLockedPacket *packet_lock = packetqueue.get_packet(analysis_it);
|
||||
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
|
||||
if ( snap->score != -1 ) {
|
||||
snap->unlock();
|
||||
if (snap->score != -1) {
|
||||
delete packet_lock;
|
||||
packetqueue.increment_it(analysis_it);
|
||||
Error("skipping because score was %d", snap->score);
|
||||
return false;
|
||||
|
@ -1837,25 +1828,24 @@ bool Monitor::Analyse() {
|
|||
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 ( 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));
|
||||
int score = 0;
|
||||
// Ready means that we have captured the warmup # of frames
|
||||
if ( !Ready() ) {
|
||||
if (!Ready()) {
|
||||
Debug(3, "Not ready?");
|
||||
snap->unlock();
|
||||
delete packet_lock;
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug(4, "Ready");
|
||||
std::string cause;
|
||||
Event::StringSetMap noteSetMap;
|
||||
|
||||
// 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;
|
||||
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
|
||||
if ( !event ) {
|
||||
if (!event) {
|
||||
cause += trigger_data->trigger_cause;
|
||||
}
|
||||
Event::StringSet noteSet;
|
||||
|
@ -1863,12 +1853,13 @@ bool Monitor::Analyse() {
|
|||
noteSetMap[trigger_data->trigger_cause] = noteSet;
|
||||
} // 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);
|
||||
const char *signalText = "Unknown";
|
||||
if ( !signal ) {
|
||||
if (!signal) {
|
||||
signalText = "Lost";
|
||||
if ( event ) {
|
||||
if (event) {
|
||||
Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, analysis_image_count, event->Id());
|
||||
closeEvent();
|
||||
last_section_mod = 0;
|
||||
|
@ -1877,9 +1868,8 @@ bool Monitor::Analyse() {
|
|||
signalText = "Reacquired";
|
||||
score += 100;
|
||||
}
|
||||
if ( !event ) {
|
||||
if ( cause.length() )
|
||||
cause += ", ";
|
||||
if (!event) {
|
||||
if (cause.length()) cause += ", ";
|
||||
cause += SIGNAL_CAUSE;
|
||||
}
|
||||
Event::StringSet noteSet;
|
||||
|
@ -1887,64 +1877,25 @@ bool Monitor::Analyse() {
|
|||
noteSetMap[SIGNAL_CAUSE] = noteSet;
|
||||
shared_data->state = state = IDLE;
|
||||
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));
|
||||
}// else
|
||||
|
||||
if (signal) {
|
||||
if (snap->image or (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
|
||||
|
||||
if (snap->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// Check to see if linked monitors are triggering.
|
||||
if (n_linked_monitors > 0) {
|
||||
Debug(4, "Checking linked monitors");
|
||||
Debug(1, "Checking linked monitors");
|
||||
// FIXME improve logic here
|
||||
bool first_link = true;
|
||||
Event::StringSet noteSet;
|
||||
for ( int i = 0; i < n_linked_monitors; i++ ) {
|
||||
// TODO: Shouldn't we try to connect?
|
||||
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());
|
||||
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());
|
||||
if ( !event ) {
|
||||
if ( first_link ) {
|
||||
|
@ -1957,7 +1908,7 @@ bool Monitor::Analyse() {
|
|||
noteSet.insert(linked_monitors[i]->Name());
|
||||
score += linked_monitors[i]->lastFrameScore(); // 50;
|
||||
} 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());
|
||||
}
|
||||
} else {
|
||||
|
@ -1969,16 +1920,72 @@ bool Monitor::Analyse() {
|
|||
noteSetMap[LINKED_CAUSE] = noteSet;
|
||||
} // 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 doing record, check to see if we need to close the event or not.
|
||||
|
||||
if (event) {
|
||||
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",
|
||||
name, image_count, event->Id(),
|
||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||
|
@ -1999,22 +2006,33 @@ bool Monitor::Analyse() {
|
|||
);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
// Have added the packet, don't want to unlock it until we have locked the next
|
||||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ( (*start_it) == snap_it ) {
|
||||
starting_packet->unlock();
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
ZMPacket *p = packetqueue.get_packet(start_it);
|
||||
starting_packet->unlock();
|
||||
starting_packet = p;
|
||||
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||
delete starting_packet_lock;
|
||||
if (!lp) return false;
|
||||
starting_packet_lock = lp;
|
||||
starting_packet = lp->packet_;
|
||||
}
|
||||
packetqueue.free_it(start_it);
|
||||
delete start_it;
|
||||
|
@ -2030,9 +2048,7 @@ bool Monitor::Analyse() {
|
|||
for ( int i=0; i < n_zones; i++ ) {
|
||||
if ( zones[i]->Alarmed() ) {
|
||||
alarm_cause += std::string(zones[i]->Label());
|
||||
if ( i < n_zones-1 ) {
|
||||
alarm_cause += ",";
|
||||
}
|
||||
if (i < n_zones-1) alarm_cause += ",";
|
||||
}
|
||||
}
|
||||
alarm_cause = cause+" "+alarm_cause;
|
||||
|
@ -2085,7 +2101,16 @@ bool Monitor::Analyse() {
|
|||
snap_it,
|
||||
(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);
|
||||
shared_data->last_event_id = event->Id();
|
||||
|
@ -2099,12 +2124,18 @@ bool Monitor::Analyse() {
|
|||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ( (*start_it) == snap_it ) {
|
||||
starting_packet->unlock();
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
ZMPacket *p = packetqueue.get_packet(start_it);
|
||||
starting_packet->unlock();
|
||||
starting_packet = p;
|
||||
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||
delete starting_packet_lock;
|
||||
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);
|
||||
delete start_it;
|
||||
|
@ -2138,7 +2169,7 @@ bool Monitor::Analyse() {
|
|||
Debug(1, "Staying in %s", State_Strings[state].c_str());
|
||||
|
||||
}
|
||||
if ( state == ALARM ) {
|
||||
if (state == ALARM) {
|
||||
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
|
||||
} else { // no score?
|
||||
|
@ -2146,7 +2177,7 @@ bool Monitor::Analyse() {
|
|||
if (state == ALARM) {
|
||||
Info("%s: %03d - Gone into alert state", name, analysis_image_count);
|
||||
shared_data->state = state = ALERT;
|
||||
} else if ( state == ALERT ) {
|
||||
} else if (state == ALERT) {
|
||||
if (
|
||||
( analysis_image_count-last_alarm_count > post_event_count )
|
||||
&&
|
||||
|
@ -2164,7 +2195,7 @@ bool Monitor::Analyse() {
|
|||
shared_data->state = state = TAPE;
|
||||
}
|
||||
}
|
||||
} else if ( state == PREALARM ) {
|
||||
} else if (state == PREALARM) {
|
||||
// Back to IDLE
|
||||
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
|
||||
} else {
|
||||
|
@ -2172,19 +2203,19 @@ bool Monitor::Analyse() {
|
|||
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);
|
||||
}
|
||||
if ( Event::PreAlarmCount() )
|
||||
if (Event::PreAlarmCount())
|
||||
Event::EmptyPreAlarmFrames();
|
||||
} // end if score or not
|
||||
|
||||
snap->score = score;
|
||||
|
||||
if ( state == PREALARM ) {
|
||||
if (state == PREALARM) {
|
||||
// Generate analysis images if necessary
|
||||
if ( (savejpegs > 1) and snap->image ) {
|
||||
for ( int i = 0; i < n_zones; i++ ) {
|
||||
if ( zones[i]->Alarmed() ) {
|
||||
if ( zones[i]->AlarmImage() ) {
|
||||
if ( ! snap->analysis_image )
|
||||
if ((savejpegs > 1) and snap->image) {
|
||||
for (int i = 0; i < n_zones; i++) {
|
||||
if (zones[i]->Alarmed()) {
|
||||
if (zones[i]->AlarmImage()) {
|
||||
if (!snap->analysis_image)
|
||||
snap->analysis_image = new Image(*(snap->image));
|
||||
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
|
||||
}
|
||||
|
@ -2195,38 +2226,42 @@ bool Monitor::Analyse() {
|
|||
// incremement pre alarm image count
|
||||
//have_pre_alarmed_frames ++;
|
||||
Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr);
|
||||
} else if ( state == ALARM ) {
|
||||
if ( ( savejpegs > 1 ) and snap->image ) {
|
||||
for ( int i = 0; i < n_zones; i++ ) {
|
||||
if ( zones[i]->Alarmed() ) {
|
||||
if ( zones[i]->AlarmImage() ) {
|
||||
if ( ! snap->analysis_image )
|
||||
} else if (state == ALARM) {
|
||||
if ((savejpegs > 1 ) and snap->image) {
|
||||
for (int i = 0; i < n_zones; i++) {
|
||||
if (zones[i]->Alarmed()) {
|
||||
if (zones[i]->AlarmImage()) {
|
||||
if (!snap->analysis_image)
|
||||
snap->analysis_image = new Image(*(snap->image));
|
||||
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
|
||||
}
|
||||
if ( config.record_event_stats )
|
||||
if (config.record_event_stats)
|
||||
zones[i]->RecordStats(event);
|
||||
} // end if zone is alarmed
|
||||
} // end foreach zone
|
||||
}
|
||||
if ( noteSetMap.size() > 0 )
|
||||
event->updateNotes(noteSetMap);
|
||||
if ( section_length
|
||||
&& ( ( 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(),
|
||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||
section_length
|
||||
);
|
||||
closeEvent();
|
||||
event = new Event(this, *timestamp, cause, noteSetMap);
|
||||
shared_data->last_event_id = event->Id();
|
||||
//set up video store data
|
||||
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||
video_store_data->recording = event->StartTime();
|
||||
}
|
||||
if (event) {
|
||||
if (noteSetMap.size() > 0)
|
||||
event->updateNotes(noteSetMap);
|
||||
if ( section_length
|
||||
&& ( ( 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(),
|
||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||
section_length
|
||||
);
|
||||
closeEvent();
|
||||
event = new Event(this, *timestamp, cause, noteSetMap);
|
||||
shared_data->last_event_id = event->Id();
|
||||
//set up video store data
|
||||
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 ) {
|
||||
|
@ -2255,27 +2290,18 @@ bool Monitor::Analyse() {
|
|||
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
||||
|
||||
if (event) event->AddPacket(snap);
|
||||
#if 0
|
||||
if (snap->packet.stream_index == video_stream_id) {
|
||||
if (video_fifo) {
|
||||
if ( snap->keyframe ) {
|
||||
// avcodec strips out important nals that describe the stream and
|
||||
// stick them in extradata. Need to send them along with keyframes
|
||||
AVStream *stream = camera->getVideoStream();
|
||||
video_fifo->write(
|
||||
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);
|
||||
|
||||
// In the case where people have pre-alarm frames, the web ui will generate the frame images
|
||||
// from the mp4. So no one will notice anyways.
|
||||
if (snap->image and (videowriter == PASSTHROUGH) and !savejpegs) {
|
||||
Debug(1, "Deleting image data for %d", snap->image_index);
|
||||
// Don't need raw images anymore
|
||||
delete snap->image;
|
||||
snap->image = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// popPacket will have placed a second lock on snap, so release it here.
|
||||
snap->unlock();
|
||||
delete packet_lock;
|
||||
|
||||
if ( snap->image_index > 0 ) {
|
||||
// Only do these if it's a video packet.
|
||||
|
@ -2507,7 +2533,6 @@ int Monitor::Capture() {
|
|||
gettimeofday(packet->timestamp, nullptr);
|
||||
shared_data->zmc_heartbeat_time = packet->timestamp->tv_sec;
|
||||
|
||||
Image* capture_image = image_buffer[index].image;
|
||||
int captureResult = 0;
|
||||
|
||||
if ( deinterlacing_value == 4 ) {
|
||||
|
@ -2526,7 +2551,7 @@ int Monitor::Capture() {
|
|||
}
|
||||
} else {
|
||||
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 ) {
|
||||
Debug(2, "failed capture");
|
||||
|
@ -2536,7 +2561,7 @@ int Monitor::Capture() {
|
|||
Rgb signalcolor;
|
||||
/* HTML colour code is actually BGR in memory, we want RGB */
|
||||
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);
|
||||
shared_data->signal = false;
|
||||
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
|
||||
return -1;
|
||||
} 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) ",
|
||||
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;
|
||||
} // end if audio
|
||||
|
||||
#if 0
|
||||
if ( !packet->image ) {
|
||||
if ( packet->packet.size and !packet->in_frame ) {
|
||||
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->last_write_index = index;
|
||||
shared_data->last_write_time = packet->timestamp->tv_sec;
|
||||
#endif
|
||||
image_count++;
|
||||
|
||||
// Will only be queued if there are iterators allocated in the queue.
|
||||
|
@ -2710,13 +2743,108 @@ int Monitor::Capture() {
|
|||
return captureResult;
|
||||
} // 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 {
|
||||
if ( !label_format[0] )
|
||||
return;
|
||||
|
||||
// Expand the strftime macros first
|
||||
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];
|
||||
const char *s_ptr = label_time_text;
|
||||
char *d_ptr = label_text;
|
||||
|
@ -3026,6 +3154,18 @@ int Monitor::PrimeCapture() {
|
|||
audio_fifo = new Fifo(shared_data->audio_fifo_path, true);
|
||||
}
|
||||
} // 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 {
|
||||
Debug(2, "Failed to prime %d", ret);
|
||||
}
|
||||
|
@ -3035,13 +3175,24 @@ int Monitor::PrimeCapture() {
|
|||
int Monitor::PreCapture() const { return camera->PreCapture(); }
|
||||
int Monitor::PostCapture() const { return camera->PostCapture(); }
|
||||
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);
|
||||
if (event) {
|
||||
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id());
|
||||
closeEvent();
|
||||
}
|
||||
if (camera) camera->Close();
|
||||
packetqueue.clear();
|
||||
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.
|
||||
// This function is deprecated.
|
||||
void Monitor::get_ref_image() {
|
||||
ZMPacket *snap = nullptr;
|
||||
ZMLockedPacket *snap_lock = nullptr;
|
||||
|
||||
if ( !analysis_it )
|
||||
analysis_it = packetqueue.get_video_it(true);
|
||||
|
||||
while (
|
||||
(
|
||||
!( snap = packetqueue.get_packet(analysis_it))
|
||||
!( snap_lock = packetqueue.get_packet(analysis_it))
|
||||
or
|
||||
( snap->codec_type != AVMEDIA_TYPE_VIDEO )
|
||||
( snap_lock->packet_->codec_type != AVMEDIA_TYPE_VIDEO )
|
||||
or
|
||||
! snap->image
|
||||
! snap_lock->packet_->image
|
||||
)
|
||||
and !zm_terminate) {
|
||||
|
||||
Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)",
|
||||
shared_data->last_write_index, shared_data->last_write_time);
|
||||
if ( snap and ! snap->image ) {
|
||||
snap->unlock();
|
||||
if ( snap_lock and ! snap_lock->packet_->image ) {
|
||||
delete snap_lock;
|
||||
// can't analyse it anyways, incremement
|
||||
packetqueue.increment_it(analysis_it);
|
||||
}
|
||||
|
@ -3078,6 +3229,7 @@ void Monitor::get_ref_image() {
|
|||
if ( zm_terminate )
|
||||
return;
|
||||
|
||||
ZMPacket *snap = snap_lock->packet_;
|
||||
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 );
|
||||
// Might not have been decoded yet FIXME
|
||||
|
@ -3088,7 +3240,7 @@ void Monitor::get_ref_image() {
|
|||
} else {
|
||||
Debug(2, "Have no ref image about to unlock");
|
||||
}
|
||||
snap->unlock();
|
||||
delete snap_lock;
|
||||
}
|
||||
|
||||
std::vector<Group *> Monitor::Groups() {
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include "zm_define.h"
|
||||
#include "zm_camera.h"
|
||||
#include "zm_analysis_thread.h"
|
||||
#include "zm_decoder_thread.h"
|
||||
#include "zm_event.h"
|
||||
#include "zm_fifo.h"
|
||||
#include "zm_image.h"
|
||||
|
@ -375,7 +377,9 @@ protected:
|
|||
VideoStore *videoStore;
|
||||
PacketQueue packetqueue;
|
||||
packetqueue_iterator *analysis_it;
|
||||
|
||||
AnalysisThread *analysis_thread;
|
||||
packetqueue_iterator *decoder_it;
|
||||
DecoderThread *decoder;
|
||||
|
||||
int n_zones;
|
||||
Zone **zones;
|
||||
|
@ -411,6 +415,8 @@ public:
|
|||
if ( shared_data && shared_data->valid ) {
|
||||
struct timeval now;
|
||||
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)
|
||||
return true;
|
||||
}
|
||||
|
@ -426,6 +432,7 @@ public:
|
|||
}
|
||||
return storage;
|
||||
}
|
||||
inline CameraType GetType() const { return type; }
|
||||
inline Function GetFunction() const { return function; }
|
||||
inline PacketQueue * GetPacketQueue() { return &packetqueue; }
|
||||
inline bool Enabled() const {
|
||||
|
@ -438,10 +445,6 @@ public:
|
|||
}
|
||||
inline const char *EventPrefix() const { return event_prefix; }
|
||||
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 ) {
|
||||
return true;
|
||||
}
|
||||
|
@ -549,6 +552,7 @@ public:
|
|||
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
bool CheckSignal( const Image *image );
|
||||
bool Analyse();
|
||||
bool Decode();
|
||||
void DumpImage( Image *dump_image ) const;
|
||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||
bool closeEvent();
|
||||
|
|
|
@ -467,28 +467,35 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
|||
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
|
||||
|
||||
void MonitorStream::runStream() {
|
||||
if ( type == STREAM_SINGLE ) {
|
||||
if (type == STREAM_SINGLE) {
|
||||
// Not yet migrated over to stream class
|
||||
SingleImage(scale);
|
||||
if (checkInitialised())
|
||||
SingleImage(scale);
|
||||
else
|
||||
sendTextFrame("Unable to send image");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
openComms();
|
||||
|
||||
if ( type == STREAM_JPEG )
|
||||
if (type == STREAM_JPEG)
|
||||
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
|
||||
|
||||
if ( !checkInitialised() ) {
|
||||
Error("Not initialized");
|
||||
while ( !(loadMonitor(monitor_id) || zm_terminate) ) {
|
||||
sendTextFrame("Not connected");
|
||||
if ( connkey )
|
||||
checkCommandQueue();
|
||||
sleep(1);
|
||||
}
|
||||
if ( zm_terminate )
|
||||
return;
|
||||
while (!(loadMonitor(monitor_id) || zm_terminate)) {
|
||||
sendTextFrame("Not connected");
|
||||
if (connkey)
|
||||
checkCommandQueue();
|
||||
sleep(1);
|
||||
}
|
||||
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());
|
||||
|
||||
|
@ -562,7 +569,7 @@ void MonitorStream::runStream() {
|
|||
capture_fps = capture_max_fps;
|
||||
}
|
||||
|
||||
while ( !zm_terminate ) {
|
||||
while (!zm_terminate) {
|
||||
bool got_command = false;
|
||||
if ( feof(stdout) ) {
|
||||
Debug(2, "feof stdout");
|
||||
|
@ -570,7 +577,7 @@ void MonitorStream::runStream() {
|
|||
} else if ( ferror(stdout) ) {
|
||||
Debug(2, "ferror stdout");
|
||||
break;
|
||||
} else if ( !monitor->ShmValid() ) {
|
||||
} else if (!monitor->ShmValid()) {
|
||||
Debug(2, "monitor not valid.... maybe we should wait until it comes back.");
|
||||
break;
|
||||
}
|
||||
|
@ -856,7 +863,7 @@ void MonitorStream::SingleImage(int scale) {
|
|||
int img_buffer_size = 0;
|
||||
static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||
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");
|
||||
usleep(100000);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,8 @@ ZMPacket::ZMPacket() :
|
|||
score(-1),
|
||||
codec_type(AVMEDIA_TYPE_UNKNOWN),
|
||||
image_index(-1),
|
||||
codec_imgsize(0)
|
||||
codec_imgsize(0),
|
||||
decoded(0)
|
||||
{
|
||||
av_init_packet(&packet);
|
||||
packet.size = 0; // So we can detect whether it has been filled.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define ZM_PACKET_H
|
||||
|
||||
#include "zm_logger.h"
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
extern "C" {
|
||||
|
@ -36,7 +37,9 @@ class Image;
|
|||
class ZMPacket {
|
||||
public:
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_;
|
||||
|
||||
int keyframe;
|
||||
AVStream *stream; // Input stream
|
||||
AVPacket packet; // Input packet, undecoded
|
||||
|
@ -51,6 +54,7 @@ class ZMPacket {
|
|||
int image_index;
|
||||
int codec_imgsize;
|
||||
int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q
|
||||
bool decoded;
|
||||
|
||||
public:
|
||||
AVPacket *av_packet() { return &packet; }
|
||||
|
@ -65,21 +69,45 @@ class ZMPacket {
|
|||
explicit ZMPacket(ZMPacket &packet);
|
||||
ZMPacket();
|
||||
~ZMPacket();
|
||||
void lock() {
|
||||
Debug(4,"Locking packet %d", this->image_index);
|
||||
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 );
|
||||
|
||||
AVFrame *get_out_frame(const AVCodecContext *ctx);
|
||||
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 */
|
||||
|
|
|
@ -31,7 +31,8 @@ PacketQueue::PacketQueue():
|
|||
max_video_packet_count(-1),
|
||||
max_stream_id(-1),
|
||||
packet_counts(nullptr),
|
||||
deleting(false)
|
||||
deleting(false),
|
||||
keep_keyframes(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -56,11 +57,11 @@ int PacketQueue::addStream() {
|
|||
|
||||
PacketQueue::~PacketQueue() {
|
||||
clear();
|
||||
if ( packet_counts ) {
|
||||
if (packet_counts) {
|
||||
delete[] packet_counts;
|
||||
packet_counts = nullptr;
|
||||
}
|
||||
while ( !iterators.empty() ) {
|
||||
while (!iterators.empty()) {
|
||||
packetqueue_iterator *it = iterators.front();
|
||||
iterators.pop_front();
|
||||
delete it;
|
||||
|
@ -87,7 +88,7 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) {
|
|||
|
||||
pktQueue.push_back(add_packet);
|
||||
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,
|
||||
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.
|
||||
// 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
|
||||
and
|
||||
add_packet->keyframe
|
||||
and
|
||||
(packet_counts[video_stream_id] > max_video_packet_count)
|
||||
and
|
||||
*(pktQueue.begin()) != add_packet
|
||||
)
|
||||
and
|
||||
add_packet->keyframe
|
||||
and
|
||||
(packet_counts[video_stream_id] > max_video_packet_count)
|
||||
and
|
||||
*(pktQueue.begin()) != add_packet
|
||||
)
|
||||
) {
|
||||
Debug(3, "stream index %d ?= video_stream_id %d, keyframe %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,
|
||||
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, keep_keyframes, packet_counts[video_stream_id], max_video_packet_count,
|
||||
( *(pktQueue.begin()) != add_packet )
|
||||
);
|
||||
return;
|
||||
}
|
||||
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 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
|
||||
|
||||
|
||||
// First packet is special because we know it is a video keyframe and only need to check for lock
|
||||
ZMPacket *zm_packet = *it;
|
||||
if ( zm_packet->trylock() ) {
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||
if (lp->trylock()) {
|
||||
++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
|
||||
while ( *it != add_packet ) {
|
||||
while (*it != add_packet) {
|
||||
zm_packet = *it;
|
||||
if ( !zm_packet->trylock() ) {
|
||||
break;
|
||||
}
|
||||
zm_packet->unlock();
|
||||
lp = new ZMLockedPacket(zm_packet);
|
||||
if (!lp->trylock()) break;
|
||||
delete lp;
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
if ( zm_packet->packet.stream_index == video_stream_id ) {
|
||||
if ( zm_packet->keyframe ) {
|
||||
if (zm_packet->packet.stream_index == video_stream_id) {
|
||||
if (zm_packet->keyframe) {
|
||||
Debug(3, "Have a video keyframe so setting next front to it");
|
||||
next_front = it;
|
||||
}
|
||||
++video_packets_to_delete;
|
||||
Debug(4, "Counted %d video packets. Which would leave %d in packetqueue",
|
||||
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete);
|
||||
if (packet_counts[video_stream_id] - video_packets_to_delete <= max_video_packet_count) {
|
||||
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, tail_count);
|
||||
if (packet_counts[video_stream_id] - video_packets_to_delete <= max_video_packet_count + tail_count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +232,7 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) {
|
|||
return;
|
||||
} // end voidPacketQueue::clearPackets(ZMPacket* zm_packet)
|
||||
|
||||
ZMPacket* PacketQueue::popPacket( ) {
|
||||
ZMLockedPacket* PacketQueue::popPacket( ) {
|
||||
Debug(4, "pktQueue size %d", pktQueue.size());
|
||||
if ( pktQueue.empty() ) {
|
||||
return nullptr;
|
||||
|
@ -222,14 +254,15 @@ ZMPacket* PacketQueue::popPacket( ) {
|
|||
}
|
||||
} // end foreach iterator
|
||||
|
||||
zm_packet->lock();
|
||||
ZMLockedPacket *lp = new ZMLockedPacket (zm_packet);
|
||||
lp->lock();
|
||||
|
||||
pktQueue.pop_front();
|
||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||
|
||||
mutex.unlock();
|
||||
|
||||
return zm_packet;
|
||||
return lp;
|
||||
} // popPacket
|
||||
|
||||
|
||||
|
@ -319,15 +352,17 @@ unsigned int PacketQueue::clear(unsigned int frames_to_keep, int stream_id) {
|
|||
|
||||
void PacketQueue::clear() {
|
||||
deleting = true;
|
||||
condition.notify_all();
|
||||
|
||||
std::unique_lock<std::mutex> lck(mutex);
|
||||
|
||||
while (!pktQueue.empty()) {
|
||||
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
|
||||
packet->lock();
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(packet);
|
||||
lp->lock();
|
||||
pktQueue.pop_front();
|
||||
packet->unlock();
|
||||
delete lp;
|
||||
delete packet;
|
||||
}
|
||||
|
||||
|
@ -452,8 +487,8 @@ int PacketQueue::packet_count(int stream_id) {
|
|||
|
||||
|
||||
// Returns a packet. Packet will be locked
|
||||
ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
|
||||
if ( deleting or zm_terminate )
|
||||
ZMLockedPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
|
||||
if (deleting or zm_terminate)
|
||||
return nullptr;
|
||||
|
||||
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()));
|
||||
condition.wait(lck);
|
||||
}
|
||||
if ( deleting or zm_terminate )
|
||||
if (deleting or zm_terminate)
|
||||
return nullptr;
|
||||
|
||||
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?!");
|
||||
return nullptr;
|
||||
}
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(p);
|
||||
Debug(3, "get_packet %p image_index: %d, about to lock packet", p, p->image_index);
|
||||
while ( !(zm_terminate or deleting) and !p->trylock() ) {
|
||||
Debug(3, "waiting. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) );
|
||||
while (!(zm_terminate or deleting) and !lp->trylock()) {
|
||||
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);
|
||||
}
|
||||
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");
|
||||
return p;
|
||||
} // end ZMPacket *PacketQueue::get_packet(it)
|
||||
return lp;
|
||||
} // end ZMLockedPacket *PacketQueue::get_packet(it)
|
||||
|
||||
bool PacketQueue::increment_it(packetqueue_iterator *it) {
|
||||
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 ) {
|
||||
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);
|
||||
*it = pktQueue.begin();
|
||||
}
|
||||
|
@ -612,14 +654,14 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
|
|||
|
||||
while ( *it != pktQueue.end() ) {
|
||||
ZMPacket *zm_packet = *(*it);
|
||||
if ( !zm_packet ) {
|
||||
if (!zm_packet) {
|
||||
Error("Null zmpacket in queue!?");
|
||||
free_it(it);
|
||||
return nullptr;
|
||||
}
|
||||
Debug(1, "Packet keyframe %d for stream %d, so returning the it to it",
|
||||
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);
|
||||
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);
|
||||
return it;
|
||||
}
|
||||
} // get video_it
|
||||
|
||||
void PacketQueue::free_it(packetqueue_iterator *it) {
|
||||
for (
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <mutex>
|
||||
|
||||
class ZMPacket;
|
||||
class ZMLockedPacket;
|
||||
|
||||
typedef std::list<ZMPacket *>::iterator packetqueue_iterator;
|
||||
|
||||
|
@ -37,6 +38,7 @@ class PacketQueue {
|
|||
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 */
|
||||
bool deleting;
|
||||
bool keep_keyframes;
|
||||
std::list<packetqueue_iterator *> iterators;
|
||||
|
||||
std::mutex mutex;
|
||||
|
@ -50,9 +52,10 @@ class PacketQueue {
|
|||
|
||||
int addStream();
|
||||
void setMaxVideoPackets(int p);
|
||||
void setKeepKeyframes(bool k) { keep_keyframes = k; };
|
||||
|
||||
bool queuePacket(ZMPacket* packet);
|
||||
ZMPacket * popPacket();
|
||||
ZMLockedPacket * popPacket();
|
||||
bool popVideoPacket(ZMPacket* packet);
|
||||
bool popAudioPacket(ZMPacket* packet);
|
||||
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, int stream_id);
|
||||
ZMPacket *get_packet(packetqueue_iterator *);
|
||||
ZMLockedPacket *get_packet(packetqueue_iterator *);
|
||||
packetqueue_iterator *get_video_it(bool wait);
|
||||
packetqueue_iterator *get_stream_it(int stream_id);
|
||||
void free_it(packetqueue_iterator *);
|
||||
|
|
|
@ -202,7 +202,7 @@ int RemoteCameraHttp::SendRequest() {
|
|||
* > 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_ZERO(&rfds);
|
||||
FD_SET(sd, &rfds);
|
||||
|
@ -210,44 +210,44 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
|||
struct timeval temp_timeout = timeout;
|
||||
|
||||
int n_found = select(sd+1, &rfds, nullptr, nullptr, &temp_timeout);
|
||||
if( n_found == 0 ) {
|
||||
Debug( 1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec );
|
||||
if (n_found == 0) {
|
||||
Debug(1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec);
|
||||
int error = 0;
|
||||
socklen_t len = sizeof (error);
|
||||
int retval = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if(retval != 0 ) {
|
||||
Debug( 1, "error getting socket error code %s", strerror(retval) );
|
||||
socklen_t len = sizeof(error);
|
||||
int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (retval != 0) {
|
||||
Debug(1, "error getting socket error code %s", strerror(retval));
|
||||
}
|
||||
if (error != 0 ) {
|
||||
if (error != 0) {
|
||||
return -1;
|
||||
}
|
||||
// Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
|
||||
//Disconnect();
|
||||
return 0;
|
||||
} else if ( n_found < 0) {
|
||||
} else if (n_found < 0) {
|
||||
Error("Select error: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int total_bytes_to_read = 0;
|
||||
|
||||
if ( bytes_expected ) {
|
||||
if (bytes_expected) {
|
||||
total_bytes_to_read = bytes_expected;
|
||||
} else {
|
||||
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) {
|
||||
Error( "Can't ioctl(): %s", strerror(errno) );
|
||||
if (ioctl(sd, FIONREAD, &total_bytes_to_read) < 0) {
|
||||
Error("Can't ioctl(): %s", strerror(errno) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( total_bytes_to_read == 0 ) {
|
||||
if ( mode == SINGLE_IMAGE ) {
|
||||
if (total_bytes_to_read == 0) {
|
||||
if (mode == SINGLE_IMAGE) {
|
||||
int error = 0;
|
||||
socklen_t len = sizeof (error);
|
||||
int retval = getsockopt( sd, SOL_SOCKET, SO_ERROR, &error, &len );
|
||||
if ( retval != 0 ) {
|
||||
Debug( 1, "error getting socket error code %s", strerror(retval) );
|
||||
socklen_t len = sizeof(error);
|
||||
int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (retval != 0) {
|
||||
Debug(1, "error getting socket error code %s", strerror(retval));
|
||||
}
|
||||
if ( error != 0 ) {
|
||||
if (error != 0) {
|
||||
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.
|
||||
|
@ -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.
|
||||
if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) {
|
||||
if (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 {
|
||||
Debug(4, "Just getting %d", total_bytes_to_read );
|
||||
Debug(4, "Just getting %d", total_bytes_to_read);
|
||||
}
|
||||
} // 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;
|
||||
do {
|
||||
int bytes_read = buffer.read_into( sd, total_bytes_to_read );
|
||||
if ( bytes_read < 0 ) {
|
||||
Error( "Read error: %s", strerror(errno) );
|
||||
return( -1 );
|
||||
} else if ( bytes_read == 0 ) {
|
||||
Debug( 2, "Socket closed" );
|
||||
int bytes_read = buffer.read_into(sd, total_bytes_to_read);
|
||||
if (bytes_read < 0) {
|
||||
Error("Read error: %s", strerror(errno));
|
||||
return -1;
|
||||
} else if (bytes_read == 0) {
|
||||
Debug(2, "Socket closed");
|
||||
//Disconnect(); // Disconnect is done outside of ReadData now.
|
||||
return( -1 );
|
||||
} else if ( (unsigned int)bytes_read < total_bytes_to_read ) {
|
||||
Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read );
|
||||
return( -1 );
|
||||
return -1;
|
||||
} else if ((unsigned int)bytes_read < total_bytes_to_read) {
|
||||
Debug(1, "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read);
|
||||
} else {
|
||||
Debug(3, "Read %d bytes", bytes_read);
|
||||
}
|
||||
Debug( 3, "Read %d bytes", bytes_read );
|
||||
total_bytes_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;
|
||||
}
|
||||
} // end readData
|
||||
|
||||
int RemoteCameraHttp::GetData() {
|
||||
time_t start_time = time(nullptr);
|
||||
|
@ -497,36 +497,36 @@ int RemoteCameraHttp::GetResponse() {
|
|||
return( -1 );
|
||||
}
|
||||
|
||||
if ( content_length ) {
|
||||
while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) {
|
||||
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
|
||||
int bytes_read = GetData();
|
||||
if (content_length) {
|
||||
while (((long)buffer.size() < content_length ) and !zm_terminate) {
|
||||
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length);
|
||||
int bytes_read = ReadData(buffer, content_length - buffer.size());
|
||||
|
||||
if ( bytes_read < 0 ) {
|
||||
Error( "Unable to read content" );
|
||||
return( -1 );
|
||||
if (bytes_read < 0) {
|
||||
Error("Unable to read content");
|
||||
return -1;
|
||||
}
|
||||
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 {
|
||||
while ( !content_length ) {
|
||||
while (!content_length) {
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read content" );
|
||||
return( -1 );
|
||||
if (buffer_len < 0) {
|
||||
Error("Unable to read content");
|
||||
return -1;
|
||||
}
|
||||
bytes += buffer_len;
|
||||
static RegExpr *content_expr = 0;
|
||||
if ( mode == MULTI_IMAGE ) {
|
||||
if ( !content_expr ) {
|
||||
if (mode == MULTI_IMAGE) {
|
||||
if (!content_expr) {
|
||||
char content_pattern[256] = "";
|
||||
snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary );
|
||||
content_expr = new RegExpr( content_pattern, PCRE_DOTALL );
|
||||
snprintf(content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary);
|
||||
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 );
|
||||
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 :
|
||||
{
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
if (buffer_len < 0) {
|
||||
Error("Unable to read header");
|
||||
return -1;
|
||||
}
|
||||
|
@ -634,13 +634,13 @@ int RemoteCameraHttp::GetResponse() {
|
|||
int header_len = buffer.size();
|
||||
bool all_headers = false;
|
||||
|
||||
while ( true ) {
|
||||
while (true and !zm_terminate) {
|
||||
int crlf_len = memspn(header_ptr, "\r\n", header_len);
|
||||
if ( n_headers ) {
|
||||
if (n_headers) {
|
||||
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");
|
||||
*header_ptr = '\0';
|
||||
|
@ -650,8 +650,8 @@ int RemoteCameraHttp::GetResponse() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if ( crlf_len ) {
|
||||
if ( header_len == crlf_len ) {
|
||||
if (crlf_len) {
|
||||
if (header_len == crlf_len) {
|
||||
break;
|
||||
} else {
|
||||
*header_ptr = '\0';
|
||||
|
@ -661,22 +661,22 @@ int RemoteCameraHttp::GetResponse() {
|
|||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
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) ) {
|
||||
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) ) {
|
||||
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) ) {
|
||||
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) ) {
|
||||
content_type_header = header_ptr+content_type_match_len;
|
||||
Debug(6, "Got content type header '%s'", header_ptr);
|
||||
|
@ -691,10 +691,10 @@ int RemoteCameraHttp::GetResponse() {
|
|||
}
|
||||
} // end while search for headers
|
||||
|
||||
if ( all_headers ) {
|
||||
if (all_headers) {
|
||||
char *start_ptr, *end_ptr;
|
||||
|
||||
if ( !http_header ) {
|
||||
if (!http_header) {
|
||||
Error("Unable to extract HTTP status from header");
|
||||
return -1;
|
||||
}
|
||||
|
@ -703,7 +703,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
end_ptr = start_ptr+strspn(start_ptr, "10.");
|
||||
|
||||
// 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);
|
||||
|
||||
start_ptr = end_ptr;
|
||||
|
@ -718,12 +718,12 @@ int RemoteCameraHttp::GetResponse() {
|
|||
start_ptr += strspn(start_ptr, " ");
|
||||
strcpy(status_mesg, start_ptr);
|
||||
|
||||
if ( status == 401 ) {
|
||||
if ( mNeedAuth ) {
|
||||
if (status == 401) {
|
||||
if (mNeedAuth) {
|
||||
Error("Failed authentication");
|
||||
return -1;
|
||||
}
|
||||
if ( !authenticate_header ) {
|
||||
if (!authenticate_header) {
|
||||
Error("Failed authentication, but don't have an authentication header.");
|
||||
return -1;
|
||||
}
|
||||
|
@ -732,7 +732,7 @@ int RemoteCameraHttp::GetResponse() {
|
|||
Debug(2, "Checking for digest auth in %s", authenticate_header);
|
||||
|
||||
mAuthenticator->checkAuthResponse(Header);
|
||||
if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) {
|
||||
if (mAuthenticator->auth_method() == zm::AUTH_DIGEST) {
|
||||
Debug(2, "Need Digest Authentication");
|
||||
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);
|
||||
|
@ -747,26 +747,26 @@ int RemoteCameraHttp::GetResponse() {
|
|||
} else {
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
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));
|
||||
start_ptr = connection_header + strspn(connection_header, " ");
|
||||
// FIXME Should we not use strncpy?
|
||||
strcpy(connection_type, start_ptr);
|
||||
Debug(3, "Got connection '%s'", connection_type);
|
||||
}
|
||||
if ( content_length_header ) {
|
||||
if (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);
|
||||
}
|
||||
if ( content_type_header ) {
|
||||
memset(content_type, 0, sizeof(content_type));
|
||||
if (content_type_header) {
|
||||
//memset(content_type, 0, sizeof(content_type));
|
||||
start_ptr = content_type_header + strspn(content_type_header, " ");
|
||||
if ( (end_ptr = strchr(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, "; ");
|
||||
|
||||
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 += strspn(start_ptr, "-");
|
||||
content_boundary_len = sprintf(content_boundary, "--%s", start_ptr);
|
||||
|
@ -788,24 +788,24 @@ int RemoteCameraHttp::GetResponse() {
|
|||
}
|
||||
} // 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
|
||||
mode = SINGLE_IMAGE;
|
||||
format = JPEG;
|
||||
state = CONTENT;
|
||||
} else if ( !strcasecmp(content_type, "image/x-rgb") ) {
|
||||
} else if (!strcasecmp(content_type, "image/x-rgb")) {
|
||||
// Single image
|
||||
mode = SINGLE_IMAGE;
|
||||
format = X_RGB;
|
||||
state = CONTENT;
|
||||
} else if ( !strcasecmp(content_type, "image/x-rgbz") ) {
|
||||
} else if (!strcasecmp(content_type, "image/x-rgbz")) {
|
||||
// Single image
|
||||
mode = SINGLE_IMAGE;
|
||||
format = X_RGBZ;
|
||||
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
|
||||
if ( !content_boundary[0] ) {
|
||||
if (!content_boundary[0]) {
|
||||
Error("No content boundary found in header '%s'", content_type_header);
|
||||
return -1;
|
||||
}
|
||||
|
@ -817,8 +817,8 @@ int RemoteCameraHttp::GetResponse() {
|
|||
//// MPEG stream, coming soon!
|
||||
//}
|
||||
else {
|
||||
Error( "Unrecognised content type '%s'", content_type );
|
||||
return( -1 );
|
||||
Error("Unrecognised content type '%s'", content_type);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Unable to extract entire header from stream, continuing");
|
||||
|
@ -844,24 +844,24 @@ int RemoteCameraHttp::GetResponse() {
|
|||
int subheader_len = buffer.size();
|
||||
bool all_headers = false;
|
||||
|
||||
while( true ) {
|
||||
int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len );
|
||||
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 )) ) {
|
||||
while (true and !zm_terminate) {
|
||||
int crlf_len = memspn(subheader_ptr, "\r\n", subheader_len);
|
||||
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 )) ) {
|
||||
*subheader_ptr = '\0';
|
||||
subheader_ptr += crlf_len;
|
||||
subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
|
||||
subheader_len -= buffer.consume(subheader_ptr-(char *)buffer);
|
||||
all_headers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( crlf_len ) {
|
||||
if ( subheader_len == crlf_len ) {
|
||||
if (crlf_len) {
|
||||
if (subheader_len == crlf_len) {
|
||||
break;
|
||||
} else {
|
||||
*subheader_ptr = '\0';
|
||||
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*/;
|
||||
|
||||
Debug( 3, "Got boundary '%s'", boundary_header );
|
||||
Debug(3, "Got boundary '%s'", boundary_header);
|
||||
|
||||
if ( subcontent_length_header[0] ) {
|
||||
start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " );
|
||||
content_length = atoi( start_ptr );
|
||||
Debug( 3, "Got subcontent length '%d'", content_length );
|
||||
if (subcontent_length_header[0]) {
|
||||
start_ptr = subcontent_length_header + strspn(subcontent_length_header, " ");
|
||||
content_length = atoi(start_ptr);
|
||||
Debug(3, "Got subcontent length '%d'", content_length);
|
||||
}
|
||||
if ( subcontent_type_header[0] ) {
|
||||
memset( content_type, 0, sizeof(content_type) );
|
||||
start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " );
|
||||
strcpy( content_type, start_ptr );
|
||||
Debug( 3, "Got subcontent type '%s'", content_type );
|
||||
if (subcontent_type_header[0]) {
|
||||
//memset(content_type, 0, sizeof(content_type));
|
||||
start_ptr = subcontent_type_header + strspn(subcontent_type_header, " ");
|
||||
strcpy(content_type, start_ptr);
|
||||
Debug(3, "Got subcontent type '%s'", content_type);
|
||||
}
|
||||
state = CONTENT;
|
||||
} else {
|
||||
Debug( 3, "Unable to extract subheader from stream, retrying" );
|
||||
Debug(3, "Unable to extract subheader from stream, retrying");
|
||||
buffer_len = GetData();
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read subheader" );
|
||||
return( -1 );
|
||||
if (buffer_len < 0) {
|
||||
Error("Unable to read subheader");
|
||||
return -1;
|
||||
}
|
||||
bytes += buffer_len;
|
||||
state = SUBHEADERCONT;
|
||||
|
@ -942,90 +942,90 @@ int RemoteCameraHttp::GetResponse() {
|
|||
*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;
|
||||
} else if ( !strcasecmp( content_type, "image/x-rgb" ) ) {
|
||||
} else if (!strcasecmp(content_type, "image/x-rgb")) {
|
||||
format = X_RGB;
|
||||
} else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) {
|
||||
} else if (!strcasecmp(content_type, "image/x-rgbz")) {
|
||||
format = X_RGBZ;
|
||||
} else {
|
||||
Error( "Found unsupported content type '%s'", content_type );
|
||||
return( -1 );
|
||||
Error("Found unsupported content type '%s'", content_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This is an early test for jpeg content, so we can bail early
|
||||
if ( format == JPEG && buffer.size() >= 2 ) {
|
||||
if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) {
|
||||
Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] );
|
||||
return( -1 );
|
||||
if (format == JPEG and buffer.size() >= 2) {
|
||||
if (buffer[0] != 0xff or buffer[1] != 0xd8) {
|
||||
Error("Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( content_length ) {
|
||||
while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) {
|
||||
if (content_length) {
|
||||
while (((long)buffer.size() < content_length) and !zm_terminate) {
|
||||
Debug(4, "getting more data");
|
||||
int bytes_read = GetData();
|
||||
if ( bytes_read < 0 ) {
|
||||
int bytes_read = ReadData(buffer, content_length-buffer.size());
|
||||
if (bytes_read < 0) {
|
||||
Error("Unable to read content");
|
||||
return -1;
|
||||
}
|
||||
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 {
|
||||
// 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");
|
||||
buffer_len = ReadData( buffer );
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read content" );
|
||||
return( -1 );
|
||||
buffer_len = ReadData(buffer);
|
||||
if (buffer_len < 0) {
|
||||
Error("Unable to read content");
|
||||
return -1;
|
||||
}
|
||||
bytes += buffer_len;
|
||||
int buffer_size = buffer.size();
|
||||
if ( buffer_len ) {
|
||||
if (buffer_len) {
|
||||
// Got some data
|
||||
|
||||
if ( mode == MULTI_IMAGE ) {
|
||||
if (mode == MULTI_IMAGE) {
|
||||
// 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;
|
||||
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 {
|
||||
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
|
||||
} else {
|
||||
content_length = buffer_size;
|
||||
Debug( 2, "Got end of image by closure, content-length = %d", content_length );
|
||||
if ( mode == SINGLE_IMAGE ) {
|
||||
Debug(2, "Got end of image by closure, content-length = %d", content_length);
|
||||
if (mode == SINGLE_IMAGE) {
|
||||
char *end_ptr = (char *)buffer+buffer_size;
|
||||
|
||||
// strip off any last line feeds
|
||||
while( *end_ptr == '\r' || *end_ptr == '\n' ) {
|
||||
while (*end_ptr == '\r' || *end_ptr == '\n') {
|
||||
content_length--;
|
||||
end_ptr--;
|
||||
}
|
||||
|
||||
if ( end_ptr != ((char *)buffer+buffer_size) ) {
|
||||
Debug( 2, "Trimmed end of image, new content-length = %d", content_length );
|
||||
if (end_ptr != ((char *)buffer+buffer_size)) {
|
||||
Debug(2, "Trimmed end of image, new content-length = %d", content_length);
|
||||
}
|
||||
} // end if SINGLE_IMAGE
|
||||
} // end if read some data
|
||||
} // end while ! content_length
|
||||
} // end if content_length
|
||||
|
||||
if ( mode == SINGLE_IMAGE ) {
|
||||
if (mode == SINGLE_IMAGE) {
|
||||
state = HEADER;
|
||||
Disconnect();
|
||||
} else {
|
||||
state = SUBHEADER;
|
||||
}
|
||||
|
||||
if ( format == JPEG && buffer.size() >= 2 ) {
|
||||
if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) {
|
||||
Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] );
|
||||
return( -1 );
|
||||
if (format == JPEG && buffer.size() >= 2) {
|
||||
if (buffer[0] != 0xff || buffer[1] != 0xd8) {
|
||||
Error("Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -187,14 +187,16 @@ int main(int argc, char *argv[]) {
|
|||
for (size_t i = 0; i < monitors.size(); i++) {
|
||||
std::shared_ptr<Monitor> monitor = monitors[i];
|
||||
|
||||
if (!(monitor->ShmValid() or monitor->connect())) {
|
||||
Warning("Couldn't connect to monitor %d", monitor->Id());
|
||||
if (sessions[i]) {
|
||||
rtspServer->RemoveSession(sessions[i]->GetMediaSessionId());
|
||||
sessions[i] = nullptr;
|
||||
}
|
||||
monitor->disconnect();
|
||||
continue;
|
||||
if (!monitor->ShmValid()) {
|
||||
monitor->disconnect();
|
||||
if (!monitor->connect()) {
|
||||
Warning("Couldn't connect to monitor %d", monitor->Id());
|
||||
if (sessions[i]) {
|
||||
rtspServer->RemoveSession(sessions[i]->GetMediaSessionId());
|
||||
sessions[i] = nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sessions[i]) {
|
||||
|
|
|
@ -46,6 +46,9 @@ class ZM_RtspServer_Authenticator : public xop::Authenticator {
|
|||
if (query.has("jwt_token")) {
|
||||
const QueryParameter *jwt_token = query.get("jwt_token");
|
||||
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) {
|
||||
if (query.has("username")) {
|
||||
std::string username = query.get("username")->firstValue();
|
||||
|
|
|
@ -83,6 +83,7 @@ std::list< std::pair<unsigned char*, size_t> > H264_ZoneMinderFifoSource::splitF
|
|||
}
|
||||
#endif
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
|
||||
if (!bufSize) break;
|
||||
|
||||
buffer = this->extractFrame(&buffer[size], bufSize, size);
|
||||
} // end while buffer
|
||||
|
|
|
@ -84,7 +84,7 @@ int ZoneMinderFifoSource::getNextFrame() {
|
|||
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()) {
|
||||
unsigned int data_size = 0;
|
||||
int64_t pts;
|
||||
|
@ -96,14 +96,14 @@ int ZoneMinderFifoSource::getNextFrame() {
|
|||
header_end = (unsigned char *)memchr(header_start, '\n', m_buffer.tail()-header_start);
|
||||
if (!header_end) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
unsigned int header_size = header_end-header_start;
|
||||
char *header = new char[header_size+1];
|
||||
strncpy(header, reinterpret_cast<const char *>(header_start), header_size);
|
||||
header[header_size] = '\0';
|
||||
strncpy(header, reinterpret_cast<const char *>(header_start), header_end-header_start);
|
||||
|
||||
char *content_length_ptr = strchr(header, ' ');
|
||||
if (!content_length_ptr) {
|
||||
|
@ -125,12 +125,12 @@ int ZoneMinderFifoSource::getNextFrame() {
|
|||
pts_ptr ++;
|
||||
data_size = atoi(content_length_ptr);
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
Debug(4, "ZM Packet size %u pts %" PRId64, data_size, pts);
|
||||
if (header_start != m_buffer) {
|
||||
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]);
|
||||
|
@ -142,21 +142,21 @@ int ZoneMinderFifoSource::getNextFrame() {
|
|||
int bytes_needed = data_size - (m_buffer.size() - header_size);
|
||||
if (bytes_needed > 0) {
|
||||
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 ) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
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);
|
||||
m_buffer.consume(header_size + data_size);
|
||||
m_buffer.consume(data_size);
|
||||
while (framesList.size()) {
|
||||
std::pair<unsigned char*, size_t> nal = framesList.front();
|
||||
framesList.pop_front();
|
||||
|
||||
PushFrame(nal.first, nal.second, pts);
|
||||
}
|
||||
} // end while m_buffer.size()
|
||||
|
|
|
@ -40,7 +40,7 @@ StreamBase::~StreamBase() {
|
|||
bool StreamBase::loadMonitor(int 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);
|
||||
return false;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ bool StreamBase::loadMonitor(int p_monitor_id) {
|
|||
|
||||
if ( !monitor->connect() ) {
|
||||
Error("Unable to connect to monitor id %d for streaming", monitor_id);
|
||||
monitor->disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -71,6 +72,10 @@ bool StreamBase::checkInitialised() {
|
|||
Error("Monitor shm is not connected");
|
||||
return false;
|
||||
}
|
||||
if ((monitor->GetType() == Monitor::FFMPEG) and !monitor->DecodingEnabled() ) {
|
||||
Error("Monitor is not decoding.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -245,18 +245,19 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
|
|||
const char *pass = dbrow[2];
|
||||
|
||||
time_t our_now = now;
|
||||
tm now_tm = {};
|
||||
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",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year);
|
||||
now_tm.tm_hour,
|
||||
now_tm.tm_mday,
|
||||
now_tm.tm_mon,
|
||||
now_tm.tm_year);
|
||||
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char *)auth_key, strlen(auth_key), md5sum);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "zm_config.h"
|
||||
#include "zm_logger.h"
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <fcntl.h> /* Definition of AT_* constants */
|
||||
#include <sstream>
|
||||
|
@ -43,7 +43,7 @@ std::string trimSet(std::string str, std::string trimset) {
|
|||
// 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 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 ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
|
||||
return std::string("");
|
||||
|
@ -148,7 +148,7 @@ const std::string base64Encode(const std::string &inString) {
|
|||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
|
||||
|
||||
if ( *inPtr ) {
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
|
@ -175,7 +175,7 @@ int split(const char* string, const char delim, std::vector<std::string>& items)
|
|||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
|
||||
|
||||
while ( true ) {
|
||||
size_t pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
|
@ -242,7 +242,7 @@ void hwcaps_detect() {
|
|||
} else {
|
||||
sse_version = 0;
|
||||
Debug(1, "Detected a x86\\x86-64 processor");
|
||||
}
|
||||
}
|
||||
#elif defined(__arm__)
|
||||
// ARM processor in 32bit mode
|
||||
// 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"
|
||||
"movdqa (%0),%%xmm0\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 0x40(%0),%%xmm4\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 ) {
|
||||
time_t nowtime;
|
||||
struct tm *nowtm;
|
||||
static char tmbuf[20], buf[28];
|
||||
std::string TimevalToString(timeval tv) {
|
||||
tm now = {};
|
||||
std::array<char, 26> tm_buf = {};
|
||||
|
||||
nowtime = tv.tv_sec;
|
||||
nowtm = localtime(&nowtime);
|
||||
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm);
|
||||
snprintf(buf, sizeof buf-1, "%s.%06ld", tmbuf, tv.tv_usec);
|
||||
return buf;
|
||||
localtime_r(&tv.tv_sec, &now);
|
||||
size_t tm_buf_len = strftime(tm_buf.data(), tm_buf.size(), "%Y-%m-%d %H:%M:%S", &now);
|
||||
if (tm_buf_len == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return stringtf("%s.%06ld", tm_buf.data(), tv.tv_usec);
|
||||
}
|
||||
|
||||
std::string UriDecode( const std::string &encoded ) {
|
||||
|
|
|
@ -63,7 +63,7 @@ void hwcaps_detect();
|
|||
extern unsigned int sse_version;
|
||||
extern unsigned int neonversion;
|
||||
|
||||
char *timeval_to_string( struct timeval tv );
|
||||
std::string TimevalToString(timeval tv);
|
||||
std::string UriDecode( const std::string &encoded );
|
||||
void touch( const char *pathname );
|
||||
|
||||
|
|
|
@ -984,7 +984,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
|
|||
);
|
||||
} else if ( !zm_packet->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");
|
||||
if ( !zm_packet->decode(video_in_ctx) ) {
|
||||
Debug(2, "unable to decode yet.");
|
||||
|
@ -1216,29 +1216,6 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
|
|||
return 0;
|
||||
} // 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) {
|
||||
pkt->pos = -1;
|
||||
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_analysis_thread.h"
|
||||
#include "zm_camera.h"
|
||||
#include "zm_db.h"
|
||||
#include "zm_define.h"
|
||||
|
@ -272,8 +271,6 @@ int main(int argc, char *argv[]) {
|
|||
} // end foreach monitor
|
||||
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 *alarm_capture_delays = new int[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();
|
||||
Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)",
|
||||
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;
|
||||
|
@ -320,29 +311,31 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
}
|
||||
|
||||
gettimeofday(&now, nullptr);
|
||||
// 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];
|
||||
if (delay && 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);
|
||||
if (delay) {
|
||||
gettimeofday(&now, nullptr);
|
||||
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
|
||||
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",
|
||||
sleep_time,
|
||||
now.tv_sec, now.tv_usec,
|
||||
last_capture_times[i].tv_sec, last_capture_times[i].tv_usec,
|
||||
delta_time.delta,
|
||||
delay
|
||||
);
|
||||
|
||||
if (sleep_time > 0) {
|
||||
Debug(4, "usleeping (%d)", sleep_time);
|
||||
usleep(sleep_time);
|
||||
}
|
||||
} // end if has a last_capture time
|
||||
last_capture_times[i] = now;
|
||||
// You have to add back in the previous 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",
|
||||
sleep_time,
|
||||
now.tv_sec, now.tv_usec,
|
||||
last_capture_times[i].tv_sec, last_capture_times[i].tv_usec,
|
||||
delta_time.delta,
|
||||
delay
|
||||
);
|
||||
|
||||
if (sleep_time > 0) {
|
||||
Debug(4, "usleeping (%d)", sleep_time);
|
||||
usleep(sleep_time);
|
||||
}
|
||||
} // end if has a last_capture time
|
||||
last_capture_times[i] = now;
|
||||
} // end if delay
|
||||
} // end foreach n_monitors
|
||||
|
||||
if ((result < 0) or zm_reload) {
|
||||
|
@ -351,16 +344,10 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
} // 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++) {
|
||||
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 [] capture_delays;
|
||||
delete [] last_capture_times;
|
||||
|
@ -385,7 +372,7 @@ int main(int argc, char *argv[]) {
|
|||
for (std::shared_ptr<Monitor> &monitor : monitors) {
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
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());
|
||||
zmDbDo(sql);
|
||||
}
|
||||
|
|
|
@ -241,8 +241,9 @@ int main(int argc, const char *argv[], char **envp) {
|
|||
|
||||
time_t now = time(nullptr);
|
||||
char date_string[64];
|
||||
tm now_tm = {};
|
||||
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(date_string, stdout);
|
||||
|
|
|
@ -506,8 +506,10 @@ int main(int argc, char *argv[]) {
|
|||
struct timeval timestamp = monitor->GetTimestamp(image_idx);
|
||||
if ( verbose ) {
|
||||
char timestamp_str[64] = "None";
|
||||
if ( timestamp.tv_sec )
|
||||
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime(×tamp.tv_sec));
|
||||
if ( timestamp.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 )
|
||||
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
|
||||
else
|
||||
|
|
|
@ -117,6 +117,17 @@ commonprep () {
|
|||
exit 1
|
||||
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
|
||||
|
@ -137,6 +148,13 @@ movecrud () {
|
|||
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior
|
||||
mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
|
||||
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
|
||||
|
@ -347,7 +365,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
|
|||
setdebpkgname
|
||||
movecrud
|
||||
|
||||
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "buster" ]; then
|
||||
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "groovy" ] || [ "${DIST}" == "hirsuit" ] || [ "${DIST}" == "buster" ]; then
|
||||
ln -sfT distros/ubuntu2004 debian
|
||||
elif [ "${DIST}" == "beowulf" ]; then
|
||||
ln -sfT distros/beowulf debian
|
||||
|
|
|
@ -344,7 +344,7 @@ class MonitorsController extends AppController {
|
|||
public function daemonControl($id, $command, $daemon=null) {
|
||||
// Need to see if it is local or remote
|
||||
$monitor = $this->Monitor->find('first', array(
|
||||
'fields' => array('Type', 'Function', 'Device', 'ServerId'),
|
||||
'fields' => array('Id', 'Type', 'Function', 'Device', 'ServerId'),
|
||||
'conditions' => array('Id' => $id)
|
||||
));
|
||||
$monitor = $monitor['Monitor'];
|
||||
|
|
|
@ -83,7 +83,7 @@ class Monitor extends ZM_Object {
|
|||
'LabelX' => 0,
|
||||
'LabelY' => 0,
|
||||
'LabelSize' => 1,
|
||||
'ImageBufferCount' => 20,
|
||||
'ImageBufferCount' => 3,
|
||||
'WarmupCount' => 0,
|
||||
'PreEventCount' => 5,
|
||||
'PostEventCount' => 5,
|
||||
|
|
|
@ -5,6 +5,7 @@ require_once('database.php');
|
|||
$object_cache = array();
|
||||
|
||||
class ZM_Object {
|
||||
protected $_last_error;
|
||||
|
||||
public function __construct($IdOrRow = NULL) {
|
||||
$class = get_class($this);
|
||||
|
@ -38,6 +39,7 @@ class ZM_Object {
|
|||
|
||||
public function __call($fn, array $args){
|
||||
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
|
||||
|
||||
if ( count($args) ) {
|
||||
if ( $type == 'set' and is_array($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]);
|
||||
}
|
||||
} 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);
|
||||
} else {
|
||||
if ( is_array($value) ) {
|
||||
# perhaps should turn into a comma-separated string
|
||||
# perhaps should turn into a comma-separated string
|
||||
$this->{$field} = implode(',', $value);
|
||||
} else if ( is_string($value) ) {
|
||||
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 {
|
||||
$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 {
|
||||
$this->{$field} = trim($value);
|
||||
$this->{$field} = $value;
|
||||
}
|
||||
} else if ( is_integer($value) ) {
|
||||
$this->{$field} = $value;
|
||||
|
@ -207,8 +223,8 @@ class ZM_Object {
|
|||
public function changes($new_values, $defaults=null) {
|
||||
$changes = array();
|
||||
|
||||
if ( $defaults ) {
|
||||
foreach ( $defaults as $field => $type ) {
|
||||
if ($defaults) {
|
||||
foreach ($defaults as $field => $type) {
|
||||
if ( isset($new_values[$field]) ) {
|
||||
# Will have already been handled above
|
||||
continue;
|
||||
|
@ -247,47 +263,40 @@ class ZM_Object {
|
|||
} else if ( $this->$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';
|
||||
if ( $type == 'set' ) {
|
||||
if ($type == 'set') {
|
||||
$old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array());
|
||||
$new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array());
|
||||
|
||||
$diff = array_recursive_diff($old_value, $new_value);
|
||||
if ( count($diff) ) {
|
||||
$changes[$field] = $new_value;
|
||||
}
|
||||
if (count($diff)) $changes[$field] = $new_value;
|
||||
|
||||
# Input might be a command separated string, or an array
|
||||
|
||||
} else {
|
||||
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']) ) {
|
||||
foreach ( $this->defaults[$field]['filter_regexp'] as $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'])) {
|
||||
foreach ($this->defaults[$field]['filter_regexp'] as $regexp) {
|
||||
$value = preg_replace($regexp, '', trim($value));
|
||||
}
|
||||
} else {
|
||||
$value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
|
||||
}
|
||||
}
|
||||
if ( $this->{$field} != $value ) {
|
||||
$changes[$field] = $value;
|
||||
}
|
||||
if ($this->{$field} != $value) $changes[$field] = $value;
|
||||
}
|
||||
} else if ( array_key_exists($field, $this->defaults) ) {
|
||||
if ( is_array($this->defaults[$field]) and isset($this->defaults[$field]['default']) ) {
|
||||
} else if (array_key_exists($field, $this->defaults)) {
|
||||
if (is_array($this->defaults[$field]) and isset($this->defaults[$field]['default'])) {
|
||||
$default = $this->defaults[$field]['default'];
|
||||
} else {
|
||||
$default = $this->defaults[$field];
|
||||
}
|
||||
|
||||
if ( $default != $value ) {
|
||||
$changes[$field] = $value;
|
||||
}
|
||||
if ($default != $value) $changes[$field] = $value;
|
||||
}
|
||||
} # end foreach newvalue
|
||||
|
||||
|
||||
return $changes;
|
||||
} # end public function changes
|
||||
|
||||
|
@ -302,14 +311,20 @@ class ZM_Object {
|
|||
# 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
|
||||
foreach ( $this->defaults as $field => $default ) {
|
||||
if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) {
|
||||
if ( is_array($default) ) {
|
||||
if (!property_exists($this, $field)) {
|
||||
if (is_array($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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} # end foreach default
|
||||
|
||||
$fields = array_filter(
|
||||
$this->defaults,
|
||||
|
@ -329,8 +344,7 @@ class ZM_Object {
|
|||
$sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?';
|
||||
$values = array_map(function($field){ return $this->{$field};}, $fields);
|
||||
$values[] = $this->{'Id'};
|
||||
if ( dbQuery($sql, $values) )
|
||||
return true;
|
||||
if (dbQuery($sql, $values)) return true;
|
||||
} else {
|
||||
unset($fields['Id']);
|
||||
|
||||
|
@ -339,15 +353,16 @@ class ZM_Object {
|
|||
') VALUES ('.
|
||||
implode(', ', array_map(function($field){return $this->$field() == 'NOW()' ? 'NOW()' : '?';}, $fields)).')';
|
||||
|
||||
$values = array_values(array_map(
|
||||
function($field){return $this->$field();},
|
||||
array_filter($fields, function($field){ return $this->$field() != 'NOW()';})
|
||||
));
|
||||
if ( dbQuery($sql, $values) ) {
|
||||
# For some reason comparing 0 to 'NOW()' returns false; So we do this.
|
||||
$filtered = array_filter($fields, function($field){ return ( (!$this->$field()) or ($this->$field() != 'NOW()'));});
|
||||
$mapped = array_map(function($field){return $this->$field();}, $filtered);
|
||||
$values = array_values($mapped);
|
||||
if (dbQuery($sql, $values)) {
|
||||
$this->{'Id'} = dbInsertId();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$this->_last_error = dbError($sql);
|
||||
return false;
|
||||
} // end function save
|
||||
|
||||
|
@ -421,5 +436,8 @@ class ZM_Object {
|
|||
public function remove_from_cache() {
|
||||
return ZM_Object::_remove_from_cache(get_class(), $this);
|
||||
}
|
||||
public function get_last_error() {
|
||||
return $this->_last_error;
|
||||
}
|
||||
} # end class Object
|
||||
?>
|
||||
|
|
|
@ -40,10 +40,12 @@ if ( $action == 'function' ) {
|
|||
$newFunction = validStr($_REQUEST['newFunction']);
|
||||
# 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';
|
||||
$newDecodingEnabled = ( !isset($_REQUEST['newDecodingEnabled']) or $_REQUEST['newDecodingEnabled'] != '1' ) ? '0' : '1';
|
||||
$oldFunction = $monitor->Function();
|
||||
$oldEnabled = $monitor->Enabled();
|
||||
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
|
||||
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled));
|
||||
$oldDecodingEnabled = $monitor->DecodingEnabled();
|
||||
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled || $newDecodingEnabled != $oldDecodingEnabled ) {
|
||||
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled, 'DecodingEnabled'=>$newDecodingEnabled));
|
||||
|
||||
if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) {
|
||||
$monitor->zmcControl(($newFunction != 'None') ? 'restart' : 'stop');
|
||||
|
|
|
@ -18,77 +18,72 @@
|
|||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
if ( $action == 'Save' ) {
|
||||
if ( canEdit('System') ) {
|
||||
if ( !empty($_REQUEST['uid']) ) {
|
||||
$dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid']));
|
||||
} else {
|
||||
$dbUser = array();
|
||||
}
|
||||
global $error_message;
|
||||
|
||||
$types = array();
|
||||
if ( isset($_REQUEST['newUser']['MonitorIds']) and is_array($_REQUEST['newUser']['MonitorIds']) )
|
||||
if ($action == 'Save') {
|
||||
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']);
|
||||
if ( !$_REQUEST['newUser']['Password'] )
|
||||
if (!empty($_REQUEST['newUser']['Password'])) {
|
||||
$_REQUEST['newUser']['Password'] = password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT);
|
||||
} else {
|
||||
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 ( !empty($_REQUEST['uid']) ) {
|
||||
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid']));
|
||||
# If we are updating the logged in user, then update our session user data.
|
||||
if ( $user and ( $dbUser['Username'] == $user['Username'] ) ) {
|
||||
if (count($changes)) {
|
||||
if (!$dbUser->save($changes)) {
|
||||
$error_message = $dbUser->get_last_error();
|
||||
unset($_REQUEST['redirect']);
|
||||
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
|
||||
$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
|
||||
zm_session_start();
|
||||
generateAuthHash(ZM_AUTH_HASH_IPS, true);
|
||||
session_write_close();
|
||||
}
|
||||
} else {
|
||||
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
|
||||
}
|
||||
} # end if changes
|
||||
} else if ( ZM_USER_SELF_EDIT and ( $_REQUEST['uid'] == $user['Id'] ) ) {
|
||||
$uid = $user['Id'];
|
||||
|
||||
$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 if (ZM_USER_SELF_EDIT and ($uid == $user['Id'])) {
|
||||
if (!empty($_REQUEST['newUser']['Password'])) {
|
||||
$_REQUEST['newUser']['Password'] = 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');
|
||||
unset($_REQUEST['newUser']['Password']);
|
||||
}
|
||||
$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']) ) {
|
||||
$changes['Password'] = 'Password = '.$pass_hash;
|
||||
} else {
|
||||
unset($changes['Password']);
|
||||
}
|
||||
if ( count($changes) ) {
|
||||
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
|
||||
if (count($changes)) {
|
||||
if (!$dbUser->save($changes)) {
|
||||
$error_message = $dbUser->get_last_error();
|
||||
unset($_REQUEST['redirect']);
|
||||
return;
|
||||
}
|
||||
|
||||
# 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=?';
|
||||
|
|
|
@ -98,7 +98,7 @@ if ( isset($_GET['skin']) ) {
|
|||
$skin = 'classic';
|
||||
}
|
||||
|
||||
if ( ! is_dir("skins/$skin") ) {
|
||||
if (!is_dir('skins/'.$skin) ) {
|
||||
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
|
||||
|
||||
if ( !in_array($skin, $skins) ) {
|
||||
|
@ -117,10 +117,10 @@ if ( isset($_GET['css']) ) {
|
|||
$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));
|
||||
if ( count($css_skins) ) {
|
||||
if ( !in_array($css, $css_skins) ) {
|
||||
if (count($css_skins)) {
|
||||
if (!in_array($css, $css_skins)) {
|
||||
ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]);
|
||||
$css = $css_skins[0];
|
||||
} else {
|
||||
|
@ -137,7 +137,7 @@ define('ZM_SKIN_PATH', "skins/$skin");
|
|||
define('ZM_SKIN_NAME', $skin);
|
||||
|
||||
$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'");
|
||||
$skinBase[] = $skin;
|
||||
|
||||
|
@ -183,9 +183,6 @@ $user = null;
|
|||
if ( isset($_REQUEST['view']) )
|
||||
$view = detaintPath($_REQUEST['view']);
|
||||
|
||||
if ( isset($_REQUEST['redirect']) )
|
||||
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
|
||||
|
||||
# Add CSP Headers
|
||||
$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;
|
||||
}
|
||||
|
||||
if ( isset($_REQUEST['redirect']) )
|
||||
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
|
||||
|
||||
if ( $redirect ) {
|
||||
ZM\Debug("Redirecting to $redirect");
|
||||
|
|
|
@ -16,3 +16,14 @@ tr.log-dbg td {
|
|||
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 : '';
|
||||
|
||||
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>'
|
||||
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
|
||||
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>';
|
||||
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 ( row.Notes.indexOf('detected:') >= 0 ) {
|
||||
|
|
|
@ -40,10 +40,8 @@ function validateForm(form) {
|
|||
var have_endtime_term = false;
|
||||
for ( var i = 0; i < rows.length; i++ ) {
|
||||
if (
|
||||
( 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 == 'EndDateTime' ) ||
|
||||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndTime' ) ||
|
||||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDate' )
|
||||
) {
|
||||
have_endtime_term = true;
|
||||
|
|
|
@ -48,6 +48,7 @@ function updateMonitorDimensions(element) {
|
|||
form.elements['newMonitor[Height]'].value = dimensions[1];
|
||||
}
|
||||
}
|
||||
update_estimated_ram_use();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -271,7 +271,9 @@ function reloadWebSite(ndx) {
|
|||
}
|
||||
|
||||
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('&');
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ function manageDelConfirmModalBtns() {
|
|||
}
|
||||
|
||||
function initPage() {
|
||||
|
||||
// enable or disable buttons based on current selection and user rights
|
||||
/*
|
||||
renameBtn.prop('disabled', !canEdit.Events);
|
||||
|
|
|
@ -48,7 +48,6 @@ function ajaxRequest(params) {
|
|||
|
||||
function processRows(rows) {
|
||||
$j.each(rows, function(ndx, row) {
|
||||
|
||||
var id = row.Id;
|
||||
row.Id = '<a href="?view=snapshot&id=' + id + '">' + id + '</a>';
|
||||
row.Name = '<a href="?view=snapshot&id=' + id + '">' + row.Name + '</a>';
|
||||
|
@ -192,7 +191,7 @@ function initPage() {
|
|||
window.location.reload(true);
|
||||
});
|
||||
|
||||
/*
|
||||
/*
|
||||
// Manage the ARCHIVE button
|
||||
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
|
||||
var selections = getIdSelections();
|
||||
|
@ -268,7 +267,7 @@ function initPage() {
|
|||
})
|
||||
.fail(logAjaxFail);
|
||||
});
|
||||
*/
|
||||
*/
|
||||
// Manage the DELETE button
|
||||
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
|
||||
if ( ! canEdit.Events ) {
|
||||
|
|
|
@ -202,7 +202,7 @@ function toPercent(field, maxValue) {
|
|||
field.value = 100;
|
||||
}
|
||||
}
|
||||
field.setAttribute('step', 0.01);
|
||||
field.setAttribute('step', 'any');
|
||||
field.setAttribute('max', 100);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,16 +37,14 @@ $mid = null;
|
|||
$monitor = null;
|
||||
if ( !empty($_REQUEST['mid']) ) {
|
||||
$mid = validInt($_REQUEST['mid']);
|
||||
|
||||
$monitor = new ZM\Monitor($mid);
|
||||
if ( $monitor and ZM_OPT_X10 )
|
||||
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($mid));
|
||||
}
|
||||
|
||||
if ( !$monitor ) {
|
||||
$nextId = getTableAutoInc('Monitors');
|
||||
$monitor = new ZM\Monitor();
|
||||
$monitor->Name(translate('Monitor').'-'.$nextId);
|
||||
$monitor->Name(translate('Monitor').'-'.getTableAutoInc('Monitors'));
|
||||
$monitor->WebColour(random_colour());
|
||||
} # end if $_REQUEST['mid']
|
||||
|
||||
|
|
|
@ -156,7 +156,6 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as
|
|||
$userMonitors[] = $monitors[$monitorId]['Name'];
|
||||
}
|
||||
}
|
||||
ZM\Debug("monitors: ".$user_row['Username'] . ' ' . $user_row['MonitorIds']. ' :' . print_r($userMonitors, true));
|
||||
?>
|
||||
<tr>
|
||||
<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;
|
||||
}
|
||||
|
||||
require('includes/User.php');
|
||||
require_once('includes/User.php');
|
||||
|
||||
if ( $_REQUEST['uid'] ) {
|
||||
if ( !($newUser = new ZM\User($_REQUEST['uid'])) ) {
|
||||
|
@ -54,9 +54,9 @@ foreach ( dbFetchAll($sql) as $monitor ) {
|
|||
$focusWindow = true;
|
||||
|
||||
xhtmlHeaders(__FILE__, translate('User').' - '.$newUser->Username());
|
||||
echo getBodyTopHTML();
|
||||
echo getNavBarHTML();
|
||||
?>
|
||||
<body>
|
||||
<?php echo getNavBarHTML() ?>
|
||||
<div id="page">
|
||||
<div class="w-100">
|
||||
<div class="float-left pl-3 pt-1">
|
||||
|
|
|
@ -206,7 +206,7 @@ if ( count($other_zones) ) {
|
|||
array('data-on-change'=>'applyZoneUnits', 'id'=>'newZone[Units]')
|
||||
);
|
||||
# Used later for number inputs
|
||||
$step = $newZone['Units'] == 'Percent' ? ' step="0.01" max="100"' : '';
|
||||
$step = $newZone['Units'] == 'Percent' ? ' step="any" max="100"' : '';
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
Loading…
Reference in New Issue