Merge branch 'master' of github.com:/ZoneMinder/zoneminder

This commit is contained in:
Isaac Connor 2021-03-22 12:41:36 -04:00
commit b5f64f1c69
58 changed files with 1067 additions and 681 deletions

View File

@ -1,6 +1,7 @@
target_compile_options(zm-warning-interface target_compile_options(zm-warning-interface
INTERFACE INTERFACE
-Wall -Wall
-Wconditionally-supported
-Wextra -Wextra
-Wformat-security -Wformat-security
-Wno-cast-function-type -Wno-cast-function-type

@ -1 +1 @@
Subproject commit 20846b25ffc0c9a1de1b6701ca99425ef39e9f3f Subproject commit c81ec629f091b77e2948b1e6113064de7e9bc9e3

View File

@ -138,6 +138,14 @@ if ( $options{command} ) {
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR"); Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
} }
my $zm_terminate = 0;
sub TermHandler {
Info('Received TERM, exiting');
$zm_terminate = 1;
}
$SIG{TERM} = \&TermHandler;
$SIG{INT} = \&TermHandler;
Info("Control server $id/$protocol starting at " Info("Control server $id/$protocol starting at "
.strftime('%y/%m/%d %H:%M:%S', localtime()) .strftime('%y/%m/%d %H:%M:%S', localtime())
); );
@ -166,7 +174,7 @@ if ( $options{command} ) {
my $win = $rin; my $win = $rin;
my $ein = $win; my $ein = $win;
my $timeout = MAX_COMMAND_WAIT; my $timeout = MAX_COMMAND_WAIT;
while ( 1 ) { while (!$zm_terminate) {
my $nfound = select(my $rout = $rin, undef, undef, $timeout); my $nfound = select(my $rout = $rin, undef, undef, $timeout);
if ( $nfound > 0 ) { if ( $nfound > 0 ) {
if ( vec($rout, fileno(SERVER), 1) ) { if ( vec($rout, fileno(SERVER), 1) ) {
@ -202,7 +210,7 @@ if ( $options{command} ) {
} else { } else {
Debug('Select timed out'); Debug('Select timed out');
} }
} # end while forever } # end while !$zm_terminate
Info("Control server $id/$protocol exiting"); Info("Control server $id/$protocol exiting");
unlink($sock_file); unlink($sock_file);
$control->close(); $control->close();

View File

@ -28,13 +28,20 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit(); logInit();
logSetSignal(); logSetSignal();
my $zm_terminate = 0;
sub TermHandler {
Info('Received TERM, exiting');
$zm_terminate = 1;
}
$SIG{TERM} = \&TermHandler;
$SIG{INT} = \&TermHandler;
Info('Stats Daemon starting in '.START_DELAY.' seconds'); Info('Stats Daemon starting in '.START_DELAY.' seconds');
sleep(START_DELAY); sleep(START_DELAY);
my $dbh = zmDbConnect(); my $dbh = zmDbConnect();
while( 1 ) { while (!$zm_terminate) {
while ( ! ( $dbh and $dbh->ping() ) ) { while ( ! ( $dbh and $dbh->ping() ) ) {
Info('Reconnecting to db'); Info('Reconnecting to db');
if ( !($dbh = zmDbConnect()) ) { if ( !($dbh = zmDbConnect()) ) {
@ -95,7 +102,7 @@ while( 1 ) {
zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7)); zmDbDo('DELETE FROM Sessions WHERE access < ? LIMIT 100', time - (60*60*24*7));
sleep($Config{ZM_STATS_UPDATE_INTERVAL}); sleep($Config{ZM_STATS_UPDATE_INTERVAL});
} # end while (1) } # end while (!$zm_terminate)
Info('Stats Daemon exiting'); Info('Stats Daemon exiting');
exit(); exit();

View File

@ -87,6 +87,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit(); logInit();
logSetSignal(); logSetSignal();
my $zm_terminate = 0;
sub TermHandler {
Info('Received TERM, exiting');
$zm_terminate = 1;
}
$SIG{TERM} = \&TermHandler;
$SIG{INT} = \&TermHandler;
Info('Trigger daemon starting'); Info('Trigger daemon starting');
@ -118,7 +125,7 @@ my $win = $rin;
my $ein = $win; my $ein = $win;
my $timeout = SELECT_TIMEOUT; my $timeout = SELECT_TIMEOUT;
my %actions; my %actions;
while (1) { while (!$zm_terminate) {
$rin = $base_rin; $rin = $base_rin;
# Add the file descriptors of any spawned connections # Add the file descriptors of any spawned connections
foreach my $fileno ( keys %spawned_connections ) { foreach my $fileno ( keys %spawned_connections ) {
@ -312,7 +319,7 @@ while (1) {
# zmDbConnect will ping and reconnect if neccessary # zmDbConnect will ping and reconnect if neccessary
$dbh = zmDbConnect(); $dbh = zmDbConnect();
} # end while ( 1 ) } # end while (!$zm_terminate)
Info('Trigger daemon exiting'); Info('Trigger daemon exiting');
exit; exit;

View File

@ -68,6 +68,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
logInit(); logInit();
logSetSignal(); logSetSignal();
my $zm_terminate = 0;
sub TermHandler {
Info('Received TERM, exiting');
$zm_terminate = 1;
}
$SIG{TERM} = \&TermHandler;
$SIG{INT} = \&TermHandler;
Info('Watchdog starting, pausing for '.START_DELAY.' seconds'); Info('Watchdog starting, pausing for '.START_DELAY.' seconds');
sleep(START_DELAY); sleep(START_DELAY);
@ -77,7 +84,7 @@ my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'S
my $sth = $dbh->prepare_cached($sql) my $sth = $dbh->prepare_cached($sql)
or Fatal("Can't prepare '$sql': ".$dbh->errstr()); or Fatal("Can't prepare '$sql': ".$dbh->errstr());
while( 1 ) { while (!$zm_terminate) {
while ( ! ( $dbh and $dbh->ping() ) ) { while ( ! ( $dbh and $dbh->ping() ) ) {
if ( ! ( $dbh = zmDbConnect() ) ) { if ( ! ( $dbh = zmDbConnect() ) ) {
sleep($Config{ZM_WATCH_CHECK_INTERVAL}); sleep($Config{ZM_WATCH_CHECK_INTERVAL});
@ -192,7 +199,7 @@ while( 1 ) {
} # end foreach monitor } # end foreach monitor
sleep($Config{ZM_WATCH_CHECK_INTERVAL}); sleep($Config{ZM_WATCH_CHECK_INTERVAL});
} # end while (1) } # end while (!$zm_terminate)
Info("Watchdog exiting"); Info("Watchdog exiting");
exit(); exit();

View File

@ -16,6 +16,7 @@ set(ZM_BIN_SRC_FILES
zm_crypt.cpp zm_crypt.cpp
zm.cpp zm.cpp
zm_db.cpp zm_db.cpp
zm_decoder_thread.cpp
zm_logger.cpp zm_logger.cpp
zm_event.cpp zm_event.cpp
zm_eventstream.cpp zm_eventstream.cpp

View File

@ -1,10 +1,13 @@
#include "zm_analysis_thread.h" #include "zm_analysis_thread.h"
#include "zm_monitor.h"
#include "zm_signal.h" #include "zm_signal.h"
#include "zm_utils.h" #include "zm_utils.h"
AnalysisThread::AnalysisThread(std::shared_ptr<Monitor> monitor) : //AnalysisThread::AnalysisThread(std::shared_ptr<Monitor> monitor) :
monitor_(std::move(monitor)), terminate_(false) { AnalysisThread::AnalysisThread(Monitor* monitor) :
monitor_(monitor), terminate_(false) {
//monitor_(std::move(monitor)), terminate_(false) {
thread_ = std::thread(&AnalysisThread::Run, this); thread_ = std::thread(&AnalysisThread::Run, this);
} }

View File

@ -1,14 +1,15 @@
#ifndef ZM_ANALYSIS_THREAD_H #ifndef ZM_ANALYSIS_THREAD_H
#define ZM_ANALYSIS_THREAD_H #define ZM_ANALYSIS_THREAD_H
#include "zm_monitor.h" class Monitor;
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <thread> #include <thread>
class AnalysisThread { class AnalysisThread {
public: public:
explicit AnalysisThread(std::shared_ptr<Monitor> monitor); explicit AnalysisThread(Monitor* monitor);
//explicit AnalysisThread(std::shared_ptr<Monitor> monitor);
~AnalysisThread(); ~AnalysisThread();
AnalysisThread(AnalysisThread &rhs) = delete; AnalysisThread(AnalysisThread &rhs) = delete;
AnalysisThread(AnalysisThread &&rhs) = delete; AnalysisThread(AnalysisThread &&rhs) = delete;
@ -18,7 +19,8 @@ class AnalysisThread {
private: private:
void Run(); void Run();
std::shared_ptr<Monitor> monitor_; Monitor* monitor_;
//std::shared_ptr<Monitor> monitor_;
std::atomic<bool> terminate_; std::atomic<bool> terminate_;
std::thread thread_; std::thread thread_;
}; };

53
src/zm_decoder_thread.cpp Normal file
View File

@ -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");
}
}
}

29
src/zm_decoder_thread.h Normal file
View File

@ -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

View File

@ -71,8 +71,8 @@ Event::Event(
std::string notes; std::string notes;
createNotes(notes); createNotes(notes);
struct timeval now; timeval now = {};
gettimeofday(&now, 0); gettimeofday(&now, nullptr);
if ( !start_time.tv_sec ) { if ( !start_time.tv_sec ) {
Warning("Event has zero time, setting to now"); Warning("Event has zero time, setting to now");
@ -80,12 +80,12 @@ Event::Event(
} else if ( start_time.tv_sec > now.tv_sec ) { } else if ( start_time.tv_sec > now.tv_sec ) {
char buffer[26]; char buffer[26];
char buffer_now[26]; char buffer_now[26];
struct tm* tm_info; tm tm_info = {};
tm_info = localtime(&start_time.tv_sec); localtime_r(&start_time.tv_sec, &tm_info);
strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", tm_info); strftime(buffer, 26, "%Y:%m:%d %H:%M:%S", &tm_info);
tm_info = localtime(&now.tv_sec); localtime_r(&now.tv_sec, &tm_info);
strftime(buffer_now, 26, "%Y:%m:%d %H:%M:%S", tm_info); strftime(buffer_now, 26, "%Y:%m:%d %H:%M:%S", &tm_info);
Error( Error(
"StartDateTime in the future starttime %u.%u >? now %u.%u difference %d\n%s\n%s", "StartDateTime in the future starttime %u.%u >? now %u.%u difference %d\n%s\n%s",
@ -661,16 +661,17 @@ bool Event::SetPath(Storage *storage) {
return false; return false;
} }
struct tm *stime = localtime(&start_time.tv_sec); tm stime = {};
localtime_r(&start_time.tv_sec, &stime);
if ( scheme == Storage::DEEP ) { if ( scheme == Storage::DEEP ) {
int dt_parts[6]; int dt_parts[6];
dt_parts[0] = stime->tm_year-100; dt_parts[0] = stime.tm_year-100;
dt_parts[1] = stime->tm_mon+1; dt_parts[1] = stime.tm_mon+1;
dt_parts[2] = stime->tm_mday; dt_parts[2] = stime.tm_mday;
dt_parts[3] = stime->tm_hour; dt_parts[3] = stime.tm_hour;
dt_parts[4] = stime->tm_min; dt_parts[4] = stime.tm_min;
dt_parts[5] = stime->tm_sec; dt_parts[5] = stime.tm_sec;
std::string date_path; std::string date_path;
std::string time_path; std::string time_path;
@ -685,7 +686,7 @@ bool Event::SetPath(Storage *storage) {
if ( i == 2 ) if ( i == 2 )
date_path = path; date_path = path;
} }
time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec); time_path = stringtf("%02d/%02d/%02d", stime.tm_hour, stime.tm_min, stime.tm_sec);
// Create event id symlink // Create event id symlink
std::string id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id); std::string id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id);
@ -695,7 +696,7 @@ bool Event::SetPath(Storage *storage) {
} }
} else if ( scheme == Storage::MEDIUM ) { } else if ( scheme == Storage::MEDIUM ) {
path += stringtf("/%04d-%02d-%02d", path += stringtf("/%04d-%02d-%02d",
stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday stime.tm_year+1900, stime.tm_mon+1, stime.tm_mday
); );
if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) { if ( mkdir(path.c_str(), 0755) and ( errno != EEXIST ) ) {
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno)); Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));

View File

@ -138,18 +138,20 @@ class Event {
bool SetPath(Storage *storage); bool SetPath(Storage *storage);
public: public:
static const char *getSubPath(struct tm *time) { static const char *getSubPath(tm time) {
static char subpath[PATH_MAX] = ""; static char subpath[PATH_MAX] = "";
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
time->tm_year-100, time->tm_mon+1, time->tm_mday, time.tm_year-100, time.tm_mon+1, time.tm_mday,
time->tm_hour, time->tm_min, time->tm_sec); time.tm_hour, time.tm_min, time.tm_sec);
return subpath; return subpath;
} }
static const char *getSubPath(time_t *time) { static const char *getSubPath(time_t *time) {
return Event::getSubPath(localtime(time)); tm time_tm = {};
localtime_r(time, &time_tm);
return Event::getSubPath(time_tm);
} }
const char* getEventFile(void) const { const char* getEventFile() const {
return video_file.c_str(); return video_file.c_str();
} }

View File

@ -171,33 +171,35 @@ bool EventStream::loadEventData(uint64_t event_id) {
const char *storage_path = storage->Path(); const char *storage_path = storage->Path();
if ( event_data->scheme == Storage::DEEP ) { if ( event_data->scheme == Storage::DEEP ) {
struct tm *event_time = localtime(&event_data->start_time); tm event_time = {};
localtime_r(&event_data->start_time, &event_time);
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path), snprintf(event_data->path, sizeof(event_data->path),
"%s/%u/%02d/%02d/%02d/%02d/%02d/%02d", "%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
storage_path, event_data->monitor_id, storage_path, event_data->monitor_id,
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time.tm_year-100, event_time.tm_mon+1, event_time.tm_mday,
event_time->tm_hour, event_time->tm_min, event_time->tm_sec); event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
else else
snprintf(event_data->path, sizeof(event_data->path), snprintf(event_data->path, sizeof(event_data->path),
"%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d", "%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time.tm_year-100, event_time.tm_mon+1, event_time.tm_mday,
event_time->tm_hour, event_time->tm_min, event_time->tm_sec); event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
} else if ( event_data->scheme == Storage::MEDIUM ) { } else if ( event_data->scheme == Storage::MEDIUM ) {
struct tm *event_time = localtime(&event_data->start_time); tm event_time = {};
localtime_r(&event_data->start_time, &event_time);
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path), snprintf(event_data->path, sizeof(event_data->path),
"%s/%u/%04d-%02d-%02d/%" PRIu64, "%s/%u/%04d-%02d-%02d/%" PRIu64,
storage_path, event_data->monitor_id, storage_path, event_data->monitor_id,
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_time.tm_year+1900, event_time.tm_mon+1, event_time.tm_mday,
event_data->event_id); event_data->event_id);
else else
snprintf(event_data->path, sizeof(event_data->path), snprintf(event_data->path, sizeof(event_data->path),
"%s/%s/%u/%04d-%02d-%02d/%" PRIu64, "%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_time.tm_year+1900, event_time.tm_mon+1, event_time.tm_mday,
event_data->event_id); event_data->event_id);
} else { } else {

View File

@ -200,10 +200,10 @@ int FfmpegCamera::Capture(ZMPacket &zm_packet) {
) ) { ) ) {
// if audio stream is behind video stream, then read from audio, otherwise video // if audio stream is behind video stream, then read from audio, otherwise video
mFormatContextPtr = mSecondFormatContext; mFormatContextPtr = mSecondFormatContext;
Debug(1, "Using audio input"); Debug(2, "Using audio input");
} else { } else {
mFormatContextPtr = mFormatContext; mFormatContextPtr = mFormatContext;
Debug(1, "Using video input because %" PRId64 " >= %" PRId64, Debug(2, "Using video input because %" PRId64 " >= %" PRId64,
(mAudioStream?av_rescale_q(mLastAudioPTS, mAudioStream->time_base, AV_TIME_BASE_Q):0), (mAudioStream?av_rescale_q(mLastAudioPTS, mAudioStream->time_base, AV_TIME_BASE_Q):0),
av_rescale_q(mLastVideoPTS, mVideoStream->time_base, AV_TIME_BASE_Q) av_rescale_q(mLastVideoPTS, mVideoStream->time_base, AV_TIME_BASE_Q)
); );

View File

@ -86,7 +86,7 @@ bool Fifo::open() {
} }
long pipe_size = (long)fcntl(raw_fd, F_GETPIPE_SZ); long pipe_size = (long)fcntl(raw_fd, F_GETPIPE_SZ);
if (pipe_size == -1) { if (pipe_size == -1) {
perror("get pipe size failed."); Error("get pipe size failed.");
} }
Debug(1, "default pipe size: %ld\n", pipe_size); Debug(1, "default pipe size: %ld\n", pipe_size);
#endif #endif
@ -104,9 +104,9 @@ bool Fifo::close() {
bool Fifo::writePacket(ZMPacket &packet) { bool Fifo::writePacket(ZMPacket &packet) {
if (!(outfile or open())) return false; if (!(outfile or open())) return false;
Debug(1, "Writing header ZM %u %" PRId64, packet.packet.size, packet.pts); Debug(2, "Writing header ZM %u %" PRId64, packet.packet.size, packet.pts);
// Going to write a brief header // Going to write a brief header
if ( fprintf(outfile, "ZM %u %" PRId64 "\n", packet.packet.size, packet.pts) < 0 ) { if (fprintf(outfile, "ZM %u %" PRId64 "\n", packet.packet.size, packet.pts) < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
Error("Problem during writing: %s", strerror(errno)); Error("Problem during writing: %s", strerror(errno));
} else { } else {
@ -121,6 +121,7 @@ bool Fifo::writePacket(ZMPacket &packet) {
} }
return true; return true;
} }
bool Fifo::writePacket(std::string filename, ZMPacket &packet) { bool Fifo::writePacket(std::string filename, ZMPacket &packet) {
bool on_blocking_abort = true; bool on_blocking_abort = true;
FILE *outfile = nullptr; FILE *outfile = nullptr;

View File

@ -1186,7 +1186,8 @@ cinfo->out_color_space = JCS_RGB;
// This is a lot of stuff to allocate on the stack. Recommend char *timebuf[64]; // This is a lot of stuff to allocate on the stack. Recommend char *timebuf[64];
char timebuf[64], msbuf[64]; char timebuf[64], msbuf[64];
strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime(&(timestamp.tv_sec))); tm timestamp_tm = {};
strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime_r(&timestamp.tv_sec, &timestamp_tm));
snprintf(msbuf, sizeof msbuf, "%06d",(int)(timestamp.tv_usec)); // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it snprintf(msbuf, sizeof msbuf, "%06d",(int)(timestamp.tv_usec)); // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it
unsigned char exiftimes[82] = { unsigned char exiftimes[82] = {
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
@ -2132,7 +2133,8 @@ void Image::Annotate(
void Image::Timestamp( const char *label, const time_t when, const Coord &coord, const int size ) { void Image::Timestamp( const char *label, const time_t when, const Coord &coord, const int size ) {
char time_text[64]; char time_text[64];
strftime(time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime(&when)); tm when_tm = {};
strftime(time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime_r(&when, &when_tm));
if ( label ) { if ( label ) {
// Assume label is max 64, + ' - ' + 64 chars of time_text // Assume label is max 64, + ' - ' + 64 chars of time_text
char text[132]; char text[132];

View File

@ -798,10 +798,10 @@ void LocalCamera::Initialise() {
); );
if ( v4l2_data.fmt.fmt.pix.width != width ) { if ( v4l2_data.fmt.fmt.pix.width != width ) {
Warning("Failed to set requested width"); Warning("Failed to set requested width");
} }
if ( v4l2_data.fmt.fmt.pix.height != height ) { if ( v4l2_data.fmt.fmt.pix.height != height ) {
Warning("Failed to set requested height"); Warning("Failed to set requested height");
} }
/* Buggy driver paranoia. */ /* Buggy driver paranoia. */
@ -2087,8 +2087,11 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
buffer_bytesused = v4l2_data.bufptr->bytesused; buffer_bytesused = v4l2_data.bufptr->bytesused;
bytes += buffer_bytesused; bytes += buffer_bytesused;
if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height) ) { if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) > (width * height) ) {
Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d", Fatal("Captured image dimensions larger than image buffer: V4L2: %dx%d monitor: %dx%d",
v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height);
} else if ( (v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height) ) {
Error("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",
v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height); v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height);
} }
} // end if v4l2 } // end if v4l2

View File

@ -447,7 +447,8 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
} else { } else {
#endif #endif
char *timePtr = timeString; char *timePtr = timeString;
timePtr += strftime(timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec)); tm now_tm = {};
timePtr += strftime(timePtr, sizeof(timeString), "%x %H:%M:%S", localtime_r(&timeVal.tv_sec, &now_tm));
snprintf(timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec); snprintf(timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec);
#if 0 #if 0
} }
@ -524,8 +525,8 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
if (mLogFileFP) { if (mLogFileFP) {
fputs(logString, mLogFileFP); fputs(logString, mLogFileFP);
if (mFlush) fflush(mLogFileFP); if (mFlush) fflush(mLogFileFP);
} else { } else if (mTerminalLevel != NOLOG) {
puts("Logging to file, but failed to open it\n"); puts("Logging to file but failed to open it\n");
} }
} // end if level <= mFileLevel } // end if level <= mFileLevel

View File

@ -395,6 +395,9 @@ Monitor::Monitor()
storage(nullptr), storage(nullptr),
videoStore(nullptr), videoStore(nullptr),
analysis_it(nullptr), analysis_it(nullptr),
analysis_thread(nullptr),
decoder_it(nullptr),
decoder(nullptr),
n_zones(0), n_zones(0),
zones(nullptr), zones(nullptr),
privacy_bitmask(nullptr), privacy_bitmask(nullptr),
@ -444,8 +447,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
storage_id = atoi(dbrow[col]); col++; storage_id = atoi(dbrow[col]); col++;
if ( storage ) if (storage) delete storage;
delete storage;
storage = new Storage(storage_id); storage = new Storage(storage_id);
if ( ! strcmp(dbrow[col], "Local") ) { if ( ! strcmp(dbrow[col], "Local") ) {
@ -580,6 +582,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
warmup_count = atoi(dbrow[col]); col++; warmup_count = atoi(dbrow[col]); col++;
pre_event_count = atoi(dbrow[col]); col++; pre_event_count = atoi(dbrow[col]); col++;
packetqueue.setMaxVideoPackets(pre_event_count); packetqueue.setMaxVideoPackets(pre_event_count);
packetqueue.setKeepKeyframes(videowriter == PASSTHROUGH);
post_event_count = atoi(dbrow[col]); col++; post_event_count = atoi(dbrow[col]); col++;
stream_replay_buffer = atoi(dbrow[col]); col++; stream_replay_buffer = atoi(dbrow[col]); col++;
alarm_frame_count = atoi(dbrow[col]); col++; alarm_frame_count = atoi(dbrow[col]); col++;
@ -634,16 +637,16 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
image_buffer_count, image_size, (image_buffer_count*image_size), image_buffer_count, image_size, (image_buffer_count*image_size),
mem_size); mem_size);
Zone **zones = 0;
int n_zones = Zone::Load(this, zones);
this->AddZones(n_zones, zones);
this->AddPrivacyBitmask(zones);
// Should maybe store this for later use // Should maybe store this for later use
std::string monitor_dir = stringtf("%s/%d", storage->Path(), id); std::string monitor_dir = stringtf("%s/%d", storage->Path(), id);
LoadCamera(); LoadCamera();
if ( purpose != QUERY ) { if ( purpose != QUERY ) {
Zone **zones = 0;
int n_zones = Zone::Load(this, zones);
this->AddZones(n_zones, zones);
this->AddPrivacyBitmask(zones);
if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) { if ( mkdir(monitor_dir.c_str(), 0755) && ( errno != EEXIST ) ) {
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno)); Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
} }
@ -1125,12 +1128,11 @@ Monitor::~Monitor() {
disconnect(); disconnect();
} // end if mem_ptr } // end if mem_ptr
if (analysis_it) { // Will be free by packetqueue destructor
packetqueue.free_it(analysis_it); analysis_it = nullptr;
analysis_it = nullptr; decoder_it = nullptr;
}
for ( int i = 0; i < n_zones; i++ ) { for (int i = 0; i < n_zones; i++) {
delete zones[i]; delete zones[i];
} }
delete[] zones; delete[] zones;
@ -1733,16 +1735,10 @@ void Monitor::UpdateCaptureFPS() {
last_fps_time = now_double; last_fps_time = now_double;
last_capture_image_count = image_count; last_capture_image_count = image_count;
static char sql[ZM_SQL_SML_BUFSIZ]; std::string sql = stringtf(
// The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost, "UPDATE Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u WHERE MonitorId=%u",
// and nothing else will update the status until zmc restarts. Since we are successfully capturing we can new_capture_fps, new_capture_bandwidth, id);
// assume that we are connected dbQueue.push(sql.c_str());
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth,Status) "
"VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE "
"CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected'",
id, new_capture_fps, new_capture_bandwidth, new_capture_fps, new_capture_bandwidth);
dbQueue.push(sql);
} // now != last_fps_time } // now != last_fps_time
} // end if report fps } // end if report fps
} // void Monitor::UpdateCaptureFPS() } // void Monitor::UpdateCaptureFPS()
@ -1802,22 +1798,17 @@ void Monitor::UpdateAnalysisFPS() {
// If there isn't then we keep pre-event + alarm frames. = pre_event_count // If there isn't then we keep pre-event + alarm frames. = pre_event_count
bool Monitor::Analyse() { bool Monitor::Analyse() {
if ( !Enabled() ) {
Warning("Shouldn't be doing Analyse when not Enabled");
return false;
}
if ( !analysis_it )
analysis_it = packetqueue.get_video_it(true);
// if have event, send frames until we find a video packet, at which point do analysis. Adaptive skip should only affect which frames we do analysis on. // if have event, send frames until we find a video packet, at which point do analysis. Adaptive skip should only affect which frames we do analysis on.
// get_analysis_packet will lock the packet and may wait if analysis_it is at the end // get_analysis_packet will lock the packet and may wait if analysis_it is at the end
ZMPacket *snap = packetqueue.get_packet(analysis_it); ZMLockedPacket *packet_lock = packetqueue.get_packet(analysis_it);
if ( !snap ) return false; if (!packet_lock) return false;
ZMPacket *snap = packet_lock->packet_;
// Is it possible for snap->score to be ! -1 ? Not if everything is working correctly // Is it possible for snap->score to be ! -1 ? Not if everything is working correctly
if ( snap->score != -1 ) { if (snap->score != -1) {
snap->unlock(); delete packet_lock;
packetqueue.increment_it(analysis_it); packetqueue.increment_it(analysis_it);
Error("skipping because score was %d", snap->score); Error("skipping because score was %d", snap->score);
return false; return false;
@ -1837,25 +1828,24 @@ bool Monitor::Analyse() {
std::lock_guard<std::mutex> lck(event_mutex); std::lock_guard<std::mutex> lck(event_mutex);
// if we have been told to be OFF, then we are off and don't do any processing. // if we have been told to be OFF, then we are off and don't do any processing.
if ( trigger_data->trigger_state != TriggerState::TRIGGER_OFF ) { if (trigger_data->trigger_state != TriggerState::TRIGGER_OFF) {
Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state)); Debug(4, "Trigger not OFF state is (%d)", int(trigger_data->trigger_state));
int score = 0; int score = 0;
// Ready means that we have captured the warmup # of frames // Ready means that we have captured the warmup # of frames
if ( !Ready() ) { if (!Ready()) {
Debug(3, "Not ready?"); Debug(3, "Not ready?");
snap->unlock(); delete packet_lock;
return false; return false;
} }
Debug(4, "Ready");
std::string cause; std::string cause;
Event::StringSetMap noteSetMap; Event::StringSetMap noteSetMap;
// Specifically told to be on. Setting the score here will trigger the alarm. // Specifically told to be on. Setting the score here will trigger the alarm.
if ( trigger_data->trigger_state == TriggerState::TRIGGER_ON ) { if (trigger_data->trigger_state == TriggerState::TRIGGER_ON) {
score += trigger_data->trigger_score; score += trigger_data->trigger_score;
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score); Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
if ( !event ) { if (!event) {
cause += trigger_data->trigger_cause; cause += trigger_data->trigger_cause;
} }
Event::StringSet noteSet; Event::StringSet noteSet;
@ -1863,12 +1853,13 @@ bool Monitor::Analyse() {
noteSetMap[trigger_data->trigger_cause] = noteSet; noteSetMap[trigger_data->trigger_cause] = noteSet;
} // end if trigger_on } // end if trigger_on
if ( signal_change ) { // FIXME this snap might not be the one that caused the signal change. Need to store that in the packet.
if (signal_change) {
Debug(2, "Signal change, new signal is %d", signal); Debug(2, "Signal change, new signal is %d", signal);
const char *signalText = "Unknown"; const char *signalText = "Unknown";
if ( !signal ) { if (!signal) {
signalText = "Lost"; signalText = "Lost";
if ( event ) { if (event) {
Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, analysis_image_count, event->Id()); Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, analysis_image_count, event->Id());
closeEvent(); closeEvent();
last_section_mod = 0; last_section_mod = 0;
@ -1877,9 +1868,8 @@ bool Monitor::Analyse() {
signalText = "Reacquired"; signalText = "Reacquired";
score += 100; score += 100;
} }
if ( !event ) { if (!event) {
if ( cause.length() ) if (cause.length()) cause += ", ";
cause += ", ";
cause += SIGNAL_CAUSE; cause += SIGNAL_CAUSE;
} }
Event::StringSet noteSet; Event::StringSet noteSet;
@ -1887,64 +1877,25 @@ bool Monitor::Analyse() {
noteSetMap[SIGNAL_CAUSE] = noteSet; noteSetMap[SIGNAL_CAUSE] = noteSet;
shared_data->state = state = IDLE; shared_data->state = state = IDLE;
shared_data->active = signal; shared_data->active = signal;
if ( (function == MODECT or function == MOCORD) and snap->image ) if ((function == MODECT or function == MOCORD) and snap->image)
ref_image.Assign(*(snap->image)); ref_image.Assign(*(snap->image));
}// else }// else
if (signal) { if (signal) {
if (snap->image or (snap->codec_type == AVMEDIA_TYPE_VIDEO)) { if (snap->codec_type == AVMEDIA_TYPE_VIDEO) {
struct timeval *timestamp = snap->timestamp;
if ( Active() and (function == MODECT or function == MOCORD) and snap->image ) {
Debug(3, "signal and active and modect");
Event::StringSet zoneSet;
int motion_score = last_motion_score;
if ( analysis_fps_limit ) {
double capture_fps = get_capture_fps();
motion_frame_skip = capture_fps / analysis_fps_limit;
Debug(1, "Recalculating motion_frame_skip (%d) = capture_fps(%f) / analysis_fps(%f)",
motion_frame_skip, capture_fps, analysis_fps_limit);
}
if ( !(analysis_image_count % (motion_frame_skip+1)) ) {
if ( snap->image ) {
// Get new score.
motion_score = DetectMotion(*(snap->image), zoneSet);
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score);
} else {
Warning("No image in snap");
}
// Why are we updating the last_motion_score too?
last_motion_score = motion_score;
motion_frame_count += 1;
} else {
Debug(1, "Skipped motion detection");
}
if (motion_score) {
score += motion_score;
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
} // end if active and doing motion detection
// Check to see if linked monitors are triggering. // Check to see if linked monitors are triggering.
if (n_linked_monitors > 0) { if (n_linked_monitors > 0) {
Debug(4, "Checking linked monitors"); Debug(1, "Checking linked monitors");
// FIXME improve logic here // FIXME improve logic here
bool first_link = true; bool first_link = true;
Event::StringSet noteSet; Event::StringSet noteSet;
for ( int i = 0; i < n_linked_monitors; i++ ) { for ( int i = 0; i < n_linked_monitors; i++ ) {
// TODO: Shouldn't we try to connect? // TODO: Shouldn't we try to connect?
if ( linked_monitors[i]->isConnected() ) { if ( linked_monitors[i]->isConnected() ) {
Debug(4, "Linked monitor %d %s is connected", Debug(1, "Linked monitor %d %s is connected",
linked_monitors[i]->Id(), linked_monitors[i]->Name()); linked_monitors[i]->Id(), linked_monitors[i]->Name());
if ( linked_monitors[i]->hasAlarmed() ) { if ( linked_monitors[i]->hasAlarmed() ) {
Debug(4, "Linked monitor %d %s is alarmed", Debug(1, "Linked monitor %d %s is alarmed",
linked_monitors[i]->Id(), linked_monitors[i]->Name()); linked_monitors[i]->Id(), linked_monitors[i]->Name());
if ( !event ) { if ( !event ) {
if ( first_link ) { if ( first_link ) {
@ -1957,7 +1908,7 @@ bool Monitor::Analyse() {
noteSet.insert(linked_monitors[i]->Name()); noteSet.insert(linked_monitors[i]->Name());
score += linked_monitors[i]->lastFrameScore(); // 50; score += linked_monitors[i]->lastFrameScore(); // 50;
} else { } else {
Debug(4, "Linked monitor %d %s is not alarmed", Debug(1, "Linked monitor %d %s is not alarmed",
linked_monitors[i]->Id(), linked_monitors[i]->Name()); linked_monitors[i]->Id(), linked_monitors[i]->Name());
} }
} else { } else {
@ -1969,16 +1920,72 @@ bool Monitor::Analyse() {
noteSetMap[LINKED_CAUSE] = noteSet; noteSetMap[LINKED_CAUSE] = noteSet;
} // end if linked_monitors } // end if linked_monitors
if ( decoding_enabled ) {
while (!snap->image and !snap->decoded and !zm_terminate) {
// Need to wait for the decoder thread.
Debug(1, "Waiting for decode");
packet_lock->wait();
if (!snap->image and snap->decoded) {
Debug(1, "No image but was decoded, giving up");
delete packet_lock;
return false;
}
} // end while ! decoded
}
struct timeval *timestamp = snap->timestamp;
if (Active() and (function == MODECT or function == MOCORD)) {
Debug(3, "signal and active and modect");
Event::StringSet zoneSet;
int motion_score = last_motion_score;
if ( analysis_fps_limit ) {
double capture_fps = get_capture_fps();
motion_frame_skip = capture_fps / analysis_fps_limit;
Debug(1, "Recalculating motion_frame_skip (%d) = capture_fps(%f) / analysis_fps(%f)",
motion_frame_skip, capture_fps, analysis_fps_limit);
}
if (!(analysis_image_count % (motion_frame_skip+1))) {
if (snap->image) {
// Get new score.
motion_score = DetectMotion(*(snap->image), zoneSet);
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score);
motion_frame_count += 1;
} else {
Debug(1, "No image in snap, codec likely not ready");
}
// Why are we updating the last_motion_score too?
last_motion_score = motion_score;
} else {
Debug(1, "Skipped motion detection");
}
if (motion_score) {
score += motion_score;
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
} // end if active and doing motion detection
if (function == RECORD or function == MOCORD) { if (function == RECORD or function == MOCORD) {
// If doing record, check to see if we need to close the event or not. // If doing record, check to see if we need to close the event or not.
if (event) { if (event) {
Debug(2, "Have event %" PRIu64 " in mocord", event->Id()); Debug(2, "Have event %" PRIu64 " in mocord", event->Id());
if (section_length
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
&& ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) )
) {
if (section_length &&
( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
&& (
( (function == MOCORD) && (event_close_mode != CLOSE_TIME) )
||
( (function == RECORD) && (event_close_mode == CLOSE_TIME) )
|| ! ( timestamp->tv_sec % section_length )
)
) {
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d", Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d",
name, image_count, event->Id(), name, image_count, event->Id(),
timestamp->tv_sec, video_store_data->recording.tv_sec, timestamp->tv_sec, video_store_data->recording.tv_sec,
@ -1999,22 +2006,33 @@ bool Monitor::Analyse() {
); );
// This gets a lock on the starting packet // This gets a lock on the starting packet
ZMPacket *starting_packet = packetqueue.get_packet(start_it);
ZMLockedPacket *starting_packet_lock = nullptr;
ZMPacket *starting_packet = nullptr;
if ( *start_it != snap_it ) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) return false;
starting_packet = starting_packet_lock->packet_;
} else {
starting_packet = snap;
}
event = new Event(this, *(starting_packet->timestamp), "Continuous", noteSetMap); event = new Event(this, *(starting_packet->timestamp), "Continuous", noteSetMap);
// Write out starting packets, do not modify packetqueue it will garbage collect itself // Write out starting packets, do not modify packetqueue it will garbage collect itself
while ( starting_packet and (*start_it) != snap_it ) { while ( starting_packet and ((*start_it) != snap_it) ) {
event->AddPacket(starting_packet); event->AddPacket(starting_packet);
// Have added the packet, don't want to unlock it until we have locked the next // Have added the packet, don't want to unlock it until we have locked the next
packetqueue.increment_it(start_it); packetqueue.increment_it(start_it);
if ( (*start_it) == snap_it ) { if ( (*start_it) == snap_it ) {
starting_packet->unlock(); if (starting_packet_lock) delete starting_packet_lock;
break; break;
} }
ZMPacket *p = packetqueue.get_packet(start_it); ZMLockedPacket *lp = packetqueue.get_packet(start_it);
starting_packet->unlock(); delete starting_packet_lock;
starting_packet = p; if (!lp) return false;
starting_packet_lock = lp;
starting_packet = lp->packet_;
} }
packetqueue.free_it(start_it); packetqueue.free_it(start_it);
delete start_it; delete start_it;
@ -2030,9 +2048,7 @@ bool Monitor::Analyse() {
for ( int i=0; i < n_zones; i++ ) { for ( int i=0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) { if ( zones[i]->Alarmed() ) {
alarm_cause += std::string(zones[i]->Label()); alarm_cause += std::string(zones[i]->Label());
if ( i < n_zones-1 ) { if (i < n_zones-1) alarm_cause += ",";
alarm_cause += ",";
}
} }
} }
alarm_cause = cause+" "+alarm_cause; alarm_cause = cause+" "+alarm_cause;
@ -2085,7 +2101,16 @@ bool Monitor::Analyse() {
snap_it, snap_it,
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count) (pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
); );
ZMPacket *starting_packet = *(*start_it);
ZMLockedPacket *starting_packet_lock = nullptr;
ZMPacket *starting_packet = nullptr;
if ( *start_it != snap_it ) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) return false;
starting_packet = starting_packet_lock->packet_;
} else {
starting_packet = snap;
}
event = new Event(this, *(starting_packet->timestamp), cause, noteSetMap); event = new Event(this, *(starting_packet->timestamp), cause, noteSetMap);
shared_data->last_event_id = event->Id(); shared_data->last_event_id = event->Id();
@ -2099,12 +2124,18 @@ bool Monitor::Analyse() {
packetqueue.increment_it(start_it); packetqueue.increment_it(start_it);
if ( (*start_it) == snap_it ) { if ( (*start_it) == snap_it ) {
starting_packet->unlock(); if (starting_packet_lock) delete starting_packet_lock;
break; break;
} }
ZMPacket *p = packetqueue.get_packet(start_it); ZMLockedPacket *lp = packetqueue.get_packet(start_it);
starting_packet->unlock(); delete starting_packet_lock;
starting_packet = p; if (!lp) {
// Shutting down event will be closed by ~Monitor()
// Perhaps we shouldn't do this.
return false;
}
starting_packet_lock = lp;
starting_packet = lp->packet_;
} }
packetqueue.free_it(start_it); packetqueue.free_it(start_it);
delete start_it; delete start_it;
@ -2138,7 +2169,7 @@ bool Monitor::Analyse() {
Debug(1, "Staying in %s", State_Strings[state].c_str()); Debug(1, "Staying in %s", State_Strings[state].c_str());
} }
if ( state == ALARM ) { if (state == ALARM) {
last_alarm_count = analysis_image_count; last_alarm_count = analysis_image_count;
} // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT } // This is needed so post_event_count counts after last alarmed frames while in ALARM not single alarmed frames while ALERT
} else { // no score? } else { // no score?
@ -2146,7 +2177,7 @@ bool Monitor::Analyse() {
if (state == ALARM) { if (state == ALARM) {
Info("%s: %03d - Gone into alert state", name, analysis_image_count); Info("%s: %03d - Gone into alert state", name, analysis_image_count);
shared_data->state = state = ALERT; shared_data->state = state = ALERT;
} else if ( state == ALERT ) { } else if (state == ALERT) {
if ( if (
( analysis_image_count-last_alarm_count > post_event_count ) ( analysis_image_count-last_alarm_count > post_event_count )
&& &&
@ -2164,7 +2195,7 @@ bool Monitor::Analyse() {
shared_data->state = state = TAPE; shared_data->state = state = TAPE;
} }
} }
} else if ( state == PREALARM ) { } else if (state == PREALARM) {
// Back to IDLE // Back to IDLE
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE); shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
} else { } else {
@ -2172,19 +2203,19 @@ bool Monitor::Analyse() {
State_Strings[state].c_str(), analysis_image_count, last_alarm_count, post_event_count, State_Strings[state].c_str(), analysis_image_count, last_alarm_count, post_event_count,
timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length); timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length);
} }
if ( Event::PreAlarmCount() ) if (Event::PreAlarmCount())
Event::EmptyPreAlarmFrames(); Event::EmptyPreAlarmFrames();
} // end if score or not } // end if score or not
snap->score = score; snap->score = score;
if ( state == PREALARM ) { if (state == PREALARM) {
// Generate analysis images if necessary // Generate analysis images if necessary
if ( (savejpegs > 1) and snap->image ) { if ((savejpegs > 1) and snap->image) {
for ( int i = 0; i < n_zones; i++ ) { for (int i = 0; i < n_zones; i++) {
if ( zones[i]->Alarmed() ) { if (zones[i]->Alarmed()) {
if ( zones[i]->AlarmImage() ) { if (zones[i]->AlarmImage()) {
if ( ! snap->analysis_image ) if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image)); snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zones[i]->AlarmImage())); snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
} }
@ -2195,38 +2226,42 @@ bool Monitor::Analyse() {
// incremement pre alarm image count // incremement pre alarm image count
//have_pre_alarmed_frames ++; //have_pre_alarmed_frames ++;
Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr); Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr);
} else if ( state == ALARM ) { } else if (state == ALARM) {
if ( ( savejpegs > 1 ) and snap->image ) { if ((savejpegs > 1 ) and snap->image) {
for ( int i = 0; i < n_zones; i++ ) { for (int i = 0; i < n_zones; i++) {
if ( zones[i]->Alarmed() ) { if (zones[i]->Alarmed()) {
if ( zones[i]->AlarmImage() ) { if (zones[i]->AlarmImage()) {
if ( ! snap->analysis_image ) if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image)); snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zones[i]->AlarmImage())); snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
} }
if ( config.record_event_stats ) if (config.record_event_stats)
zones[i]->RecordStats(event); zones[i]->RecordStats(event);
} // end if zone is alarmed } // end if zone is alarmed
} // end foreach zone } // end foreach zone
} }
if ( noteSetMap.size() > 0 ) if (event) {
event->updateNotes(noteSetMap); if (noteSetMap.size() > 0)
if ( section_length event->updateNotes(noteSetMap);
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) if ( section_length
&& ! (image_count % fps_report_interval) && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
) { && ! (image_count % fps_report_interval)
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %d - %d = %d >= %d", ) {
name, image_count, event->Id(), Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %d - %d = %d >= %d",
timestamp->tv_sec, video_store_data->recording.tv_sec, name, image_count, event->Id(),
timestamp->tv_sec - video_store_data->recording.tv_sec, timestamp->tv_sec, video_store_data->recording.tv_sec,
section_length timestamp->tv_sec - video_store_data->recording.tv_sec,
); section_length
closeEvent(); );
event = new Event(this, *timestamp, cause, noteSetMap); closeEvent();
shared_data->last_event_id = event->Id(); event = new Event(this, *timestamp, cause, noteSetMap);
//set up video store data shared_data->last_event_id = event->Id();
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); //set up video store data
video_store_data->recording = event->StartTime(); snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
}
} else {
Error("ALARM but no event");
} }
} else if ( state == ALERT ) { } else if ( state == ALERT ) {
@ -2255,27 +2290,18 @@ bool Monitor::Analyse() {
} // end if ( trigger_data->trigger_state != TRIGGER_OFF ) } // end if ( trigger_data->trigger_state != TRIGGER_OFF )
if (event) event->AddPacket(snap); if (event) event->AddPacket(snap);
#if 0
if (snap->packet.stream_index == video_stream_id) { // In the case where people have pre-alarm frames, the web ui will generate the frame images
if (video_fifo) { // from the mp4. So no one will notice anyways.
if ( snap->keyframe ) { if (snap->image and (videowriter == PASSTHROUGH) and !savejpegs) {
// avcodec strips out important nals that describe the stream and Debug(1, "Deleting image data for %d", snap->image_index);
// stick them in extradata. Need to send them along with keyframes // Don't need raw images anymore
AVStream *stream = camera->getVideoStream(); delete snap->image;
video_fifo->write( snap->image = nullptr;
static_cast<unsigned char *>(stream->codecpar->extradata),
stream->codecpar->extradata_size);
}
video_fifo->writePacket(*snap);
}
} else if (snap->packet.stream_index == audio_stream_id) {
if (audio_fifo)
audio_fifo->writePacket(*snap);
} }
#endif
// popPacket will have placed a second lock on snap, so release it here. // popPacket will have placed a second lock on snap, so release it here.
snap->unlock(); delete packet_lock;
if ( snap->image_index > 0 ) { if ( snap->image_index > 0 ) {
// Only do these if it's a video packet. // Only do these if it's a video packet.
@ -2507,7 +2533,6 @@ int Monitor::Capture() {
gettimeofday(packet->timestamp, nullptr); gettimeofday(packet->timestamp, nullptr);
shared_data->zmc_heartbeat_time = packet->timestamp->tv_sec; shared_data->zmc_heartbeat_time = packet->timestamp->tv_sec;
Image* capture_image = image_buffer[index].image;
int captureResult = 0; int captureResult = 0;
if ( deinterlacing_value == 4 ) { if ( deinterlacing_value == 4 ) {
@ -2526,7 +2551,7 @@ int Monitor::Capture() {
} }
} else { } else {
captureResult = camera->Capture(*packet); captureResult = camera->Capture(*packet);
Debug(4, "Back from capture result=%d image %d", captureResult, image_count); Debug(4, "Back from capture result=%d image count %d", captureResult, image_count);
if ( captureResult < 0 ) { if ( captureResult < 0 ) {
Debug(2, "failed capture"); Debug(2, "failed capture");
@ -2536,7 +2561,7 @@ int Monitor::Capture() {
Rgb signalcolor; Rgb signalcolor;
/* HTML colour code is actually BGR in memory, we want RGB */ /* HTML colour code is actually BGR in memory, we want RGB */
signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR);
capture_image = new Image(width, height, camera->Colours(), camera->SubpixelOrder()); Image *capture_image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
capture_image->Fill(signalcolor); capture_image->Fill(signalcolor);
shared_data->signal = false; shared_data->signal = false;
shared_data->last_write_index = index; shared_data->last_write_index = index;
@ -2550,6 +2575,12 @@ int Monitor::Capture() {
// Don't want to do analysis on it, but we won't due to signal // Don't want to do analysis on it, but we won't due to signal
return -1; return -1;
} else if ( captureResult > 0 ) { } else if ( captureResult > 0 ) {
// If we captured, let's assume signal, Decode will detect further
if (!decoding_enabled) {
shared_data->last_write_index = index;
shared_data->last_write_time = packet->timestamp->tv_sec;
shared_data->signal = true;
}
Debug(2, "Have packet stream_index:%d ?= videostream_id:(%d) q.vpktcount(%d) event?(%d) ", Debug(2, "Have packet stream_index:%d ?= videostream_id:(%d) q.vpktcount(%d) event?(%d) ",
packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ) ); packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ) );
@ -2592,6 +2623,7 @@ int Monitor::Capture() {
return 1; return 1;
} // end if audio } // end if audio
#if 0
if ( !packet->image ) { if ( !packet->image ) {
if ( packet->packet.size and !packet->in_frame ) { if ( packet->packet.size and !packet->in_frame ) {
if ( !decoding_enabled ) { if ( !decoding_enabled ) {
@ -2678,6 +2710,7 @@ int Monitor::Capture() {
shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true; shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true;
shared_data->last_write_index = index; shared_data->last_write_index = index;
shared_data->last_write_time = packet->timestamp->tv_sec; shared_data->last_write_time = packet->timestamp->tv_sec;
#endif
image_count++; image_count++;
// Will only be queued if there are iterators allocated in the queue. // Will only be queued if there are iterators allocated in the queue.
@ -2710,13 +2743,108 @@ int Monitor::Capture() {
return captureResult; return captureResult;
} // end Monitor::Capture } // end Monitor::Capture
bool Monitor::Decode() {
ZMLockedPacket *packet_lock = packetqueue.get_packet(decoder_it);
if (!packet_lock) return false;
ZMPacket *packet = packet_lock->packet_;
packetqueue.increment_it(decoder_it);
if (packet->codec_type != AVMEDIA_TYPE_VIDEO) {
delete packet_lock;
return true; // Don't need decode
}
int ret = 0;
if ((!packet->image) and packet->packet.size and !packet->in_frame) {
// Allocate the image first so that it can be used by hwaccel
// We don't actually care about camera colours, pixel order etc. We care about the desired settings
//
//capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
ret = packet->decode(camera->getVideoCodecContext());
if (ret > 0) {
if (packet->in_frame and !packet->image) {
packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder());
packet->get_image();
}
} else {
Debug(1, "No packet.size(%d) or packet->in_frame(%p). Not decoding", packet->packet.size, packet->in_frame);
}
}
Image* capture_image = nullptr;
unsigned int index = image_count % image_buffer_count;
if (packet->image) {
capture_image = packet->image;
/* Deinterlacing */
if ( deinterlacing_value ) {
if ( deinterlacing_value == 1 ) {
capture_image->Deinterlace_Discard();
} else if ( deinterlacing_value == 2 ) {
capture_image->Deinterlace_Linear();
} else if ( deinterlacing_value == 3 ) {
capture_image->Deinterlace_Blend();
} else if ( deinterlacing_value == 4 ) {
capture_image->Deinterlace_4Field(next_buffer.image, (deinterlacing>>8)&0xff);
} else if ( deinterlacing_value == 5 ) {
capture_image->Deinterlace_Blend_CustomRatio((deinterlacing>>8)&0xff);
}
}
if ( orientation != ROTATE_0 ) {
Debug(2, "Doing rotation");
switch ( orientation ) {
case ROTATE_0 :
// No action required
break;
case ROTATE_90 :
case ROTATE_180 :
case ROTATE_270 :
capture_image->Rotate((orientation-1)*90);
break;
case FLIP_HORI :
case FLIP_VERT :
capture_image->Flip(orientation==FLIP_HORI);
break;
}
} // end if have rotation
if (privacy_bitmask) {
Debug(1, "Applying privacy");
capture_image->MaskPrivacy(privacy_bitmask);
}
if (config.timestamp_on_capture) {
Debug(1, "Timestampprivacy");
TimestampImage(packet->image, packet->timestamp);
}
if (!ref_image.Buffer()) {
// First image, so assign it to ref image
Debug(1, "Assigning ref image %dx%d size: %d", width, height, camera->ImageSize());
ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(),
packet->image->Buffer(), camera->ImageSize());
}
image_buffer[index].image->Assign(*(packet->image));
*(image_buffer[index].timestamp) = *(packet->timestamp);
} // end if have image
packet->decoded = true;
shared_data->signal = ( capture_image and signal_check_points ) ? CheckSignal(capture_image) : true;
shared_data->last_write_index = index;
shared_data->last_write_time = packet->timestamp->tv_sec;
delete packet_lock;
return true;
} // end bool Monitor::Decode()
void Monitor::TimestampImage(Image *ts_image, const struct timeval *ts_time) const { void Monitor::TimestampImage(Image *ts_image, const struct timeval *ts_time) const {
if ( !label_format[0] ) if ( !label_format[0] )
return; return;
// Expand the strftime macros first // Expand the strftime macros first
char label_time_text[256]; char label_time_text[256];
strftime(label_time_text, sizeof(label_time_text), label_format, localtime(&ts_time->tv_sec)); tm ts_tm = {};
strftime(label_time_text, sizeof(label_time_text), label_format, localtime_r(&ts_time->tv_sec, &ts_tm));
char label_text[1024]; char label_text[1024];
const char *s_ptr = label_time_text; const char *s_ptr = label_time_text;
char *d_ptr = label_text; char *d_ptr = label_text;
@ -3026,6 +3154,18 @@ int Monitor::PrimeCapture() {
audio_fifo = new Fifo(shared_data->audio_fifo_path, true); audio_fifo = new Fifo(shared_data->audio_fifo_path, true);
} }
} // end if rtsp_server } // end if rtsp_server
if (decoding_enabled) {
if (!decoder_it) decoder_it = packetqueue.get_video_it(false);
if (!decoder) decoder = new DecoderThread(this);
}
if (!analysis_it) analysis_it = packetqueue.get_video_it(false);
if (!analysis_thread) {
Debug(1, "Starting an analysis thread for monitor (%d)", id);
analysis_thread = new AnalysisThread(this);
}
} else { } else {
Debug(2, "Failed to prime %d", ret); Debug(2, "Failed to prime %d", ret);
} }
@ -3035,13 +3175,24 @@ int Monitor::PrimeCapture() {
int Monitor::PreCapture() const { return camera->PreCapture(); } int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture(); } int Monitor::PostCapture() const { return camera->PostCapture(); }
int Monitor::Close() { int Monitor::Close() {
// Because the stream indexes may change we have to clear out the packetqueue
if (decoder) decoder->Stop();
if (analysis_thread) analysis_thread->Stop();
packetqueue.clear();
if (decoder) {
delete decoder;
decoder = nullptr;
}
if (analysis_thread) {
delete analysis_thread;
analysis_thread = nullptr;
}
std::lock_guard<std::mutex> lck(event_mutex); std::lock_guard<std::mutex> lck(event_mutex);
if (event) { if (event) {
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id()); Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id());
closeEvent(); closeEvent();
} }
if (camera) camera->Close(); if (camera) camera->Close();
packetqueue.clear();
return 1; return 1;
} }
@ -3051,25 +3202,25 @@ Monitor::Orientation Monitor::getOrientation() const { return orientation; }
// So this should be done as the first task in the analysis thread startup. // So this should be done as the first task in the analysis thread startup.
// This function is deprecated. // This function is deprecated.
void Monitor::get_ref_image() { void Monitor::get_ref_image() {
ZMPacket *snap = nullptr; ZMLockedPacket *snap_lock = nullptr;
if ( !analysis_it ) if ( !analysis_it )
analysis_it = packetqueue.get_video_it(true); analysis_it = packetqueue.get_video_it(true);
while ( while (
( (
!( snap = packetqueue.get_packet(analysis_it)) !( snap_lock = packetqueue.get_packet(analysis_it))
or or
( snap->codec_type != AVMEDIA_TYPE_VIDEO ) ( snap_lock->packet_->codec_type != AVMEDIA_TYPE_VIDEO )
or or
! snap->image ! snap_lock->packet_->image
) )
and !zm_terminate) { and !zm_terminate) {
Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)", Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)",
shared_data->last_write_index, shared_data->last_write_time); shared_data->last_write_index, shared_data->last_write_time);
if ( snap and ! snap->image ) { if ( snap_lock and ! snap_lock->packet_->image ) {
snap->unlock(); delete snap_lock;
// can't analyse it anyways, incremement // can't analyse it anyways, incremement
packetqueue.increment_it(analysis_it); packetqueue.increment_it(analysis_it);
} }
@ -3078,6 +3229,7 @@ void Monitor::get_ref_image() {
if ( zm_terminate ) if ( zm_terminate )
return; return;
ZMPacket *snap = snap_lock->packet_;
Debug(1, "get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p", Debug(1, "get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p",
snap->packet.stream_index, video_stream_id, snap->image_index, snap->image ); snap->packet.stream_index, video_stream_id, snap->image_index, snap->image );
// Might not have been decoded yet FIXME // Might not have been decoded yet FIXME
@ -3088,7 +3240,7 @@ void Monitor::get_ref_image() {
} else { } else {
Debug(2, "Have no ref image about to unlock"); Debug(2, "Have no ref image about to unlock");
} }
snap->unlock(); delete snap_lock;
} }
std::vector<Group *> Monitor::Groups() { std::vector<Group *> Monitor::Groups() {

View File

@ -22,6 +22,8 @@
#include "zm_define.h" #include "zm_define.h"
#include "zm_camera.h" #include "zm_camera.h"
#include "zm_analysis_thread.h"
#include "zm_decoder_thread.h"
#include "zm_event.h" #include "zm_event.h"
#include "zm_fifo.h" #include "zm_fifo.h"
#include "zm_image.h" #include "zm_image.h"
@ -375,7 +377,9 @@ protected:
VideoStore *videoStore; VideoStore *videoStore;
PacketQueue packetqueue; PacketQueue packetqueue;
packetqueue_iterator *analysis_it; packetqueue_iterator *analysis_it;
AnalysisThread *analysis_thread;
packetqueue_iterator *decoder_it;
DecoderThread *decoder;
int n_zones; int n_zones;
Zone **zones; Zone **zones;
@ -411,6 +415,8 @@ public:
if ( shared_data && shared_data->valid ) { if ( shared_data && shared_data->valid ) {
struct timeval now; struct timeval now;
gettimeofday(&now, nullptr); gettimeofday(&now, nullptr);
Debug(3, "Shared data is valid, checking heartbeat %u - %u = %d < %f",
now.tv_sec, shared_data->zmc_heartbeat_time, (now.tv_sec - shared_data->zmc_heartbeat_time), config.watch_max_delay);
if ((now.tv_sec - shared_data->zmc_heartbeat_time) < config.watch_max_delay) if ((now.tv_sec - shared_data->zmc_heartbeat_time) < config.watch_max_delay)
return true; return true;
} }
@ -426,6 +432,7 @@ public:
} }
return storage; return storage;
} }
inline CameraType GetType() const { return type; }
inline Function GetFunction() const { return function; } inline Function GetFunction() const { return function; }
inline PacketQueue * GetPacketQueue() { return &packetqueue; } inline PacketQueue * GetPacketQueue() { return &packetqueue; }
inline bool Enabled() const { inline bool Enabled() const {
@ -438,10 +445,6 @@ public:
} }
inline const char *EventPrefix() const { return event_prefix; } inline const char *EventPrefix() const { return event_prefix; }
inline bool Ready() const { inline bool Ready() const {
if ( function <= MONITOR ) {
Error("Should not be calling Ready if the function doesn't include motion detection");
return false;
}
if ( image_count >= ready_count ) { if ( image_count >= ready_count ) {
return true; return true;
} }
@ -549,6 +552,7 @@ public:
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
bool CheckSignal( const Image *image ); bool CheckSignal( const Image *image );
bool Analyse(); bool Analyse();
bool Decode();
void DumpImage( Image *dump_image ) const; void DumpImage( Image *dump_image ) const;
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const; void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
bool closeEvent(); bool closeEvent();

View File

@ -467,28 +467,35 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) } // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
void MonitorStream::runStream() { void MonitorStream::runStream() {
if ( type == STREAM_SINGLE ) { if (type == STREAM_SINGLE) {
// Not yet migrated over to stream class // Not yet migrated over to stream class
SingleImage(scale); if (checkInitialised())
SingleImage(scale);
else
sendTextFrame("Unable to send image");
return; return;
} }
openComms(); openComms();
if ( type == STREAM_JPEG ) if (type == STREAM_JPEG)
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout); fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
if ( !checkInitialised() ) { while (!(loadMonitor(monitor_id) || zm_terminate)) {
Error("Not initialized"); sendTextFrame("Not connected");
while ( !(loadMonitor(monitor_id) || zm_terminate) ) { if (connkey)
sendTextFrame("Not connected"); checkCommandQueue();
if ( connkey ) sleep(1);
checkCommandQueue();
sleep(1);
}
if ( zm_terminate )
return;
} }
if (zm_terminate) return;
while (!checkInitialised() and !zm_terminate) {
sendTextFrame("Unable to stream");
if (connkey)
checkCommandQueue();
sleep(1);
}
if (zm_terminate) return;
updateFrameRate(monitor->GetFPS()); updateFrameRate(monitor->GetFPS());
@ -562,7 +569,7 @@ void MonitorStream::runStream() {
capture_fps = capture_max_fps; capture_fps = capture_max_fps;
} }
while ( !zm_terminate ) { while (!zm_terminate) {
bool got_command = false; bool got_command = false;
if ( feof(stdout) ) { if ( feof(stdout) ) {
Debug(2, "feof stdout"); Debug(2, "feof stdout");
@ -570,7 +577,7 @@ void MonitorStream::runStream() {
} else if ( ferror(stdout) ) { } else if ( ferror(stdout) ) {
Debug(2, "ferror stdout"); Debug(2, "ferror stdout");
break; break;
} else if ( !monitor->ShmValid() ) { } else if (!monitor->ShmValid()) {
Debug(2, "monitor not valid.... maybe we should wait until it comes back."); Debug(2, "monitor not valid.... maybe we should wait until it comes back.");
break; break;
} }
@ -856,7 +863,7 @@ void MonitorStream::SingleImage(int scale) {
int img_buffer_size = 0; int img_buffer_size = 0;
static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE];
Image scaled_image; Image scaled_image;
while ( monitor->shared_data->last_write_index >= monitor->image_buffer_count ) { while ((monitor->shared_data->last_write_index >= monitor->image_buffer_count) and !zm_terminate) {
Debug(1, "Waiting for capture to begin"); Debug(1, "Waiting for capture to begin");
usleep(100000); usleep(100000);
} }

View File

@ -37,7 +37,8 @@ ZMPacket::ZMPacket() :
score(-1), score(-1),
codec_type(AVMEDIA_TYPE_UNKNOWN), codec_type(AVMEDIA_TYPE_UNKNOWN),
image_index(-1), image_index(-1),
codec_imgsize(0) codec_imgsize(0),
decoded(0)
{ {
av_init_packet(&packet); av_init_packet(&packet);
packet.size = 0; // So we can detect whether it has been filled. packet.size = 0; // So we can detect whether it has been filled.

View File

@ -21,6 +21,7 @@
#define ZM_PACKET_H #define ZM_PACKET_H
#include "zm_logger.h" #include "zm_logger.h"
#include <condition_variable>
#include <mutex> #include <mutex>
extern "C" { extern "C" {
@ -36,7 +37,9 @@ class Image;
class ZMPacket { class ZMPacket {
public: public:
std::recursive_mutex mutex; std::mutex mutex_;
std::condition_variable condition_;
int keyframe; int keyframe;
AVStream *stream; // Input stream AVStream *stream; // Input stream
AVPacket packet; // Input packet, undecoded AVPacket packet; // Input packet, undecoded
@ -51,6 +54,7 @@ class ZMPacket {
int image_index; int image_index;
int codec_imgsize; int codec_imgsize;
int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q
bool decoded;
public: public:
AVPacket *av_packet() { return &packet; } AVPacket *av_packet() { return &packet; }
@ -65,21 +69,45 @@ class ZMPacket {
explicit ZMPacket(ZMPacket &packet); explicit ZMPacket(ZMPacket &packet);
ZMPacket(); ZMPacket();
~ZMPacket(); ~ZMPacket();
void lock() {
Debug(4,"Locking packet %d", this->image_index); AVFrame *get_out_frame(const AVCodecContext *ctx);
mutex.lock();
Debug(4,"packet %d locked", this->image_index);
};
bool trylock() {
Debug(4,"TryLocking packet %d", this->image_index);
return mutex.try_lock();
};
void unlock() {
Debug(4,"packet %d unlocked", this->image_index);
mutex.unlock();
};
AVFrame *get_out_frame( const AVCodecContext *ctx );
int get_codec_imgsize() { return codec_imgsize; }; int get_codec_imgsize() { return codec_imgsize; };
}; };
class ZMLockedPacket {
public:
ZMPacket *packet_;
std::unique_lock<std::mutex> lck_;
ZMLockedPacket(ZMPacket *p) :
packet_(p),
lck_(packet_->mutex_, std::defer_lock) {
}
~ZMLockedPacket() {
unlock();
}
void lock() {
Debug(4, "locking packet %d", packet_->image_index);
lck_.lock();
Debug(4, "packet %d locked", packet_->image_index);
};
bool trylock() {
Debug(4, "TryLocking packet %d", packet_->image_index);
return lck_.try_lock();
};
void unlock() {
Debug(4, "packet %d unlocked", packet_->image_index);
lck_.unlock();
packet_->condition_.notify_all();
};
void wait() {
Debug(4, "packet %d waiting", packet_->image_index);
// We already have a lock, but it's a recursive mutex.. so this may be ok
packet_->condition_.wait(lck_);
}
};
#endif /* ZM_PACKET_H */ #endif /* ZM_PACKET_H */

View File

@ -31,7 +31,8 @@ PacketQueue::PacketQueue():
max_video_packet_count(-1), max_video_packet_count(-1),
max_stream_id(-1), max_stream_id(-1),
packet_counts(nullptr), packet_counts(nullptr),
deleting(false) deleting(false),
keep_keyframes(false)
{ {
} }
@ -56,11 +57,11 @@ int PacketQueue::addStream() {
PacketQueue::~PacketQueue() { PacketQueue::~PacketQueue() {
clear(); clear();
if ( packet_counts ) { if (packet_counts) {
delete[] packet_counts; delete[] packet_counts;
packet_counts = nullptr; packet_counts = nullptr;
} }
while ( !iterators.empty() ) { while (!iterators.empty()) {
packetqueue_iterator *it = iterators.front(); packetqueue_iterator *it = iterators.front();
iterators.pop_front(); iterators.pop_front();
delete it; delete it;
@ -87,7 +88,7 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) {
pktQueue.push_back(add_packet); pktQueue.push_back(add_packet);
packet_counts[add_packet->packet.stream_index] += 1; packet_counts[add_packet->packet.stream_index] += 1;
Debug(1, "packet counts for %d is %d", Debug(2, "packet counts for %d is %d",
add_packet->packet.stream_index, add_packet->packet.stream_index,
packet_counts[add_packet->packet.stream_index]); packet_counts[add_packet->packet.stream_index]);
@ -120,56 +121,87 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) {
// //
// So start at the beginning, counting video packets until the next keyframe. // So start at the beginning, counting video packets until the next keyframe.
// Then if deleting those packets doesn't break 1 and 2, then go ahead and delete them. // Then if deleting those packets doesn't break 1 and 2, then go ahead and delete them.
if ( ! ( if (keep_keyframes and ! (
add_packet->packet.stream_index == video_stream_id add_packet->packet.stream_index == video_stream_id
and and
add_packet->keyframe add_packet->keyframe
and and
(packet_counts[video_stream_id] > max_video_packet_count) (packet_counts[video_stream_id] > max_video_packet_count)
and and
*(pktQueue.begin()) != add_packet *(pktQueue.begin()) != add_packet
) )
) { ) {
Debug(3, "stream index %d ?= video_stream_id %d, keyframe %d, counts %d > max %d at begin %d", Debug(3, "stream index %d ?= video_stream_id %d, keyframe %d, keep_keyframes %d, counts %d > max %d at begin %d",
add_packet->packet.stream_index, video_stream_id, add_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count, add_packet->packet.stream_index, video_stream_id, add_packet->keyframe, keep_keyframes, packet_counts[video_stream_id], max_video_packet_count,
( *(pktQueue.begin()) != add_packet ) ( *(pktQueue.begin()) != add_packet )
); );
return; return;
} }
std::unique_lock<std::mutex> lck(mutex); std::unique_lock<std::mutex> lck(mutex);
if (!keep_keyframes) {
// If not doing passthrough, we don't care about starting with a keyframe so logic is simpler
while ((*pktQueue.begin() != add_packet) and (packet_counts[video_stream_id] > max_video_packet_count)) {
ZMPacket *zm_packet = *pktQueue.begin();
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
if (!lp->trylock()) break;
delete lp;
pktQueue.pop_front();
packet_counts[zm_packet->packet.stream_index] -= 1;
Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%d",
zm_packet->packet.stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count, pktQueue.size());
delete zm_packet;
} // end while
return;
}
// If ananlysis_it isn't at the end, we need to keep that many additional packets
int tail_count = 0;
if (pktQueue.back() != add_packet) {
Debug(1, "Ours is not the back");
packetqueue_iterator it = pktQueue.end();
--it;
while (*it != add_packet) {
if ((*it)->packet.stream_index == video_stream_id)
++tail_count;
--it;
}
}
Debug(1, "Tail count is %d", tail_count);
packetqueue_iterator it = pktQueue.begin(); packetqueue_iterator it = pktQueue.begin();
packetqueue_iterator next_front = pktQueue.begin(); packetqueue_iterator next_front = pktQueue.begin();
int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking
// First packet is special because we know it is a video keyframe and only need to check for lock // First packet is special because we know it is a video keyframe and only need to check for lock
ZMPacket *zm_packet = *it; ZMPacket *zm_packet = *it;
if ( zm_packet->trylock() ) { ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
if (lp->trylock()) {
++it; ++it;
zm_packet->unlock(); delete lp;
// Since we have many packets in the queue, we should NOT be pointing at end so don't need to test for that // Since we have many packets in the queue, we should NOT be pointing at end so don't need to test for that
while ( *it != add_packet ) { while (*it != add_packet) {
zm_packet = *it; zm_packet = *it;
if ( !zm_packet->trylock() ) { lp = new ZMLockedPacket(zm_packet);
break; if (!lp->trylock()) break;
} delete lp;
zm_packet->unlock();
if ( is_there_an_iterator_pointing_to_packet(zm_packet) ) { if (is_there_an_iterator_pointing_to_packet(zm_packet)) {
Warning("Found iterator at beginning of queue. Some thread isn't keeping up"); Warning("Found iterator at beginning of queue. Some thread isn't keeping up");
break; break;
} }
if ( zm_packet->packet.stream_index == video_stream_id ) { if (zm_packet->packet.stream_index == video_stream_id) {
if ( zm_packet->keyframe ) { if (zm_packet->keyframe) {
Debug(3, "Have a video keyframe so setting next front to it"); Debug(3, "Have a video keyframe so setting next front to it");
next_front = it; next_front = it;
} }
++video_packets_to_delete; ++video_packets_to_delete;
Debug(4, "Counted %d video packets. Which would leave %d in packetqueue", Debug(4, "Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete); video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete, tail_count);
if (packet_counts[video_stream_id] - video_packets_to_delete <= max_video_packet_count) { if (packet_counts[video_stream_id] - video_packets_to_delete <= max_video_packet_count + tail_count) {
break; break;
} }
} }
@ -200,7 +232,7 @@ void PacketQueue::clearPackets(ZMPacket *add_packet) {
return; return;
} // end voidPacketQueue::clearPackets(ZMPacket* zm_packet) } // end voidPacketQueue::clearPackets(ZMPacket* zm_packet)
ZMPacket* PacketQueue::popPacket( ) { ZMLockedPacket* PacketQueue::popPacket( ) {
Debug(4, "pktQueue size %d", pktQueue.size()); Debug(4, "pktQueue size %d", pktQueue.size());
if ( pktQueue.empty() ) { if ( pktQueue.empty() ) {
return nullptr; return nullptr;
@ -222,14 +254,15 @@ ZMPacket* PacketQueue::popPacket( ) {
} }
} // end foreach iterator } // end foreach iterator
zm_packet->lock(); ZMLockedPacket *lp = new ZMLockedPacket (zm_packet);
lp->lock();
pktQueue.pop_front(); pktQueue.pop_front();
packet_counts[zm_packet->packet.stream_index] -= 1; packet_counts[zm_packet->packet.stream_index] -= 1;
mutex.unlock(); mutex.unlock();
return zm_packet; return lp;
} // popPacket } // popPacket
@ -319,15 +352,17 @@ unsigned int PacketQueue::clear(unsigned int frames_to_keep, int stream_id) {
void PacketQueue::clear() { void PacketQueue::clear() {
deleting = true; deleting = true;
condition.notify_all();
std::unique_lock<std::mutex> lck(mutex); std::unique_lock<std::mutex> lck(mutex);
while (!pktQueue.empty()) { while (!pktQueue.empty()) {
ZMPacket *packet = pktQueue.front(); ZMPacket *packet = pktQueue.front();
// Someone might have this packet, but not for very long and since we have locked the queue they won't be able to get another one // Someone might have this packet, but not for very long and since we have locked the queue they won't be able to get another one
packet->lock(); ZMLockedPacket *lp = new ZMLockedPacket(packet);
lp->lock();
pktQueue.pop_front(); pktQueue.pop_front();
packet->unlock(); delete lp;
delete packet; delete packet;
} }
@ -452,8 +487,8 @@ int PacketQueue::packet_count(int stream_id) {
// Returns a packet. Packet will be locked // Returns a packet. Packet will be locked
ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) { ZMLockedPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
if ( deleting or zm_terminate ) if (deleting or zm_terminate)
return nullptr; return nullptr;
Debug(4, "Locking in get_packet using it %p queue end? %d, packet %p", Debug(4, "Locking in get_packet using it %p queue end? %d, packet %p",
@ -467,7 +502,7 @@ ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
Debug(2, "waiting. Queue size %d it == end? %d", pktQueue.size(), (*it == pktQueue.end())); Debug(2, "waiting. Queue size %d it == end? %d", pktQueue.size(), (*it == pktQueue.end()));
condition.wait(lck); condition.wait(lck);
} }
if ( deleting or zm_terminate ) if (deleting or zm_terminate)
return nullptr; return nullptr;
Debug(4, "get_packet using it %p queue end? %d, packet %p", Debug(4, "get_packet using it %p queue end? %d, packet %p",
@ -477,14 +512,21 @@ ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) {
Error("Null p?!"); Error("Null p?!");
return nullptr; return nullptr;
} }
ZMLockedPacket *lp = new ZMLockedPacket(p);
Debug(3, "get_packet %p image_index: %d, about to lock packet", p, p->image_index); Debug(3, "get_packet %p image_index: %d, about to lock packet", p, p->image_index);
while ( !(zm_terminate or deleting) and !p->trylock() ) { while (!(zm_terminate or deleting) and !lp->trylock()) {
Debug(3, "waiting. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) ); Debug(3, "waiting on index %d. Queue size %d it == end? %d",
p->image_index, pktQueue.size(), ( *it == pktQueue.end() ) );
ZM_DUMP_PACKET(p->packet, "");
condition.wait(lck); condition.wait(lck);
} }
if (deleting or zm_terminate) {
// packet may have been deleted so we can't delete the lp FIXME
return nullptr;
}
Debug(2, "Locked packet, unlocking packetqueue mutex"); Debug(2, "Locked packet, unlocking packetqueue mutex");
return p; return lp;
} // end ZMPacket *PacketQueue::get_packet(it) } // end ZMLockedPacket *PacketQueue::get_packet(it)
bool PacketQueue::increment_it(packetqueue_iterator *it) { bool PacketQueue::increment_it(packetqueue_iterator *it) {
Debug(2, "Incrementing %p, queue size %d, end? %d", it, pktQueue.size(), ((*it) == pktQueue.end())); Debug(2, "Incrementing %p, queue size %d, end? %d", it, pktQueue.size(), ((*it) == pktQueue.end()));
@ -599,7 +641,7 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
if ( wait ) { if ( wait ) {
while ( ((! pktQueue.size()) or (*it == pktQueue.end())) and !zm_terminate and !deleting ) { while ( ((! pktQueue.size()) or (*it == pktQueue.end())) and !zm_terminate and !deleting ) {
Debug(2, "waiting. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) ); Debug(2, "waiting for packets in queue. Queue size %d it == end? %d", pktQueue.size(), ( *it == pktQueue.end() ) );
condition.wait(lck); condition.wait(lck);
*it = pktQueue.begin(); *it = pktQueue.begin();
} }
@ -612,14 +654,14 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
while ( *it != pktQueue.end() ) { while ( *it != pktQueue.end() ) {
ZMPacket *zm_packet = *(*it); ZMPacket *zm_packet = *(*it);
if ( !zm_packet ) { if (!zm_packet) {
Error("Null zmpacket in queue!?"); Error("Null zmpacket in queue!?");
free_it(it); free_it(it);
return nullptr; return nullptr;
} }
Debug(1, "Packet keyframe %d for stream %d, so returning the it to it", Debug(1, "Packet keyframe %d for stream %d, so returning the it to it",
zm_packet->keyframe, zm_packet->packet.stream_index); zm_packet->keyframe, zm_packet->packet.stream_index);
if ( zm_packet->keyframe and ( zm_packet->packet.stream_index == video_stream_id ) ) { if (zm_packet->keyframe and ( zm_packet->packet.stream_index == video_stream_id )) {
Debug(1, "Found a keyframe for stream %d, so returning the it to it", video_stream_id); Debug(1, "Found a keyframe for stream %d, so returning the it to it", video_stream_id);
return it; return it;
} }
@ -627,7 +669,7 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) {
} }
Debug(1, "DIdn't Found a keyframe for stream %d, so returning the it to it", video_stream_id); Debug(1, "DIdn't Found a keyframe for stream %d, so returning the it to it", video_stream_id);
return it; return it;
} } // get video_it
void PacketQueue::free_it(packetqueue_iterator *it) { void PacketQueue::free_it(packetqueue_iterator *it) {
for ( for (

View File

@ -24,6 +24,7 @@
#include <mutex> #include <mutex>
class ZMPacket; class ZMPacket;
class ZMLockedPacket;
typedef std::list<ZMPacket *>::iterator packetqueue_iterator; typedef std::list<ZMPacket *>::iterator packetqueue_iterator;
@ -37,6 +38,7 @@ class PacketQueue {
int max_stream_id; int max_stream_id;
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */ int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
bool deleting; bool deleting;
bool keep_keyframes;
std::list<packetqueue_iterator *> iterators; std::list<packetqueue_iterator *> iterators;
std::mutex mutex; std::mutex mutex;
@ -50,9 +52,10 @@ class PacketQueue {
int addStream(); int addStream();
void setMaxVideoPackets(int p); void setMaxVideoPackets(int p);
void setKeepKeyframes(bool k) { keep_keyframes = k; };
bool queuePacket(ZMPacket* packet); bool queuePacket(ZMPacket* packet);
ZMPacket * popPacket(); ZMLockedPacket * popPacket();
bool popVideoPacket(ZMPacket* packet); bool popVideoPacket(ZMPacket* packet);
bool popAudioPacket(ZMPacket* packet); bool popAudioPacket(ZMPacket* packet);
unsigned int clear(unsigned int video_frames_to_keep, int stream_id); unsigned int clear(unsigned int video_frames_to_keep, int stream_id);
@ -68,7 +71,7 @@ class PacketQueue {
bool increment_it(packetqueue_iterator *it); bool increment_it(packetqueue_iterator *it);
bool increment_it(packetqueue_iterator *it, int stream_id); bool increment_it(packetqueue_iterator *it, int stream_id);
ZMPacket *get_packet(packetqueue_iterator *); ZMLockedPacket *get_packet(packetqueue_iterator *);
packetqueue_iterator *get_video_it(bool wait); packetqueue_iterator *get_video_it(bool wait);
packetqueue_iterator *get_stream_it(int stream_id); packetqueue_iterator *get_stream_it(int stream_id);
void free_it(packetqueue_iterator *); void free_it(packetqueue_iterator *);

View File

@ -202,7 +202,7 @@ int RemoteCameraHttp::SendRequest() {
* > 0 is the # of bytes read. * > 0 is the # of bytes read.
*/ */
int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) { int RemoteCameraHttp::ReadData(Buffer &buffer, unsigned int bytes_expected) {
fd_set rfds; fd_set rfds;
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(sd, &rfds); FD_SET(sd, &rfds);
@ -210,44 +210,44 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
struct timeval temp_timeout = timeout; struct timeval temp_timeout = timeout;
int n_found = select(sd+1, &rfds, nullptr, nullptr, &temp_timeout); int n_found = select(sd+1, &rfds, nullptr, nullptr, &temp_timeout);
if( n_found == 0 ) { if (n_found == 0) {
Debug( 1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec ); Debug(1, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec);
int error = 0; int error = 0;
socklen_t len = sizeof (error); socklen_t len = sizeof(error);
int retval = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len); int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
if(retval != 0 ) { if (retval != 0) {
Debug( 1, "error getting socket error code %s", strerror(retval) ); Debug(1, "error getting socket error code %s", strerror(retval));
} }
if (error != 0 ) { if (error != 0) {
return -1; return -1;
} }
// Why are we disconnecting? It's just a timeout, meaning that data wasn't available. // Why are we disconnecting? It's just a timeout, meaning that data wasn't available.
//Disconnect(); //Disconnect();
return 0; return 0;
} else if ( n_found < 0) { } else if (n_found < 0) {
Error("Select error: %s", strerror(errno)); Error("Select error: %s", strerror(errno));
return -1; return -1;
} }
unsigned int total_bytes_to_read = 0; unsigned int total_bytes_to_read = 0;
if ( bytes_expected ) { if (bytes_expected) {
total_bytes_to_read = bytes_expected; total_bytes_to_read = bytes_expected;
} else { } else {
if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) { if (ioctl(sd, FIONREAD, &total_bytes_to_read) < 0) {
Error( "Can't ioctl(): %s", strerror(errno) ); Error("Can't ioctl(): %s", strerror(errno) );
return -1; return -1;
} }
if ( total_bytes_to_read == 0 ) { if (total_bytes_to_read == 0) {
if ( mode == SINGLE_IMAGE ) { if (mode == SINGLE_IMAGE) {
int error = 0; int error = 0;
socklen_t len = sizeof (error); socklen_t len = sizeof(error);
int retval = getsockopt( sd, SOL_SOCKET, SO_ERROR, &error, &len ); int retval = getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, &len);
if ( retval != 0 ) { if (retval != 0) {
Debug( 1, "error getting socket error code %s", strerror(retval) ); Debug(1, "error getting socket error code %s", strerror(retval));
} }
if ( error != 0 ) { if (error != 0) {
return -1; return -1;
} }
// Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close. // Case where we are grabbing a single jpg, but no content-length was given, so the expectation is that we read until close.
@ -261,38 +261,38 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
} }
// There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily. // There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily.
if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) { if (total_bytes_to_read > ZM_NETWORK_BUFSIZ) {
total_bytes_to_read = ZM_NETWORK_BUFSIZ; total_bytes_to_read = ZM_NETWORK_BUFSIZ;
Debug(4, "Just getting 32K" ); Debug(4, "Just getting 32K");
} else { } else {
Debug(4, "Just getting %d", total_bytes_to_read ); Debug(4, "Just getting %d", total_bytes_to_read);
} }
} // end if bytes_expected or not } // end if bytes_expected or not
Debug( 4, "Expecting %d bytes", total_bytes_to_read ); Debug(4, "Expecting %d bytes", total_bytes_to_read);
int total_bytes_read = 0; int total_bytes_read = 0;
do { do {
int bytes_read = buffer.read_into( sd, total_bytes_to_read ); int bytes_read = buffer.read_into(sd, total_bytes_to_read);
if ( bytes_read < 0 ) { if (bytes_read < 0) {
Error( "Read error: %s", strerror(errno) ); Error("Read error: %s", strerror(errno));
return( -1 ); return -1;
} else if ( bytes_read == 0 ) { } else if (bytes_read == 0) {
Debug( 2, "Socket closed" ); Debug(2, "Socket closed");
//Disconnect(); // Disconnect is done outside of ReadData now. //Disconnect(); // Disconnect is done outside of ReadData now.
return( -1 ); return -1;
} else if ( (unsigned int)bytes_read < total_bytes_to_read ) { } else if ((unsigned int)bytes_read < total_bytes_to_read) {
Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read ); Debug(1, "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read);
return( -1 ); } else {
Debug(3, "Read %d bytes", bytes_read);
} }
Debug( 3, "Read %d bytes", bytes_read );
total_bytes_read += bytes_read; total_bytes_read += bytes_read;
total_bytes_to_read -= bytes_read; total_bytes_to_read -= bytes_read;
} while ( total_bytes_to_read ); } while (total_bytes_to_read);
Debug(4, buffer); Debug(4, "buffer size: %d", static_cast<int>(buffer));
return total_bytes_read; return total_bytes_read;
} } // end readData
int RemoteCameraHttp::GetData() { int RemoteCameraHttp::GetData() {
time_t start_time = time(nullptr); time_t start_time = time(nullptr);
@ -497,36 +497,36 @@ int RemoteCameraHttp::GetResponse() {
return( -1 ); return( -1 );
} }
if ( content_length ) { if (content_length) {
while ( ((long)buffer.size() < content_length ) && ! zm_terminate ) { while (((long)buffer.size() < content_length ) and !zm_terminate) {
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length ); Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length);
int bytes_read = GetData(); int bytes_read = ReadData(buffer, content_length - buffer.size());
if ( bytes_read < 0 ) { if (bytes_read < 0) {
Error( "Unable to read content" ); Error("Unable to read content");
return( -1 ); return -1;
} }
bytes += bytes_read; bytes += bytes_read;
} }
Debug( 3, "Got end of image by length, content-length = %d", content_length ); Debug(3, "Got end of image by length, content-length = %d", content_length);
} else { } else {
while ( !content_length ) { while (!content_length) {
buffer_len = GetData(); buffer_len = GetData();
if ( buffer_len < 0 ) { if (buffer_len < 0) {
Error( "Unable to read content" ); Error("Unable to read content");
return( -1 ); return -1;
} }
bytes += buffer_len; bytes += buffer_len;
static RegExpr *content_expr = 0; static RegExpr *content_expr = 0;
if ( mode == MULTI_IMAGE ) { if (mode == MULTI_IMAGE) {
if ( !content_expr ) { if (!content_expr) {
char content_pattern[256] = ""; char content_pattern[256] = "";
snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); snprintf(content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary);
content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); content_expr = new RegExpr(content_pattern, PCRE_DOTALL);
} }
if ( content_expr->Match( buffer, buffer.size() ) == 2 ) { if (content_expr->Match( buffer, buffer.size()) == 2) {
content_length = content_expr->MatchLength( 1 ); content_length = content_expr->MatchLength( 1 );
Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); Debug(3, "Got end of image by pattern, content-length = %d", content_length);
} }
} }
} }
@ -623,7 +623,7 @@ int RemoteCameraHttp::GetResponse() {
case HEADERCONT : case HEADERCONT :
{ {
buffer_len = GetData(); buffer_len = GetData();
if ( buffer_len < 0 ) { if (buffer_len < 0) {
Error("Unable to read header"); Error("Unable to read header");
return -1; return -1;
} }
@ -634,13 +634,13 @@ int RemoteCameraHttp::GetResponse() {
int header_len = buffer.size(); int header_len = buffer.size();
bool all_headers = false; bool all_headers = false;
while ( true ) { while (true and !zm_terminate) {
int crlf_len = memspn(header_ptr, "\r\n", header_len); int crlf_len = memspn(header_ptr, "\r\n", header_len);
if ( n_headers ) { if (n_headers) {
if ( if (
(crlf_len == 2 && !strncmp(header_ptr, "\n\n", crlf_len )) (crlf_len == 2 && !strncmp(header_ptr, "\n\n", crlf_len))
|| ||
(crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) (crlf_len == 4 && !strncmp(header_ptr, "\r\n\r\n", crlf_len))
) { ) {
Debug(3, "Have double linefeed, done headers"); Debug(3, "Have double linefeed, done headers");
*header_ptr = '\0'; *header_ptr = '\0';
@ -650,8 +650,8 @@ int RemoteCameraHttp::GetResponse() {
break; break;
} }
} }
if ( crlf_len ) { if (crlf_len) {
if ( header_len == crlf_len ) { if (header_len == crlf_len) {
break; break;
} else { } else {
*header_ptr = '\0'; *header_ptr = '\0';
@ -661,22 +661,22 @@ int RemoteCameraHttp::GetResponse() {
} }
Debug(6, "%s", header_ptr); Debug(6, "%s", header_ptr);
if ( (crlf = mempbrk(header_ptr, "\r\n", header_len)) ) { if ((crlf = mempbrk(header_ptr, "\r\n", header_len))) {
//headers[n_headers++] = header_ptr; //headers[n_headers++] = header_ptr;
n_headers++; n_headers++;
if ( !http_header && (strncasecmp(header_ptr, http_match, http_match_len) == 0) ) { if (!http_header && (strncasecmp(header_ptr, http_match, http_match_len) == 0)) {
http_header = header_ptr+http_match_len; http_header = header_ptr+http_match_len;
Debug( 6, "Got http header '%s'", header_ptr ); Debug(6, "Got http header '%s'", header_ptr);
} else if ( !connection_header && (strncasecmp(header_ptr, connection_match, connection_match_len) == 0) ) { } else if ( !connection_header && (strncasecmp(header_ptr, connection_match, connection_match_len) == 0) ) {
connection_header = header_ptr+connection_match_len; connection_header = header_ptr+connection_match_len;
Debug( 6, "Got connection header '%s'", header_ptr ); Debug(6, "Got connection header '%s'", header_ptr);
} else if ( !content_length_header && (strncasecmp(header_ptr, content_length_match, content_length_match_len) == 0) ) { } else if ( !content_length_header && (strncasecmp(header_ptr, content_length_match, content_length_match_len) == 0) ) {
content_length_header = header_ptr+content_length_match_len; content_length_header = header_ptr+content_length_match_len;
Debug( 6, "Got content length header '%s'", header_ptr ); Debug(6, "Got content length header '%s'", header_ptr);
} else if ( !authenticate_header && (strncasecmp(header_ptr, authenticate_match, authenticate_match_len) == 0) ) { } else if ( !authenticate_header && (strncasecmp(header_ptr, authenticate_match, authenticate_match_len) == 0) ) {
authenticate_header = header_ptr; authenticate_header = header_ptr;
Debug( 6, "Got authenticate header '%s'", header_ptr ); Debug(6, "Got authenticate header '%s'", header_ptr);
} else if ( !content_type_header && (strncasecmp(header_ptr, content_type_match, content_type_match_len) == 0) ) { } else if ( !content_type_header && (strncasecmp(header_ptr, content_type_match, content_type_match_len) == 0) ) {
content_type_header = header_ptr+content_type_match_len; content_type_header = header_ptr+content_type_match_len;
Debug(6, "Got content type header '%s'", header_ptr); Debug(6, "Got content type header '%s'", header_ptr);
@ -691,10 +691,10 @@ int RemoteCameraHttp::GetResponse() {
} }
} // end while search for headers } // end while search for headers
if ( all_headers ) { if (all_headers) {
char *start_ptr, *end_ptr; char *start_ptr, *end_ptr;
if ( !http_header ) { if (!http_header) {
Error("Unable to extract HTTP status from header"); Error("Unable to extract HTTP status from header");
return -1; return -1;
} }
@ -703,7 +703,7 @@ int RemoteCameraHttp::GetResponse() {
end_ptr = start_ptr+strspn(start_ptr, "10."); end_ptr = start_ptr+strspn(start_ptr, "10.");
// FIXME Why are we memsetting every time? Can we not do it once? // FIXME Why are we memsetting every time? Can we not do it once?
memset(http_version, 0, sizeof(http_version)); //memset(http_version, 0, sizeof(http_version));
strncpy(http_version, start_ptr, end_ptr-start_ptr); strncpy(http_version, start_ptr, end_ptr-start_ptr);
start_ptr = end_ptr; start_ptr = end_ptr;
@ -718,12 +718,12 @@ int RemoteCameraHttp::GetResponse() {
start_ptr += strspn(start_ptr, " "); start_ptr += strspn(start_ptr, " ");
strcpy(status_mesg, start_ptr); strcpy(status_mesg, start_ptr);
if ( status == 401 ) { if (status == 401) {
if ( mNeedAuth ) { if (mNeedAuth) {
Error("Failed authentication"); Error("Failed authentication");
return -1; return -1;
} }
if ( !authenticate_header ) { if (!authenticate_header) {
Error("Failed authentication, but don't have an authentication header."); Error("Failed authentication, but don't have an authentication header.");
return -1; return -1;
} }
@ -732,7 +732,7 @@ int RemoteCameraHttp::GetResponse() {
Debug(2, "Checking for digest auth in %s", authenticate_header); Debug(2, "Checking for digest auth in %s", authenticate_header);
mAuthenticator->checkAuthResponse(Header); mAuthenticator->checkAuthResponse(Header);
if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { if (mAuthenticator->auth_method() == zm::AUTH_DIGEST) {
Debug(2, "Need Digest Authentication"); Debug(2, "Need Digest Authentication");
request = stringtf("GET %s HTTP/%s\r\n", path.c_str(), config.http_version); request = stringtf("GET %s HTTP/%s\r\n", path.c_str(), config.http_version);
request += stringtf("User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION); request += stringtf("User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION);
@ -747,26 +747,26 @@ int RemoteCameraHttp::GetResponse() {
} else { } else {
Debug(2, "Need some other kind of Authentication"); Debug(2, "Need some other kind of Authentication");
} }
} else if ( status < 200 || status > 299 ) { } else if (status < 200 || status > 299) {
Error("Invalid response status %s: %s", status_code, status_mesg); Error("Invalid response status %s: %s", status_code, status_mesg);
return -1; return -1;
} }
Debug(3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version); Debug(3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version);
if ( connection_header ) { if (connection_header) {
memset(connection_type, 0, sizeof(connection_type)); memset(connection_type, 0, sizeof(connection_type));
start_ptr = connection_header + strspn(connection_header, " "); start_ptr = connection_header + strspn(connection_header, " ");
// FIXME Should we not use strncpy? // FIXME Should we not use strncpy?
strcpy(connection_type, start_ptr); strcpy(connection_type, start_ptr);
Debug(3, "Got connection '%s'", connection_type); Debug(3, "Got connection '%s'", connection_type);
} }
if ( content_length_header ) { if (content_length_header) {
start_ptr = content_length_header + strspn(content_length_header, " "); start_ptr = content_length_header + strspn(content_length_header, " ");
content_length = atoi( start_ptr ); content_length = atoi(start_ptr);
Debug(3, "Got content length '%d'", content_length); Debug(3, "Got content length '%d'", content_length);
} }
if ( content_type_header ) { if (content_type_header) {
memset(content_type, 0, sizeof(content_type)); //memset(content_type, 0, sizeof(content_type));
start_ptr = content_type_header + strspn(content_type_header, " "); start_ptr = content_type_header + strspn(content_type_header, " ");
if ( (end_ptr = strchr(start_ptr, ';')) ) { if ( (end_ptr = strchr(start_ptr, ';')) ) {
strncpy(content_type, start_ptr, end_ptr-start_ptr); strncpy(content_type, start_ptr, end_ptr-start_ptr);
@ -774,7 +774,7 @@ int RemoteCameraHttp::GetResponse() {
start_ptr = end_ptr + strspn(end_ptr, "; "); start_ptr = end_ptr + strspn(end_ptr, "; ");
if ( strncasecmp(start_ptr, boundary_match, boundary_match_len) == 0 ) { if (strncasecmp(start_ptr, boundary_match, boundary_match_len) == 0) {
start_ptr += boundary_match_len; start_ptr += boundary_match_len;
start_ptr += strspn(start_ptr, "-"); start_ptr += strspn(start_ptr, "-");
content_boundary_len = sprintf(content_boundary, "--%s", start_ptr); content_boundary_len = sprintf(content_boundary, "--%s", start_ptr);
@ -788,24 +788,24 @@ int RemoteCameraHttp::GetResponse() {
} }
} // end if content_type_header } // end if content_type_header
if ( !strcasecmp(content_type, "image/jpeg") || !strcasecmp(content_type, "image/jpg") ) { if (!strcasecmp(content_type, "image/jpeg") || !strcasecmp(content_type, "image/jpg")) {
// Single image // Single image
mode = SINGLE_IMAGE; mode = SINGLE_IMAGE;
format = JPEG; format = JPEG;
state = CONTENT; state = CONTENT;
} else if ( !strcasecmp(content_type, "image/x-rgb") ) { } else if (!strcasecmp(content_type, "image/x-rgb")) {
// Single image // Single image
mode = SINGLE_IMAGE; mode = SINGLE_IMAGE;
format = X_RGB; format = X_RGB;
state = CONTENT; state = CONTENT;
} else if ( !strcasecmp(content_type, "image/x-rgbz") ) { } else if (!strcasecmp(content_type, "image/x-rgbz")) {
// Single image // Single image
mode = SINGLE_IMAGE; mode = SINGLE_IMAGE;
format = X_RGBZ; format = X_RGBZ;
state = CONTENT; state = CONTENT;
} else if ( !strcasecmp(content_type, "multipart/x-mixed-replace") ) { } else if (!strcasecmp(content_type, "multipart/x-mixed-replace")) {
// Image stream, so start processing // Image stream, so start processing
if ( !content_boundary[0] ) { if (!content_boundary[0]) {
Error("No content boundary found in header '%s'", content_type_header); Error("No content boundary found in header '%s'", content_type_header);
return -1; return -1;
} }
@ -817,8 +817,8 @@ int RemoteCameraHttp::GetResponse() {
//// MPEG stream, coming soon! //// MPEG stream, coming soon!
//} //}
else { else {
Error( "Unrecognised content type '%s'", content_type ); Error("Unrecognised content type '%s'", content_type);
return( -1 ); return -1;
} }
} else { } else {
Debug(3, "Unable to extract entire header from stream, continuing"); Debug(3, "Unable to extract entire header from stream, continuing");
@ -844,24 +844,24 @@ int RemoteCameraHttp::GetResponse() {
int subheader_len = buffer.size(); int subheader_len = buffer.size();
bool all_headers = false; bool all_headers = false;
while( true ) { while (true and !zm_terminate) {
int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); int crlf_len = memspn(subheader_ptr, "\r\n", subheader_len);
if ( n_subheaders ) { if (n_subheaders) {
if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) { if ( (crlf_len == 2 && !strncmp(subheader_ptr, "\n\n", crlf_len)) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) {
*subheader_ptr = '\0'; *subheader_ptr = '\0';
subheader_ptr += crlf_len; subheader_ptr += crlf_len;
subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); subheader_len -= buffer.consume(subheader_ptr-(char *)buffer);
all_headers = true; all_headers = true;
break; break;
} }
} }
if ( crlf_len ) { if (crlf_len) {
if ( subheader_len == crlf_len ) { if (subheader_len == crlf_len) {
break; break;
} else { } else {
*subheader_ptr = '\0'; *subheader_ptr = '\0';
subheader_ptr += crlf_len; subheader_ptr += crlf_len;
subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); subheader_len -= buffer.consume(subheader_ptr-(char *)buffer);
} }
} }
@ -905,29 +905,29 @@ int RemoteCameraHttp::GetResponse() {
} }
} }
if ( all_headers && boundary_header ) { if (all_headers && boundary_header) {
char *start_ptr/*, *end_ptr*/; char *start_ptr/*, *end_ptr*/;
Debug( 3, "Got boundary '%s'", boundary_header ); Debug(3, "Got boundary '%s'", boundary_header);
if ( subcontent_length_header[0] ) { if (subcontent_length_header[0]) {
start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); start_ptr = subcontent_length_header + strspn(subcontent_length_header, " ");
content_length = atoi( start_ptr ); content_length = atoi(start_ptr);
Debug( 3, "Got subcontent length '%d'", content_length ); Debug(3, "Got subcontent length '%d'", content_length);
} }
if ( subcontent_type_header[0] ) { if (subcontent_type_header[0]) {
memset( content_type, 0, sizeof(content_type) ); //memset(content_type, 0, sizeof(content_type));
start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); start_ptr = subcontent_type_header + strspn(subcontent_type_header, " ");
strcpy( content_type, start_ptr ); strcpy(content_type, start_ptr);
Debug( 3, "Got subcontent type '%s'", content_type ); Debug(3, "Got subcontent type '%s'", content_type);
} }
state = CONTENT; state = CONTENT;
} else { } else {
Debug( 3, "Unable to extract subheader from stream, retrying" ); Debug(3, "Unable to extract subheader from stream, retrying");
buffer_len = GetData(); buffer_len = GetData();
if ( buffer_len < 0 ) { if (buffer_len < 0) {
Error( "Unable to read subheader" ); Error("Unable to read subheader");
return( -1 ); return -1;
} }
bytes += buffer_len; bytes += buffer_len;
state = SUBHEADERCONT; state = SUBHEADERCONT;
@ -942,90 +942,90 @@ int RemoteCameraHttp::GetResponse() {
*semicolon = '\0'; *semicolon = '\0';
} }
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { if (!strcasecmp(content_type, "image/jpeg") || !strcasecmp(content_type, "image/jpg")) {
format = JPEG; format = JPEG;
} else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { } else if (!strcasecmp(content_type, "image/x-rgb")) {
format = X_RGB; format = X_RGB;
} else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { } else if (!strcasecmp(content_type, "image/x-rgbz")) {
format = X_RGBZ; format = X_RGBZ;
} else { } else {
Error( "Found unsupported content type '%s'", content_type ); Error("Found unsupported content type '%s'", content_type);
return( -1 ); return -1;
} }
// This is an early test for jpeg content, so we can bail early // This is an early test for jpeg content, so we can bail early
if ( format == JPEG && buffer.size() >= 2 ) { if (format == JPEG and buffer.size() >= 2) {
if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) { if (buffer[0] != 0xff or buffer[1] != 0xd8) {
Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); Error("Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1]);
return( -1 ); return -1;
} }
} }
if ( content_length ) { if (content_length) {
while ( ( (long)buffer.size() < content_length ) && ! zm_terminate ) { while (((long)buffer.size() < content_length) and !zm_terminate) {
Debug(4, "getting more data"); Debug(4, "getting more data");
int bytes_read = GetData(); int bytes_read = ReadData(buffer, content_length-buffer.size());
if ( bytes_read < 0 ) { if (bytes_read < 0) {
Error("Unable to read content"); Error("Unable to read content");
return -1; return -1;
} }
bytes += bytes_read; bytes += bytes_read;
} }
Debug( 3, "Got end of image by length, content-length = %d", content_length ); Debug(3, "Got end of image by length, content-length = %d", content_length);
} else { } else {
// Read until we find the end of image or the stream closes. // Read until we find the end of image or the stream closes.
while ( !content_length && !zm_terminate ) { while (!content_length && !zm_terminate) {
Debug(4, "!content_length, ReadData"); Debug(4, "!content_length, ReadData");
buffer_len = ReadData( buffer ); buffer_len = ReadData(buffer);
if ( buffer_len < 0 ) { if (buffer_len < 0) {
Error( "Unable to read content" ); Error("Unable to read content");
return( -1 ); return -1;
} }
bytes += buffer_len; bytes += buffer_len;
int buffer_size = buffer.size(); int buffer_size = buffer.size();
if ( buffer_len ) { if (buffer_len) {
// Got some data // Got some data
if ( mode == MULTI_IMAGE ) { if (mode == MULTI_IMAGE) {
// Look for the boundary marker, determine content length using it's position // Look for the boundary marker, determine content length using it's position
if ( char *start_ptr = (char *)memstr( (char *)buffer, "\r\n--", buffer_size ) ) { if (char *start_ptr = (char *)memstr( (char *)buffer, "\r\n--", buffer_size)) {
content_length = start_ptr - (char *)buffer; content_length = start_ptr - (char *)buffer;
Debug( 2, "Got end of image by pattern (crlf--), content-length = %d", content_length ); Debug(2, "Got end of image by pattern (crlf--), content-length = %d", content_length);
} else { } else {
Debug( 2, "Did not find end of image by patten (crlf--) yet, content-length = %d", content_length ); Debug(2, "Did not find end of image by patten (crlf--) yet, content-length = %d", content_length);
} }
} // end if MULTI_IMAGE } // end if MULTI_IMAGE
} else { } else {
content_length = buffer_size; content_length = buffer_size;
Debug( 2, "Got end of image by closure, content-length = %d", content_length ); Debug(2, "Got end of image by closure, content-length = %d", content_length);
if ( mode == SINGLE_IMAGE ) { if (mode == SINGLE_IMAGE) {
char *end_ptr = (char *)buffer+buffer_size; char *end_ptr = (char *)buffer+buffer_size;
// strip off any last line feeds // strip off any last line feeds
while( *end_ptr == '\r' || *end_ptr == '\n' ) { while (*end_ptr == '\r' || *end_ptr == '\n') {
content_length--; content_length--;
end_ptr--; end_ptr--;
} }
if ( end_ptr != ((char *)buffer+buffer_size) ) { if (end_ptr != ((char *)buffer+buffer_size)) {
Debug( 2, "Trimmed end of image, new content-length = %d", content_length ); Debug(2, "Trimmed end of image, new content-length = %d", content_length);
} }
} // end if SINGLE_IMAGE } // end if SINGLE_IMAGE
} // end if read some data } // end if read some data
} // end while ! content_length } // end while ! content_length
} // end if content_length } // end if content_length
if ( mode == SINGLE_IMAGE ) { if (mode == SINGLE_IMAGE) {
state = HEADER; state = HEADER;
Disconnect(); Disconnect();
} else { } else {
state = SUBHEADER; state = SUBHEADER;
} }
if ( format == JPEG && buffer.size() >= 2 ) { if (format == JPEG && buffer.size() >= 2) {
if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) { if (buffer[0] != 0xff || buffer[1] != 0xd8) {
Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); Error("Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1]);
return( -1 ); return -1;
} }
} }

View File

@ -187,14 +187,16 @@ int main(int argc, char *argv[]) {
for (size_t i = 0; i < monitors.size(); i++) { for (size_t i = 0; i < monitors.size(); i++) {
std::shared_ptr<Monitor> monitor = monitors[i]; std::shared_ptr<Monitor> monitor = monitors[i];
if (!(monitor->ShmValid() or monitor->connect())) { if (!monitor->ShmValid()) {
Warning("Couldn't connect to monitor %d", monitor->Id()); monitor->disconnect();
if (sessions[i]) { if (!monitor->connect()) {
rtspServer->RemoveSession(sessions[i]->GetMediaSessionId()); Warning("Couldn't connect to monitor %d", monitor->Id());
sessions[i] = nullptr; if (sessions[i]) {
} rtspServer->RemoveSession(sessions[i]->GetMediaSessionId());
monitor->disconnect(); sessions[i] = nullptr;
continue; }
continue;
}
} }
if (!sessions[i]) { if (!sessions[i]) {

View File

@ -46,6 +46,9 @@ class ZM_RtspServer_Authenticator : public xop::Authenticator {
if (query.has("jwt_token")) { if (query.has("jwt_token")) {
const QueryParameter *jwt_token = query.get("jwt_token"); const QueryParameter *jwt_token = query.get("jwt_token");
user = zmLoadTokenUser(jwt_token->firstValue().c_str(), false); user = zmLoadTokenUser(jwt_token->firstValue().c_str(), false);
} else if (query.has("token")) {
const QueryParameter *jwt_token = query.get("token");
user = zmLoadTokenUser(jwt_token->firstValue().c_str(), false);
} else if (strcmp(config.auth_relay, "none") == 0) { } else if (strcmp(config.auth_relay, "none") == 0) {
if (query.has("username")) { if (query.has("username")) {
std::string username = query.get("username")->firstValue(); std::string username = query.get("username")->firstValue();

View File

@ -83,6 +83,7 @@ std::list< std::pair<unsigned char*, size_t> > H264_ZoneMinderFifoSource::splitF
} }
#endif #endif
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size)); frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
if (!bufSize) break;
buffer = this->extractFrame(&buffer[size], bufSize, size); buffer = this->extractFrame(&buffer[size], bufSize, size);
} // end while buffer } // end while buffer

View File

@ -84,7 +84,7 @@ int ZoneMinderFifoSource::getNextFrame() {
return -1; return -1;
} }
Debug(4, "%s bytes read %d bytes, buffer size %u", m_fifo.c_str(), bytes_read, m_buffer.size()); Debug(1, "%s bytes read %d bytes, buffer size %u", m_fifo.c_str(), bytes_read, m_buffer.size());
while (m_buffer.size()) { while (m_buffer.size()) {
unsigned int data_size = 0; unsigned int data_size = 0;
int64_t pts; int64_t pts;
@ -96,14 +96,14 @@ int ZoneMinderFifoSource::getNextFrame() {
header_end = (unsigned char *)memchr(header_start, '\n', m_buffer.tail()-header_start); header_end = (unsigned char *)memchr(header_start, '\n', m_buffer.tail()-header_start);
if (!header_end) { if (!header_end) {
// Must not have enough data. So... keep all. // Must not have enough data. So... keep all.
Debug(1, "Didn't find newline"); Debug(1, "Didn't find newline buffer size is %d", m_buffer.size());
return 0; return 0;
} }
unsigned int header_size = header_end-header_start; unsigned int header_size = header_end-header_start;
char *header = new char[header_size+1]; char *header = new char[header_size+1];
strncpy(header, reinterpret_cast<const char *>(header_start), header_size);
header[header_size] = '\0'; header[header_size] = '\0';
strncpy(header, reinterpret_cast<const char *>(header_start), header_end-header_start);
char *content_length_ptr = strchr(header, ' '); char *content_length_ptr = strchr(header, ' ');
if (!content_length_ptr) { if (!content_length_ptr) {
@ -125,12 +125,12 @@ int ZoneMinderFifoSource::getNextFrame() {
pts_ptr ++; pts_ptr ++;
data_size = atoi(content_length_ptr); data_size = atoi(content_length_ptr);
pts = strtoll(pts_ptr, nullptr, 10); pts = strtoll(pts_ptr, nullptr, 10);
Debug(4, "ZM Packet %s header_size %d packet size %u pts %" PRId64, header, header_size, data_size, pts);
delete[] header; delete[] header;
} else { } else {
Debug(1, "ZM header not found %s.",m_buffer.head()); Debug(1, "ZM header not found in %d of buffer:%s.", m_buffer.size(), m_buffer.head());
return 0; return 0;
} }
Debug(4, "ZM Packet size %u pts %" PRId64, data_size, pts);
if (header_start != m_buffer) { if (header_start != m_buffer) {
Debug(4, "ZM Packet didn't start at beginning of buffer %u. %c%c", Debug(4, "ZM Packet didn't start at beginning of buffer %u. %c%c",
header_start-m_buffer.head(), m_buffer[0], m_buffer[1]); header_start-m_buffer.head(), m_buffer[0], m_buffer[1]);
@ -142,21 +142,21 @@ int ZoneMinderFifoSource::getNextFrame() {
int bytes_needed = data_size - (m_buffer.size() - header_size); int bytes_needed = data_size - (m_buffer.size() - header_size);
if (bytes_needed > 0) { if (bytes_needed > 0) {
Debug(4, "Need another %d bytes. Trying to read them", bytes_needed); Debug(4, "Need another %d bytes. Trying to read them", bytes_needed);
int bytes_read = m_buffer.read_into(m_fd, bytes_needed, {1,0}); int bytes_read = m_buffer.read_into(m_fd, bytes_needed);
if ( bytes_read != bytes_needed ) { if ( bytes_read != bytes_needed ) {
Debug(4, "Failed to read another %d bytes.", bytes_needed); Debug(1, "Failed to read another %d bytes, got %d.", bytes_needed, bytes_read);
return -1; return -1;
} }
} }
unsigned char *packet_start = m_buffer.head() + header_size; m_buffer.consume(header_size);
unsigned char *packet_start = m_buffer.head();
size_t bytes_remaining = data_size; size_t bytes_remaining = data_size;
std::list< std::pair<unsigned char*, size_t> > framesList = this->splitFrames(packet_start, bytes_remaining); std::list< std::pair<unsigned char*, size_t> > framesList = this->splitFrames(packet_start, bytes_remaining);
Debug(3, "Got %d frames, consuming %d bytes, remaining %d", framesList.size(), header_size + data_size, bytes_remaining); Debug(3, "Got %d frames, consuming %d bytes, remaining %d", framesList.size(), header_size + data_size, bytes_remaining);
m_buffer.consume(header_size + data_size); m_buffer.consume(data_size);
while (framesList.size()) { while (framesList.size()) {
std::pair<unsigned char*, size_t> nal = framesList.front(); std::pair<unsigned char*, size_t> nal = framesList.front();
framesList.pop_front(); framesList.pop_front();
PushFrame(nal.first, nal.second, pts); PushFrame(nal.first, nal.second, pts);
} }
} // end while m_buffer.size() } // end while m_buffer.size()

View File

@ -40,7 +40,7 @@ StreamBase::~StreamBase() {
bool StreamBase::loadMonitor(int p_monitor_id) { bool StreamBase::loadMonitor(int p_monitor_id) {
monitor_id = p_monitor_id; monitor_id = p_monitor_id;
if ( !(monitor = Monitor::Load(monitor_id, false, Monitor::QUERY)) ) { if ( !(monitor or (monitor = Monitor::Load(monitor_id, false, Monitor::QUERY))) ) {
Error("Unable to load monitor id %d for streaming", monitor_id); Error("Unable to load monitor id %d for streaming", monitor_id);
return false; return false;
} }
@ -52,6 +52,7 @@ bool StreamBase::loadMonitor(int p_monitor_id) {
if ( !monitor->connect() ) { if ( !monitor->connect() ) {
Error("Unable to connect to monitor id %d for streaming", monitor_id); Error("Unable to connect to monitor id %d for streaming", monitor_id);
monitor->disconnect();
return false; return false;
} }
@ -71,6 +72,10 @@ bool StreamBase::checkInitialised() {
Error("Monitor shm is not connected"); Error("Monitor shm is not connected");
return false; return false;
} }
if ((monitor->GetType() == Monitor::FFMPEG) and !monitor->DecodingEnabled() ) {
Error("Monitor is not decoding.");
return false;
}
return true; return true;
} }

View File

@ -245,18 +245,19 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
const char *pass = dbrow[2]; const char *pass = dbrow[2];
time_t our_now = now; time_t our_now = now;
tm now_tm = {};
for ( unsigned int i = 0; i < hours; i++, our_now -= 3600 ) { for ( unsigned int i = 0; i < hours; i++, our_now -= 3600 ) {
struct tm *now_tm = localtime(&our_now); localtime_r(&our_now, &now_tm);
snprintf(auth_key, sizeof(auth_key)-1, "%s%s%s%s%d%d%d%d", snprintf(auth_key, sizeof(auth_key)-1, "%s%s%s%s%d%d%d%d",
config.auth_hash_secret, config.auth_hash_secret,
user, user,
pass, pass,
remote_addr, remote_addr,
now_tm->tm_hour, now_tm.tm_hour,
now_tm->tm_mday, now_tm.tm_mday,
now_tm->tm_mon, now_tm.tm_mon,
now_tm->tm_year); now_tm.tm_year);
#if HAVE_DECL_MD5 #if HAVE_DECL_MD5
MD5((unsigned char *)auth_key, strlen(auth_key), md5sum); MD5((unsigned char *)auth_key, strlen(auth_key), md5sum);

View File

@ -22,7 +22,7 @@
#include "zm_config.h" #include "zm_config.h"
#include "zm_logger.h" #include "zm_logger.h"
#include <algorithm> #include <algorithm>
#include <cstdarg> #include <array>
#include <cstring> #include <cstring>
#include <fcntl.h> /* Definition of AT_* constants */ #include <fcntl.h> /* Definition of AT_* constants */
#include <sstream> #include <sstream>
@ -43,7 +43,7 @@ std::string trimSet(std::string str, std::string trimset) {
// Trim Both leading and trailing sets // Trim Both leading and trailing sets
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
// if all spaces or empty return an empty string // if all spaces or empty return an empty string
if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) ) if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
return std::string(""); return std::string("");
@ -148,7 +148,7 @@ const std::string base64Encode(const std::string &inString) {
selection = remainder | (*inPtr >> 4); selection = remainder | (*inPtr >> 4);
remainder = (*inPtr++ & 0x0f) << 2; remainder = (*inPtr++ & 0x0f) << 2;
outString += base64_table[selection]; outString += base64_table[selection];
if ( *inPtr ) { if ( *inPtr ) {
selection = remainder | (*inPtr >> 6); selection = remainder | (*inPtr >> 6);
outString += base64_table[selection]; outString += base64_table[selection];
@ -175,7 +175,7 @@ int split(const char* string, const char delim, std::vector<std::string>& items)
return -2; return -2;
std::string str(string); std::string str(string);
while ( true ) { while ( true ) {
size_t pos = str.find(delim); size_t pos = str.find(delim);
items.push_back(str.substr(0, pos)); items.push_back(str.substr(0, pos));
@ -242,7 +242,7 @@ void hwcaps_detect() {
} else { } else {
sse_version = 0; sse_version = 0;
Debug(1, "Detected a x86\\x86-64 processor"); Debug(1, "Detected a x86\\x86-64 processor");
} }
#elif defined(__arm__) #elif defined(__arm__)
// ARM processor in 32bit mode // ARM processor in 32bit mode
// To see if it supports NEON, we need to get that information from the kernel // To see if it supports NEON, we need to get that information from the kernel
@ -279,7 +279,7 @@ void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
"sse2_copy_iter:\n\t" "sse2_copy_iter:\n\t"
"movdqa (%0),%%xmm0\n\t" "movdqa (%0),%%xmm0\n\t"
"movdqa 0x10(%0),%%xmm1\n\t" "movdqa 0x10(%0),%%xmm1\n\t"
"movdqa 0x20(%0),%%xmm2\n\t" "movdqa 0x20(%0),%%xmm2\n\t"
"movdqa 0x30(%0),%%xmm3\n\t" "movdqa 0x30(%0),%%xmm3\n\t"
"movdqa 0x40(%0),%%xmm4\n\t" "movdqa 0x40(%0),%%xmm4\n\t"
"movdqa 0x50(%0),%%xmm5\n\t" "movdqa 0x50(%0),%%xmm5\n\t"
@ -328,16 +328,17 @@ void timespec_diff(struct timespec *start, struct timespec *end, struct timespec
} }
} }
char *timeval_to_string( struct timeval tv ) { std::string TimevalToString(timeval tv) {
time_t nowtime; tm now = {};
struct tm *nowtm; std::array<char, 26> tm_buf = {};
static char tmbuf[20], buf[28];
nowtime = tv.tv_sec; localtime_r(&tv.tv_sec, &now);
nowtm = localtime(&nowtime); size_t tm_buf_len = strftime(tm_buf.data(), tm_buf.size(), "%Y-%m-%d %H:%M:%S", &now);
strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm); if (tm_buf_len == 0) {
snprintf(buf, sizeof buf-1, "%s.%06ld", tmbuf, tv.tv_usec); return "";
return buf; }
return stringtf("%s.%06ld", tm_buf.data(), tv.tv_usec);
} }
std::string UriDecode( const std::string &encoded ) { std::string UriDecode( const std::string &encoded ) {

View File

@ -63,7 +63,7 @@ void hwcaps_detect();
extern unsigned int sse_version; extern unsigned int sse_version;
extern unsigned int neonversion; extern unsigned int neonversion;
char *timeval_to_string( struct timeval tv ); std::string TimevalToString(timeval tv);
std::string UriDecode( const std::string &encoded ); std::string UriDecode( const std::string &encoded );
void touch( const char *pathname ); void touch( const char *pathname );

View File

@ -984,7 +984,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
); );
} else if ( !zm_packet->in_frame ) { } else if ( !zm_packet->in_frame ) {
Debug(4, "Have no in_frame"); Debug(4, "Have no in_frame");
if ( zm_packet->packet.size ) { if (zm_packet->packet.size and !zm_packet->decoded) {
Debug(4, "Decoding"); Debug(4, "Decoding");
if ( !zm_packet->decode(video_in_ctx) ) { if ( !zm_packet->decode(video_in_ctx) ) {
Debug(2, "unable to decode yet."); Debug(2, "unable to decode yet.");
@ -1216,29 +1216,6 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
return 0; return 0;
} // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt) } // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt)
int VideoStore::write_packets(PacketQueue &queue) {
// Need to write out all the frames from the last keyframe?
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe.
unsigned int packet_count = 0;
ZMPacket *queued_packet;
while ( ( queued_packet = queue.popPacket() ) ) {
AVPacket *avp = queued_packet->av_packet();
packet_count += 1;
//Write the packet to our video store
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)",
avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, queue.size() );
int ret = this->writePacket( queued_packet );
if ( ret < 0 ) {
//Less than zero and we skipped a frame
}
delete queued_packet;
} // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count );
return packet_count;
} // end int VideoStore::write_packets( PacketQueue &queue ) {
int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) {
pkt->pos = -1; pkt->pos = -1;
pkt->stream_index = stream->index; pkt->stream_index = stream->index;

View File

@ -54,7 +54,6 @@ possible, this should run at more or less constant speed.
*/ */
#include "zm.h" #include "zm.h"
#include "zm_analysis_thread.h"
#include "zm_camera.h" #include "zm_camera.h"
#include "zm_db.h" #include "zm_db.h"
#include "zm_define.h" #include "zm_define.h"
@ -272,8 +271,6 @@ int main(int argc, char *argv[]) {
} // end foreach monitor } // end foreach monitor
if (zm_terminate) break; if (zm_terminate) break;
std::vector<std::unique_ptr<AnalysisThread>> analysis_threads = std::vector<std::unique_ptr<AnalysisThread>>();
int *capture_delays = new int[monitors.size()]; int *capture_delays = new int[monitors.size()];
int *alarm_capture_delays = new int[monitors.size()]; int *alarm_capture_delays = new int[monitors.size()];
struct timeval * last_capture_times = new struct timeval[monitors.size()]; struct timeval * last_capture_times = new struct timeval[monitors.size()];
@ -284,12 +281,6 @@ int main(int argc, char *argv[]) {
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)", Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)",
capture_delays[i], alarm_capture_delays[i]); capture_delays[i], alarm_capture_delays[i]);
Monitor::Function function = monitors[0]->GetFunction();
if (function != Monitor::MONITOR) {
Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id());
analysis_threads.emplace_back(ZM::make_unique<AnalysisThread>(monitors[i]));
}
} }
struct timeval now; struct timeval now;
@ -320,29 +311,31 @@ int main(int argc, char *argv[]) {
break; break;
} }
gettimeofday(&now, nullptr);
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate. // capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate.
int delay = (monitors[i]->GetState() == Monitor::ALARM) ? alarm_capture_delays[i] : capture_delays[i]; int delay = (monitors[i]->GetState() == Monitor::ALARM) ? alarm_capture_delays[i] : capture_delays[i];
if (delay && last_capture_times[i].tv_sec) { if (delay) {
// DT_PREC_3 means that the value will be in thousands of a second gettimeofday(&now, nullptr);
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_6); if (last_capture_times[i].tv_sec) {
// DT_PREC_3 means that the value will be in thousands of a second
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_6);
// You have to add back in the previous sleep time // You have to add back in the previous sleep time
sleep_time = delay - (delta_time.delta - sleep_time); sleep_time = delay - (delta_time.delta - sleep_time);
Debug(4, "Sleep time is %d from now:%d.%d last:%d.%d delta %d delay: %d", Debug(4, "Sleep time is %d from now:%d.%d last:%d.%d delta %d delay: %d",
sleep_time, sleep_time,
now.tv_sec, now.tv_usec, now.tv_sec, now.tv_usec,
last_capture_times[i].tv_sec, last_capture_times[i].tv_usec, last_capture_times[i].tv_sec, last_capture_times[i].tv_usec,
delta_time.delta, delta_time.delta,
delay delay
); );
if (sleep_time > 0) { if (sleep_time > 0) {
Debug(4, "usleeping (%d)", sleep_time); Debug(4, "usleeping (%d)", sleep_time);
usleep(sleep_time); usleep(sleep_time);
} }
} // end if has a last_capture time } // end if has a last_capture time
last_capture_times[i] = now; last_capture_times[i] = now;
} // end if delay
} // end foreach n_monitors } // end foreach n_monitors
if ((result < 0) or zm_reload) { if ((result < 0) or zm_reload) {
@ -351,16 +344,10 @@ int main(int argc, char *argv[]) {
} }
} // end while ! zm_terminate and connected } // end while ! zm_terminate and connected
for (std::unique_ptr<AnalysisThread> &analysis_thread: analysis_threads)
analysis_thread->Stop();
for (size_t i = 0; i < monitors.size(); i++) { for (size_t i = 0; i < monitors.size(); i++) {
monitors[i]->Close(); monitors[i]->Close();
} }
// Killoff the analysis threads. Don't need them spinning while we try to reconnect
analysis_threads.clear();
delete [] alarm_capture_delays; delete [] alarm_capture_delays;
delete [] capture_delays; delete [] capture_delays;
delete [] last_capture_times; delete [] last_capture_times;
@ -385,7 +372,7 @@ int main(int argc, char *argv[]) {
for (std::shared_ptr<Monitor> &monitor : monitors) { for (std::shared_ptr<Monitor> &monitor : monitors) {
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='NotRunning'", "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
monitor->Id()); monitor->Id());
zmDbDo(sql); zmDbDo(sql);
} }

View File

@ -241,8 +241,9 @@ int main(int argc, const char *argv[], char **envp) {
time_t now = time(nullptr); time_t now = time(nullptr);
char date_string[64]; char date_string[64];
tm now_tm = {};
strftime(date_string, sizeof(date_string)-1, strftime(date_string, sizeof(date_string)-1,
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); "%a, %d %b %Y %H:%M:%S GMT", gmtime_r(&now, &now_tm));
fputs("Last-Modified: ", stdout); fputs("Last-Modified: ", stdout);
fputs(date_string, stdout); fputs(date_string, stdout);

View File

@ -506,8 +506,10 @@ int main(int argc, char *argv[]) {
struct timeval timestamp = monitor->GetTimestamp(image_idx); struct timeval timestamp = monitor->GetTimestamp(image_idx);
if ( verbose ) { if ( verbose ) {
char timestamp_str[64] = "None"; char timestamp_str[64] = "None";
if ( timestamp.tv_sec ) if ( timestamp.tv_sec ) {
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime(&timestamp.tv_sec)); tm tm_info = {};
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime_r(&timestamp.tv_sec, &tm_info));
}
if ( image_idx == -1 ) if ( image_idx == -1 )
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000); printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
else else

View File

@ -117,6 +117,17 @@ commonprep () {
exit 1 exit 1
fi fi
fi fi
if [ -e "build/RtspServer" ]; then
echo "Found existing RtspServer..."
else
echo "Cloning RtspServer ..."
git clone https://github.com/ZoneMinder/RtspServer build/RtspServer
if [ $? -ne 0 ]; then
echo "ERROR: RtspServer clone failed..."
exit 1
fi
fi
} }
# Uncompress the submodule tarballs and move them into place # Uncompress the submodule tarballs and move them into place
@ -137,6 +148,13 @@ movecrud () {
rmdir web/api/app/Plugin/CakePHP-Enum-Behavior rmdir web/api/app/Plugin/CakePHP-Enum-Behavior
mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior
fi fi
if [ -e "dep/RtspServer/CMakeLists.txt" ]; then
echo "RtspServer already installed..."
else
echo "Copying RtspServer..."
rm -r dep/RtspServer
cp -Rpd build/RtspServer dep/RtspServer
fi
} }
# previsouly part of installzm.sh # previsouly part of installzm.sh
@ -347,7 +365,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
setdebpkgname setdebpkgname
movecrud movecrud
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "buster" ]; then if [ "${DIST}" == "focal" ] || [ "${DIST}" == "groovy" ] || [ "${DIST}" == "hirsuit" ] || [ "${DIST}" == "buster" ]; then
ln -sfT distros/ubuntu2004 debian ln -sfT distros/ubuntu2004 debian
elif [ "${DIST}" == "beowulf" ]; then elif [ "${DIST}" == "beowulf" ]; then
ln -sfT distros/beowulf debian ln -sfT distros/beowulf debian

View File

@ -344,7 +344,7 @@ class MonitorsController extends AppController {
public function daemonControl($id, $command, $daemon=null) { public function daemonControl($id, $command, $daemon=null) {
// Need to see if it is local or remote // Need to see if it is local or remote
$monitor = $this->Monitor->find('first', array( $monitor = $this->Monitor->find('first', array(
'fields' => array('Type', 'Function', 'Device', 'ServerId'), 'fields' => array('Id', 'Type', 'Function', 'Device', 'ServerId'),
'conditions' => array('Id' => $id) 'conditions' => array('Id' => $id)
)); ));
$monitor = $monitor['Monitor']; $monitor = $monitor['Monitor'];

View File

@ -83,7 +83,7 @@ class Monitor extends ZM_Object {
'LabelX' => 0, 'LabelX' => 0,
'LabelY' => 0, 'LabelY' => 0,
'LabelSize' => 1, 'LabelSize' => 1,
'ImageBufferCount' => 20, 'ImageBufferCount' => 3,
'WarmupCount' => 0, 'WarmupCount' => 0,
'PreEventCount' => 5, 'PreEventCount' => 5,
'PostEventCount' => 5, 'PostEventCount' => 5,

View File

@ -5,6 +5,7 @@ require_once('database.php');
$object_cache = array(); $object_cache = array();
class ZM_Object { class ZM_Object {
protected $_last_error;
public function __construct($IdOrRow = NULL) { public function __construct($IdOrRow = NULL) {
$class = get_class($this); $class = get_class($this);
@ -38,6 +39,7 @@ class ZM_Object {
public function __call($fn, array $args){ public function __call($fn, array $args){
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar'; $type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
if ( count($args) ) { if ( count($args) ) {
if ( $type == 'set' and is_array($args[0]) ) { if ( $type == 'set' and is_array($args[0]) ) {
$this->{$fn} = implode(',', $args[0]); $this->{$fn} = implode(',', $args[0]);
@ -50,7 +52,15 @@ class ZM_Object {
$this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]); $this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]);
} }
} else { } else {
$this->{$fn} = $args[0]; if ( $args[0] == '' and array_key_exists($fn, $this->defaults) ) {
if ( is_array($this->defaults[$fn]) ) {
$this->{$fn} = $this->defaults[$fn]['default'];
} else {
$this->{$fn} = $this->defaults[$fn];
}
} else {
$this->{$fn} = $args[0];
}
} }
} }
@ -174,7 +184,7 @@ class ZM_Object {
$this->$field($value); $this->$field($value);
} else { } else {
if ( is_array($value) ) { if ( is_array($value) ) {
# perhaps should turn into a comma-separated string # perhaps should turn into a comma-separated string
$this->{$field} = implode(',', $value); $this->{$field} = implode(',', $value);
} else if ( is_string($value) ) { } else if ( is_string($value) ) {
if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) { if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) {
@ -185,8 +195,14 @@ class ZM_Object {
} else { } else {
$this->{$field} = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value)); $this->{$field} = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
} }
} else if ( $value == '' and array_key_exists($field, $this->defaults) ) {
if ( is_array($this->defaults[$field]) ) {
$this->{$field} = $this->defaults[$field]['default'];
} else {
$this->{$field} = $this->defaults[$field];
}
} else { } else {
$this->{$field} = trim($value); $this->{$field} = $value;
} }
} else if ( is_integer($value) ) { } else if ( is_integer($value) ) {
$this->{$field} = $value; $this->{$field} = $value;
@ -207,8 +223,8 @@ class ZM_Object {
public function changes($new_values, $defaults=null) { public function changes($new_values, $defaults=null) {
$changes = array(); $changes = array();
if ( $defaults ) { if ($defaults) {
foreach ( $defaults as $field => $type ) { foreach ($defaults as $field => $type) {
if ( isset($new_values[$field]) ) { if ( isset($new_values[$field]) ) {
# Will have already been handled above # Will have already been handled above
continue; continue;
@ -247,47 +263,40 @@ class ZM_Object {
} else if ( $this->$field() != $value ) { } else if ( $this->$field() != $value ) {
$changes[$field] = $value; $changes[$field] = $value;
} }
} else if ( property_exists($this, $field) ) { } else if (property_exists($this, $field)) {
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar'; $type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
if ( $type == 'set' ) { if ($type == 'set') {
$old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array()); $old_value = is_array($this->$field) ? $this->$field : ($this->$field ? explode(',', $this->$field) : array());
$new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array()); $new_value = is_array($value) ? $value : ($value ? explode(',', $value) : array());
$diff = array_recursive_diff($old_value, $new_value); $diff = array_recursive_diff($old_value, $new_value);
if ( count($diff) ) { if (count($diff)) $changes[$field] = $new_value;
$changes[$field] = $new_value;
}
# Input might be a command separated string, or an array # Input might be a command separated string, or an array
} else { } else {
if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) { if (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp'])) {
if ( is_array($this->defaults[$field]['filter_regexp']) ) { if (is_array($this->defaults[$field]['filter_regexp'])) {
foreach ( $this->defaults[$field]['filter_regexp'] as $regexp ) { foreach ($this->defaults[$field]['filter_regexp'] as $regexp) {
$value = preg_replace($regexp, '', trim($value)); $value = preg_replace($regexp, '', trim($value));
} }
} else { } else {
$value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value)); $value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value));
} }
} }
if ( $this->{$field} != $value ) { if ($this->{$field} != $value) $changes[$field] = $value;
$changes[$field] = $value;
}
} }
} else if ( array_key_exists($field, $this->defaults) ) { } else if (array_key_exists($field, $this->defaults)) {
if ( is_array($this->defaults[$field]) and isset($this->defaults[$field]['default']) ) { if (is_array($this->defaults[$field]) and isset($this->defaults[$field]['default'])) {
$default = $this->defaults[$field]['default']; $default = $this->defaults[$field]['default'];
} else { } else {
$default = $this->defaults[$field]; $default = $this->defaults[$field];
} }
if ( $default != $value ) { if ($default != $value) $changes[$field] = $value;
$changes[$field] = $value;
}
} }
} # end foreach newvalue } # end foreach newvalue
return $changes; return $changes;
} # end public function changes } # end public function changes
@ -302,14 +311,20 @@ class ZM_Object {
# Set defaults. Note that we only replace "" with null, not other values # Set defaults. Note that we only replace "" with null, not other values
# because for example if we want to clear TimestampFormat, we clear it, but the default is a string value # because for example if we want to clear TimestampFormat, we clear it, but the default is a string value
foreach ( $this->defaults as $field => $default ) { foreach ( $this->defaults as $field => $default ) {
if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) { if (!property_exists($this, $field)) {
if ( is_array($default) ) { if (is_array($default)) {
$this->{$field} = $default['default']; $this->{$field} = $default['default'];
} else if ( $default == null ) { } else {
$this->{$field} = $default;
}
} else if ($this->{$field} === '') {
if (is_array($default)) {
$this->{$field} = $default['default'];
} else if ($default == null) {
$this->{$field} = $default; $this->{$field} = $default;
} }
} }
} } # end foreach default
$fields = array_filter( $fields = array_filter(
$this->defaults, $this->defaults,
@ -329,8 +344,7 @@ class ZM_Object {
$sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?'; $sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?';
$values = array_map(function($field){ return $this->{$field};}, $fields); $values = array_map(function($field){ return $this->{$field};}, $fields);
$values[] = $this->{'Id'}; $values[] = $this->{'Id'};
if ( dbQuery($sql, $values) ) if (dbQuery($sql, $values)) return true;
return true;
} else { } else {
unset($fields['Id']); unset($fields['Id']);
@ -339,15 +353,16 @@ class ZM_Object {
') VALUES ('. ') VALUES ('.
implode(', ', array_map(function($field){return $this->$field() == 'NOW()' ? 'NOW()' : '?';}, $fields)).')'; implode(', ', array_map(function($field){return $this->$field() == 'NOW()' ? 'NOW()' : '?';}, $fields)).')';
$values = array_values(array_map( # For some reason comparing 0 to 'NOW()' returns false; So we do this.
function($field){return $this->$field();}, $filtered = array_filter($fields, function($field){ return ( (!$this->$field()) or ($this->$field() != 'NOW()'));});
array_filter($fields, function($field){ return $this->$field() != 'NOW()';}) $mapped = array_map(function($field){return $this->$field();}, $filtered);
)); $values = array_values($mapped);
if ( dbQuery($sql, $values) ) { if (dbQuery($sql, $values)) {
$this->{'Id'} = dbInsertId(); $this->{'Id'} = dbInsertId();
return true; return true;
} }
} }
$this->_last_error = dbError($sql);
return false; return false;
} // end function save } // end function save
@ -421,5 +436,8 @@ class ZM_Object {
public function remove_from_cache() { public function remove_from_cache() {
return ZM_Object::_remove_from_cache(get_class(), $this); return ZM_Object::_remove_from_cache(get_class(), $this);
} }
public function get_last_error() {
return $this->_last_error;
}
} # end class Object } # end class Object
?> ?>

View File

@ -40,10 +40,12 @@ if ( $action == 'function' ) {
$newFunction = validStr($_REQUEST['newFunction']); $newFunction = validStr($_REQUEST['newFunction']);
# Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0 # Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0
$newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1'; $newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
$newDecodingEnabled = ( !isset($_REQUEST['newDecodingEnabled']) or $_REQUEST['newDecodingEnabled'] != '1' ) ? '0' : '1';
$oldFunction = $monitor->Function(); $oldFunction = $monitor->Function();
$oldEnabled = $monitor->Enabled(); $oldEnabled = $monitor->Enabled();
if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) { $oldDecodingEnabled = $monitor->DecodingEnabled();
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled)); if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled || $newDecodingEnabled != $oldDecodingEnabled ) {
$monitor->save(array('Function'=>$newFunction, 'Enabled'=>$newEnabled, 'DecodingEnabled'=>$newDecodingEnabled));
if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) { if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) {
$monitor->zmcControl(($newFunction != 'None') ? 'restart' : 'stop'); $monitor->zmcControl(($newFunction != 'None') ? 'restart' : 'stop');

View File

@ -18,77 +18,72 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
if ( $action == 'Save' ) { global $error_message;
if ( canEdit('System') ) {
if ( !empty($_REQUEST['uid']) ) {
$dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid']));
} else {
$dbUser = array();
}
$types = array(); if ($action == 'Save') {
if ( isset($_REQUEST['newUser']['MonitorIds']) and is_array($_REQUEST['newUser']['MonitorIds']) ) require_once('includes/User.php');
$uid = isset($_REQUEST['uid']) ? validInt($_REQUEST['uid']) : 0;
$dbUser = new ZM\User($uid);
if (canEdit('System')) {
# Need to check for uniqueness of Username
$user_with_my_username = ZM\User::find_one(array('Username'=>$_REQUEST['newUser']['Username']));
if ($user_with_my_username and
( ( $uid and ($user_with_my_username->Id() != $uid) ) or !$uid)
) {
$error_message = 'There already exists a user with this Username<br/>';
unset($_REQUEST['redirect']);
return;
}
# What other tests should we do?
if (isset($_REQUEST['newUser']['MonitorIds']) and is_array($_REQUEST['newUser']['MonitorIds']))
$_REQUEST['newUser']['MonitorIds'] = implode(',', $_REQUEST['newUser']['MonitorIds']); $_REQUEST['newUser']['MonitorIds'] = implode(',', $_REQUEST['newUser']['MonitorIds']);
if ( !$_REQUEST['newUser']['Password'] ) if (!empty($_REQUEST['newUser']['Password'])) {
$_REQUEST['newUser']['Password'] = password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT);
} else {
unset($_REQUEST['newUser']['Password']); unset($_REQUEST['newUser']['Password']);
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( isset($_REQUEST['newUser']['Password']) ) {
if ( function_exists('password_hash') ) {
$pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"';
} else {
$pass_hash = ' PASSWORD('.dbEscape($_REQUEST['newUser']['Password']).') ';
ZM\Info('Cannot use bcrypt as you are using PHP < 5.3');
}
if ( $_REQUEST['newUser']['Password'] ) {
$changes['Password'] = 'Password = '.$pass_hash;
} else {
unset($changes['Password']);
}
} }
$changes = $dbUser->changes($_REQUEST['newUser']);
ZM\Debug("Changes: " . print_r($changes, true));
if ( count($changes) ) { if (count($changes)) {
if ( !empty($_REQUEST['uid']) ) { if (!$dbUser->save($changes)) {
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid'])); $error_message = $dbUser->get_last_error();
# If we are updating the logged in user, then update our session user data. unset($_REQUEST['redirect']);
if ( $user and ( $dbUser['Username'] == $user['Username'] ) ) { return;
}
if ($uid) {
if ($user and ($dbUser->Username() == $user['Username'])) {
# We are the logged in user, need to update the $user object and generate a new auth_hash # We are the logged in user, need to update the $user object and generate a new auth_hash
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?'; $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?';
$user = dbFetchOne($sql, NULL, array($_REQUEST['uid'])); $user = dbFetchOne($sql, NULL, array($uid));
# Have to update auth hash in session # Have to update auth hash in session
zm_session_start(); zm_session_start();
generateAuthHash(ZM_AUTH_HASH_IPS, true); generateAuthHash(ZM_AUTH_HASH_IPS, true);
session_write_close(); session_write_close();
} }
} else {
dbQuery('INSERT INTO Users SET '.implode(', ', $changes));
} }
} # end if changes } # end if changes
} else if ( ZM_USER_SELF_EDIT and ( $_REQUEST['uid'] == $user['Id'] ) ) { } else if (ZM_USER_SELF_EDIT and ($uid == $user['Id'])) {
$uid = $user['Id']; if (!empty($_REQUEST['newUser']['Password'])) {
$_REQUEST['newUser']['Password'] = password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT);
$dbUser = dbFetchOne('SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid));
$types = array();
$changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types);
if ( function_exists('password_hash') ) {
$pass_hash = '"'.password_hash($_REQUEST['newUser']['Password'], PASSWORD_BCRYPT).'"';
} else { } else {
$pass_hash = ' PASSWORD('.dbEscape($_REQUEST['newUser']['Password']).') '; unset($_REQUEST['newUser']['Password']);
ZM\Info ('Cannot use bcrypt as you are using PHP < 5.3');
} }
$fields = array('Password'=>'', 'Language'=>'', 'HomeView'=>'');
ZM\Debug("changes: ".print_r(array_intersect_key($_REQUEST['newUser'], $fields),true));
$changes = $dbUser->changes(array_intersect_key($_REQUEST['newUser'], $fields));
ZM\Debug("changes: ".print_r($changes, true));
if ( !empty($_REQUEST['newUser']['Password']) ) { if (count($changes)) {
$changes['Password'] = 'Password = '.$pass_hash; if (!$dbUser->save($changes)) {
} else { $error_message = $dbUser->get_last_error();
unset($changes['Password']); unset($_REQUEST['redirect']);
} return;
if ( count($changes) ) { }
dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid));
# We are the logged in user, need to update the $user object and generate a new auth_hash # We are the logged in user, need to update the $user object and generate a new auth_hash
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?'; $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Id=?';

View File

@ -98,7 +98,7 @@ if ( isset($_GET['skin']) ) {
$skin = 'classic'; $skin = 'classic';
} }
if ( ! is_dir("skins/$skin") ) { if (!is_dir('skins/'.$skin) ) {
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR)); $skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
if ( !in_array($skin, $skins) ) { if ( !in_array($skin, $skins) ) {
@ -117,10 +117,10 @@ if ( isset($_GET['css']) ) {
$css = 'classic'; $css = 'classic';
} }
if ( !is_dir("skins/$skin/css/$css") ) { if (!is_dir("skins/$skin/css/$css")) {
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)); $css_skins = array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR));
if ( count($css_skins) ) { if (count($css_skins)) {
if ( !in_array($css, $css_skins) ) { if (!in_array($css, $css_skins)) {
ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]); ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]);
$css = $css_skins[0]; $css = $css_skins[0];
} else { } else {
@ -137,7 +137,7 @@ define('ZM_SKIN_PATH', "skins/$skin");
define('ZM_SKIN_NAME', $skin); define('ZM_SKIN_NAME', $skin);
$skinBase = array(); // To allow for inheritance of skins $skinBase = array(); // To allow for inheritance of skins
if ( !file_exists(ZM_SKIN_PATH) ) if (!file_exists(ZM_SKIN_PATH))
ZM\Fatal("Invalid skin '$skin'"); ZM\Fatal("Invalid skin '$skin'");
$skinBase[] = $skin; $skinBase[] = $skin;
@ -183,9 +183,6 @@ $user = null;
if ( isset($_REQUEST['view']) ) if ( isset($_REQUEST['view']) )
$view = detaintPath($_REQUEST['view']); $view = detaintPath($_REQUEST['view']);
if ( isset($_REQUEST['redirect']) )
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
# Add CSP Headers # Add CSP Headers
$cspNonce = bin2hex(zm_random_bytes(16)); $cspNonce = bin2hex(zm_random_bytes(16));
@ -265,6 +262,8 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n
$request = null; $request = null;
} }
if ( isset($_REQUEST['redirect']) )
$redirect = '?view='.detaintPath($_REQUEST['redirect']);
if ( $redirect ) { if ( $redirect ) {
ZM\Debug("Redirecting to $redirect"); ZM\Debug("Redirecting to $redirect");

View File

@ -16,3 +16,14 @@ tr.log-dbg td {
font-style: italic; font-style: italic;
} }
th[data-field="DateTime"],
th[data-field="Component"],
th[data-field="Code"],
th[data-field="Message"],
th[data-field="File"],
th[data-field="Line"] {
text-align: left;
}
th[data-field="DateTime"] {
min-width: 135px;
}

View File

@ -58,8 +58,8 @@ function processRows(rows) {
var emailed = row.Emailed == yesString ? emailedString : ''; var emailed = row.Emailed == yesString ? emailedString : '';
row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>'; row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>' row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + sortQuery + '&amp;page=1">' + row.Name + '</a>' +
+ '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>'; '<br/><div class="small text-nowrap text-muted">' + archived + emailed + '</div>';
if ( canEdit.Monitors ) row.Monitor = '<a href="?view=event&amp;eid=' + eid + '">' + row.Monitor + '</a>'; if ( canEdit.Monitors ) row.Monitor = '<a href="?view=event&amp;eid=' + eid + '">' + row.Monitor + '</a>';
if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>'; if ( canEdit.Events ) row.Cause = '<a href="#" title="' + row.Notes + '" class="eDetailLink" data-eid="' + eid + '">' + row.Cause + '</a>';
if ( row.Notes.indexOf('detected:') >= 0 ) { if ( row.Notes.indexOf('detected:') >= 0 ) {

View File

@ -40,10 +40,8 @@ function validateForm(form) {
var have_endtime_term = false; var have_endtime_term = false;
for ( var i = 0; i < rows.length; i++ ) { for ( var i = 0; i < rows.length; i++ ) {
if ( if (
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDateTime' ) ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDateTime' ) ||
|| ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndTime' ) ||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndTime' )
||
( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDate' ) ( form.elements['filter[Query][terms][' + i + '][attr]'].value == 'EndDate' )
) { ) {
have_endtime_term = true; have_endtime_term = true;

View File

@ -48,6 +48,7 @@ function updateMonitorDimensions(element) {
form.elements['newMonitor[Height]'].value = dimensions[1]; form.elements['newMonitor[Height]'].value = dimensions[1];
} }
} }
update_estimated_ram_use();
return false; return false;
} }

View File

@ -271,7 +271,9 @@ function reloadWebSite(ndx) {
} }
function takeSnapshot() { function takeSnapshot() {
monitor_ids = monitorData.map( monitor=> { return 'monitor_ids[]='+monitor.id; }); monitor_ids = monitorData.map((monitor)=>{
return 'monitor_ids[]='+monitor.id;
});
window.location = '?view=snapshot&action=create&'+monitor_ids.join('&'); window.location = '?view=snapshot&action=create&'+monitor_ids.join('&');
} }

View File

@ -27,7 +27,6 @@ function manageDelConfirmModalBtns() {
} }
function initPage() { function initPage() {
// enable or disable buttons based on current selection and user rights // enable or disable buttons based on current selection and user rights
/* /*
renameBtn.prop('disabled', !canEdit.Events); renameBtn.prop('disabled', !canEdit.Events);

View File

@ -48,7 +48,6 @@ function ajaxRequest(params) {
function processRows(rows) { function processRows(rows) {
$j.each(rows, function(ndx, row) { $j.each(rows, function(ndx, row) {
var id = row.Id; var id = row.Id;
row.Id = '<a href="?view=snapshot&amp;id=' + id + '">' + id + '</a>'; row.Id = '<a href="?view=snapshot&amp;id=' + id + '">' + id + '</a>';
row.Name = '<a href="?view=snapshot&amp;id=' + id + '">' + row.Name + '</a>'; row.Name = '<a href="?view=snapshot&amp;id=' + id + '">' + row.Name + '</a>';
@ -192,7 +191,7 @@ function initPage() {
window.location.reload(true); window.location.reload(true);
}); });
/* /*
// Manage the ARCHIVE button // Manage the ARCHIVE button
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) { document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
var selections = getIdSelections(); var selections = getIdSelections();
@ -268,7 +267,7 @@ function initPage() {
}) })
.fail(logAjaxFail); .fail(logAjaxFail);
}); });
*/ */
// Manage the DELETE button // Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) { document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEdit.Events ) { if ( ! canEdit.Events ) {

View File

@ -202,7 +202,7 @@ function toPercent(field, maxValue) {
field.value = 100; field.value = 100;
} }
} }
field.setAttribute('step', 0.01); field.setAttribute('step', 'any');
field.setAttribute('max', 100); field.setAttribute('max', 100);
} }

View File

@ -37,16 +37,14 @@ $mid = null;
$monitor = null; $monitor = null;
if ( !empty($_REQUEST['mid']) ) { if ( !empty($_REQUEST['mid']) ) {
$mid = validInt($_REQUEST['mid']); $mid = validInt($_REQUEST['mid']);
$monitor = new ZM\Monitor($mid); $monitor = new ZM\Monitor($mid);
if ( $monitor and ZM_OPT_X10 ) if ( $monitor and ZM_OPT_X10 )
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($mid)); $x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($mid));
} }
if ( !$monitor ) { if ( !$monitor ) {
$nextId = getTableAutoInc('Monitors');
$monitor = new ZM\Monitor(); $monitor = new ZM\Monitor();
$monitor->Name(translate('Monitor').'-'.$nextId); $monitor->Name(translate('Monitor').'-'.getTableAutoInc('Monitors'));
$monitor->WebColour(random_colour()); $monitor->WebColour(random_colour());
} # end if $_REQUEST['mid'] } # end if $_REQUEST['mid']

View File

@ -156,7 +156,6 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*', GLOB_ONLYDIR)) as
$userMonitors[] = $monitors[$monitorId]['Name']; $userMonitors[] = $monitors[$monitorId]['Name'];
} }
} }
ZM\Debug("monitors: ".$user_row['Username'] . ' ' . $user_row['MonitorIds']. ' :' . print_r($userMonitors, true));
?> ?>
<tr> <tr>
<td class="colUsername"><?php echo makeLink('?view=user&amp;uid='.$user_row['Id'], validHtmlStr($user_row['Username']).($user['Username']==$user_row['Username']?'*':''), $canEdit) ?></td> <td class="colUsername"><?php echo makeLink('?view=user&amp;uid='.$user_row['Id'], validHtmlStr($user_row['Username']).($user['Username']==$user_row['Username']?'*':''), $canEdit) ?></td>

View File

@ -25,7 +25,7 @@ if ( !canEdit('System') && !$selfEdit ) {
return; return;
} }
require('includes/User.php'); require_once('includes/User.php');
if ( $_REQUEST['uid'] ) { if ( $_REQUEST['uid'] ) {
if ( !($newUser = new ZM\User($_REQUEST['uid'])) ) { if ( !($newUser = new ZM\User($_REQUEST['uid'])) ) {
@ -54,9 +54,9 @@ foreach ( dbFetchAll($sql) as $monitor ) {
$focusWindow = true; $focusWindow = true;
xhtmlHeaders(__FILE__, translate('User').' - '.$newUser->Username()); xhtmlHeaders(__FILE__, translate('User').' - '.$newUser->Username());
echo getBodyTopHTML();
echo getNavBarHTML();
?> ?>
<body>
<?php echo getNavBarHTML() ?>
<div id="page"> <div id="page">
<div class="w-100"> <div class="w-100">
<div class="float-left pl-3 pt-1"> <div class="float-left pl-3 pt-1">

View File

@ -206,7 +206,7 @@ if ( count($other_zones) ) {
array('data-on-change'=>'applyZoneUnits', 'id'=>'newZone[Units]') array('data-on-change'=>'applyZoneUnits', 'id'=>'newZone[Units]')
); );
# Used later for number inputs # Used later for number inputs
$step = $newZone['Units'] == 'Percent' ? ' step="0.01" max="100"' : ''; $step = $newZone['Units'] == 'Percent' ? ' step="any" max="100"' : '';
?> ?>
</td> </td>
</tr> </tr>