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

This commit is contained in:
Isaac Connor 2021-02-25 14:58:45 -05:00
commit 224fcd2cd3
40 changed files with 735 additions and 680 deletions

View File

@ -22,6 +22,7 @@
#include "zm_exception.h" #include "zm_exception.h"
#include "zm_logger.h" #include "zm_logger.h"
#include <cerrno>
#include <netdb.h> #include <netdb.h>
#include <set> #include <set>
#include <sys/uio.h> #include <sys/uio.h>

View File

@ -22,6 +22,7 @@
#include "zm_db.h" #include "zm_db.h"
#include "zm_logger.h" #include "zm_logger.h"
#include "zm_utils.h" #include "zm_utils.h"
#include <cerrno>
#include <cstring> #include <cstring>
#include <dirent.h> #include <dirent.h>
#include <glob.h> #include <glob.h>

View File

@ -19,10 +19,12 @@
#include "zm_db.h" #include "zm_db.h"
#include "zm_logger.h" #include "zm_logger.h"
#include "zm_signal.h"
#include <cstdlib> #include <cstdlib>
MYSQL dbconn; MYSQL dbconn;
RecursiveMutex db_mutex; std::mutex db_mutex;
zmDbQueue dbQueue;
bool zmDbConnected = false; bool zmDbConnected = false;
@ -106,46 +108,36 @@ bool zmDbConnect() {
void zmDbClose() { void zmDbClose() {
if (zmDbConnected) { if (zmDbConnected) {
db_mutex.lock(); std::lock_guard<std::mutex> lck(db_mutex);
mysql_close(&dbconn); mysql_close(&dbconn);
// mysql_init() call implicitly mysql_library_init() but // mysql_init() call implicitly mysql_library_init() but
// mysql_close() does not call mysql_library_end() // mysql_close() does not call mysql_library_end()
mysql_library_end(); mysql_library_end();
zmDbConnected = false; zmDbConnected = false;
db_mutex.unlock();
} }
} }
MYSQL_RES * zmDbFetch(const char * query) { MYSQL_RES * zmDbFetch(const char * query) {
if ( !zmDbConnected ) { std::lock_guard<std::mutex> lck(db_mutex);
Error("Not connected."); if (!zmDbConnected) {
return nullptr;
}
db_mutex.lock();
// Might have been disconnected while we waited for the lock
if ( !zmDbConnected ) {
db_mutex.unlock();
Error("Not connected."); Error("Not connected.");
return nullptr; return nullptr;
} }
if ( mysql_query(&dbconn, query) ) { if (mysql_query(&dbconn, query)) {
db_mutex.unlock();
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
return nullptr; return nullptr;
} }
Debug(4, "Success running query: %s", query);
MYSQL_RES *result = mysql_store_result(&dbconn); MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) { if (!result) {
Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query); Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query);
} }
db_mutex.unlock();
return result; return result;
} // end MYSQL_RES * zmDbFetch(const char * query); } // end MYSQL_RES * zmDbFetch(const char * query);
zmDbRow *zmDbFetchOne(const char *query) { zmDbRow *zmDbFetchOne(const char *query) {
zmDbRow *row = new zmDbRow(); zmDbRow *row = new zmDbRow();
if ( row->fetch(query) ) { if (row->fetch(query)) {
return row; return row;
} }
delete row; delete row;
@ -154,10 +146,10 @@ zmDbRow *zmDbFetchOne(const char *query) {
MYSQL_RES *zmDbRow::fetch(const char *query) { MYSQL_RES *zmDbRow::fetch(const char *query) {
result_set = zmDbFetch(query); result_set = zmDbFetch(query);
if ( ! result_set ) return result_set; if (!result_set) return result_set;
int n_rows = mysql_num_rows(result_set); int n_rows = mysql_num_rows(result_set);
if ( n_rows != 1 ) { if (n_rows != 1) {
Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query); Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query);
mysql_free_result(result_set); mysql_free_result(result_set);
result_set = nullptr; result_set = nullptr;
@ -165,7 +157,7 @@ MYSQL_RES *zmDbRow::fetch(const char *query) {
} }
row = mysql_fetch_row(result_set); row = mysql_fetch_row(result_set);
if ( !row ) { if (!row) {
mysql_free_result(result_set); mysql_free_result(result_set);
result_set = nullptr; result_set = nullptr;
Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn)); Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn));
@ -176,40 +168,69 @@ MYSQL_RES *zmDbRow::fetch(const char *query) {
} }
int zmDbDo(const char *query) { int zmDbDo(const char *query) {
db_mutex.lock(); std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected)
return 0;
int rc; int rc;
while ( rc = mysql_query(&dbconn, query) ) { while ((rc = mysql_query(&dbconn, query)) and !zm_terminate) {
db_mutex.unlock();
Error("Can't run query %s: %s", query, mysql_error(&dbconn)); Error("Can't run query %s: %s", query, mysql_error(&dbconn));
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) {
return rc; return rc;
db_mutex.lock();
} }
db_mutex.unlock(); }
return 1; return 1;
} }
int zmDbDoInsert(const char *query) { int zmDbDoInsert(const char *query) {
db_mutex.lock(); std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) return 0;
int rc; int rc;
while ( rc = mysql_query(&dbconn, query) ) { while ( (rc = mysql_query(&dbconn, query)) and !zm_terminate) {
db_mutex.unlock();
Error("Can't run query %s: %s", query, mysql_error(&dbconn)); Error("Can't run query %s: %s", query, mysql_error(&dbconn));
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) )
return 0; return 0;
db_mutex.lock();
} }
int id = mysql_insert_id(&dbconn); int id = mysql_insert_id(&dbconn);
db_mutex.unlock();
return id; return id;
} }
zmDbRow::~zmDbRow() { zmDbRow::~zmDbRow() {
if ( result_set ) { if (result_set) {
mysql_free_result(result_set); mysql_free_result(result_set);
result_set = nullptr; result_set = nullptr;
} }
row = nullptr; row = nullptr;
} }
zmDbQueue::zmDbQueue() :
mThread(&zmDbQueue::process, this),
mTerminate(false)
{ }
zmDbQueue::~zmDbQueue() {
mTerminate = true;
mCondition.notify_all();
mThread.join();
}
void zmDbQueue::process() {
std::unique_lock<std::mutex> lock(mMutex);
while (!mTerminate and !zm_terminate) {
if (mQueue.empty()) {
mCondition.wait(lock);
}
if (!mQueue.empty()) {
std::string sql = mQueue.front();
mQueue.pop();
lock.unlock();
zmDbDo(sql.c_str());
lock.lock();
}
}
} // end void zmDbQueue::process()
void zmDbQueue::push(std::string sql) {
std::unique_lock<std::mutex> lock(mMutex);
mQueue.push(sql);
mCondition.notify_all();
}

View File

@ -23,6 +23,26 @@
#include "zm_thread.h" #include "zm_thread.h"
#include <mysql/mysql.h> #include <mysql/mysql.h>
#include <mysql/mysqld_error.h> #include <mysql/mysqld_error.h>
#include <string>
#include <thread>
#include <queue>
#include <condition_variable>
#include <mutex>
class zmDbQueue {
private:
std::queue<std::string> mQueue;
std::thread mThread;
std::mutex mMutex;
std::condition_variable mCondition;
bool mTerminate;
public:
zmDbQueue();
~zmDbQueue();
void push(const char *sql) { return push(std::string(sql)); };
void push(std::string);
void process();
};
class zmDbRow { class zmDbRow {
private: private:
@ -42,7 +62,8 @@ class zmDbRow {
}; };
extern MYSQL dbconn; extern MYSQL dbconn;
extern RecursiveMutex db_mutex; extern std::mutex db_mutex;
extern zmDbQueue dbQueue;
extern bool zmDbConnected; extern bool zmDbConnected;
@ -51,7 +72,7 @@ void zmDbClose();
int zmDbDo(const char *query); int zmDbDo(const char *query);
int zmDbDoInsert(const char *query); int zmDbDoInsert(const char *query);
MYSQL_RES * zmDbFetch( const char *query ); MYSQL_RES * zmDbFetch(const char *query);
zmDbRow *zmDbFetchOne( const char *query ); zmDbRow *zmDbFetchOne(const char *query);
#endif // ZM_DB_H #endif // ZM_DB_H

View File

@ -251,12 +251,10 @@ Event::~Event() {
frames, alarm_frames, frames, alarm_frames,
tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score,
id); id);
db_mutex.lock(); { // scope for lock
std::lock_guard<std::mutex> lck(db_mutex);
while ( mysql_query(&dbconn, sql) && !zm_terminate ) { while ( mysql_query(&dbconn, sql) && !zm_terminate ) {
db_mutex.unlock();
Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn)); Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn));
sleep(1);
db_mutex.lock();
} }
if ( !mysql_affected_rows(&dbconn) ) { if ( !mysql_affected_rows(&dbconn) ) {
// Name might have been changed during recording, so just do the update without changing the name. // Name might have been changed during recording, so just do the update without changing the name.
@ -268,13 +266,10 @@ Event::~Event() {
tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score,
id); id);
while ( mysql_query(&dbconn, sql) && !zm_terminate ) { while ( mysql_query(&dbconn, sql) && !zm_terminate ) {
db_mutex.unlock();
Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn)); Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn));
sleep(1);
db_mutex.lock();
} }
} // end if no changed rows due to Name change during recording } // end if no changed rows due to Name change during recording
db_mutex.unlock(); }
} // Event::~Event() } // Event::~Event()
void Event::createNotes(std::string &notes) { void Event::createNotes(std::string &notes) {
@ -427,11 +422,7 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length()); mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
snprintf(sql, sizeof(sql), "UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64, escapedNotes, id); snprintf(sql, sizeof(sql), "UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64, escapedNotes, id);
db_mutex.lock(); zmDbDo(sql);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert event: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
#endif #endif
} // end if update } // end if update
} // void Event::updateNotes(const StringSetMap &newNoteSetMap) } // void Event::updateNotes(const StringSetMap &newNoteSetMap)
@ -495,14 +486,7 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
if ( frameCount ) { if ( frameCount ) {
*(frame_insert_values-1) = '\0'; *(frame_insert_values-1) = '\0';
db_mutex.lock(); zmDbDo(frame_insert_sql);
int rc = mysql_query(&dbconn, frame_insert_sql);
db_mutex.unlock();
if ( rc ) {
Error("Can't insert frames: %s, sql was (%s)", mysql_error(&dbconn), frame_insert_sql);
} else {
Debug(1, "INSERT %d/%d frames sql %s", frameCount, n_frames, frame_insert_sql);
}
last_db_frame = frames; last_db_frame = frames;
} else { } else {
Debug(1, "No valid pre-capture frames to add"); Debug(1, "No valid pre-capture frames to add");
@ -512,7 +496,9 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
void Event::AddPacket(ZMPacket *packet) { void Event::AddPacket(ZMPacket *packet) {
have_video_keyframe = have_video_keyframe || ( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) && packet->keyframe ); have_video_keyframe = have_video_keyframe ||
( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) &&
( packet->keyframe || monitor->GetOptVideoWriter() == Monitor::ENCODE) );
Debug(2, "have_video_keyframe %d codec_type %d == video? %d packet keyframe %d", Debug(2, "have_video_keyframe %d codec_type %d == video? %d packet keyframe %d",
have_video_keyframe, packet->codec_type, (packet->codec_type == AVMEDIA_TYPE_VIDEO), packet->keyframe); have_video_keyframe, packet->codec_type, (packet->codec_type == AVMEDIA_TYPE_VIDEO), packet->keyframe);
ZM_DUMP_PACKET(packet->packet, "Adding to event"); ZM_DUMP_PACKET(packet->packet, "Adding to event");
@ -552,38 +538,9 @@ void Event::WriteDbFrames() {
delete frame; delete frame;
} }
*(frame_insert_values_ptr-1) = '\0'; // The -1 is for the extra , added for values above *(frame_insert_values_ptr-1) = '\0'; // The -1 is for the extra , added for values above
db_mutex.lock(); zmDbDo(frame_insert_sql);
int rc = mysql_query(&dbconn, frame_insert_sql);
db_mutex.unlock();
if ( rc ) {
Error("Can't insert frames: %s, sql was %s", mysql_error(&dbconn), frame_insert_sql);
return;
} else {
Debug(1, "INSERT FRAMES: sql was %s", frame_insert_sql);
}
} // end void Event::WriteDbFrames() } // end void Event::WriteDbFrames()
// Subtract an offset time from frames deltas to match with video start time
void Event::UpdateFramesDelta(double offset) {
char sql[ZM_SQL_MED_BUFSIZ];
if ( offset == 0.0 ) return;
// the table is set to auto update timestamp so we force it to keep current value
snprintf(sql, sizeof(sql),
"UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64,
offset, id);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
db_mutex.unlock();
Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql);
return;
}
db_mutex.unlock();
Info("Updating frames delta by %0.2f sec to match video file", offset);
}
void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) { void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) {
if (!timestamp.tv_sec) { if (!timestamp.tv_sec) {
Warning("Not adding new frame, zero timestamp"); Warning("Not adding new frame, zero timestamp");
@ -686,14 +643,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
max_score, max_score,
id id
); );
db_mutex.lock(); zmDbDo(sql);
while (mysql_query(&dbconn, sql) && !zm_terminate) {
Error("Can't update event: %s", mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);
db_mutex.lock();
}
db_mutex.unlock();
} else { } else {
Debug(1, "Not Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK", Debug(1, "Not Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f or BULK",
frame_data.size(), write_to_db, fps); frame_data.size(), write_to_db, fps);

View File

@ -134,7 +134,6 @@ class Event {
Image **images, Image **images,
struct timeval **timestamps); struct timeval **timestamps);
void WriteDbFrames(); void WriteDbFrames();
void UpdateFramesDelta(double offset);
bool SetPath(Storage *storage); bool SetPath(Storage *storage);
public: public:

View File

@ -27,6 +27,9 @@
#include "zm_storage.h" #include "zm_storage.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef __FreeBSD__
#include <netinet/in.h>
#endif
const std::string EventStream::StreamMode_Strings[4] = { const std::string EventStream::StreamMode_Strings[4] = {
"None", "None",
@ -348,7 +351,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
curr_frame_id = 1; curr_frame_id = 1;
} else { } else {
Debug(1, "mode is %s, current frame is %ld, frame count is %ld, last frame id is %ld", Debug(1, "mode is %s, current frame is %ld, frame count is %ld, last frame id is %ld",
StreamMode_Strings[(int)mode], curr_frame_id, event_data->frame_count ); StreamMode_Strings[(int)mode].c_str(), curr_frame_id, event_data->frame_count );
} }
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;

View File

@ -90,7 +90,7 @@ class EventStream : public StreamBase {
bool loadInitialEventData(int monitor_id, time_t event_time); bool loadInitialEventData(int monitor_id, time_t event_time);
bool checkEventLoaded(); bool checkEventLoaded();
void processCommand(const CmdMsg *msg); void processCommand(const CmdMsg *msg) override;
bool sendFrame(int delta_us); bool sendFrame(int delta_us);
public: public:

View File

@ -64,11 +64,15 @@ void log_libav_callback(void *ptr, int level, const char *fmt, va_list vargs) {
if ( log ) { if ( log ) {
char logString[8192]; char logString[8192];
vsnprintf(logString, sizeof(logString)-1, fmt, vargs); int length = vsnprintf(logString, sizeof(logString)-1, fmt, vargs);
int length = strlen(logString); if ( length > 0 ) {
if ( static_cast<size_t>(length) > sizeof(logString)-1 ) length = sizeof(logString)-1;
// ffmpeg logs have a carriage return, so replace it with terminator // ffmpeg logs have a carriage return, so replace it with terminator
logString[length-1] = 0; logString[length-1] = 0;
log->logPrint(false, __FILE__, __LINE__, log_level, logString); log->logPrint(false, __FILE__, __LINE__, log_level, logString);
} else {
log->logPrint(false, __FILE__, __LINE__, AV_LOG_ERROR, "Can't encode log from av. fmt was %s", fmt);
}
} }
} }
@ -385,8 +389,9 @@ enum AVPixelFormat fix_deprecated_pix_fmt(enum AVPixelFormat fmt) {
return AV_PIX_FMT_YUV440P; return AV_PIX_FMT_YUV440P;
case AV_PIX_FMT_NONE : case AV_PIX_FMT_NONE :
case AV_PIX_FMT_YUVJ420P : case AV_PIX_FMT_YUVJ420P :
default:
return AV_PIX_FMT_YUV420P; return AV_PIX_FMT_YUV420P;
default:
return fmt;
} }
} }

View File

@ -369,10 +369,12 @@ void zm_dump_codecpar(const AVCodecParameters *par);
if (logDebugging()) { \ if (logDebugging()) { \
double pts_time = static_cast<double>(av_rescale_q(pkt.pts, stream->time_base, AV_TIME_BASE_Q)) / AV_TIME_BASE; \ double pts_time = static_cast<double>(av_rescale_q(pkt.pts, stream->time_base, AV_TIME_BASE_Q)) / AV_TIME_BASE; \
\ \
Debug(2, "%s: pts: %" PRId64 "=%f, dts: %" PRId64 \ Debug(2, "%s: pts: %" PRId64 " * %u/%u=%f, dts: %" PRId64 \
", size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64", duration: %" AV_PACKET_DURATION_FMT, \ ", size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64", duration: %" AV_PACKET_DURATION_FMT, \
text, \ text, \
pkt.pts, \ pkt.pts, \
stream->time_base.num, \
stream->time_base.den, \
pts_time, \ pts_time, \
pkt.dts, \ pkt.dts, \
pkt.size, \ pkt.size, \

View File

@ -44,7 +44,6 @@ int zmFifoDbgInit(Monitor * monitor);
class FifoStream : public StreamBase { class FifoStream : public StreamBase {
private: private:
char * stream_path; char * stream_path;
int fd;
int total_read; int total_read;
int bytes_read; int bytes_read;
unsigned int frame_count; unsigned int frame_count;
@ -59,12 +58,11 @@ class FifoStream : public StreamBase {
StreamType stream_type; StreamType stream_type;
bool sendMJEGFrames(); bool sendMJEGFrames();
bool sendRAWFrames(); bool sendRAWFrames();
void processCommand(const CmdMsg *msg) {} void processCommand(const CmdMsg *msg) override {}
public: public:
FifoStream() : FifoStream() :
stream_path(nullptr), stream_path(nullptr),
fd(0),
total_read(0), total_read(0),
bytes_read(0), bytes_read(0),
frame_count(0), frame_count(0),

View File

@ -276,13 +276,15 @@ void Image::Assign(const AVFrame *frame) {
AVFrame *dest_frame = zm_av_frame_alloc(); AVFrame *dest_frame = zm_av_frame_alloc();
PopulateFrame(dest_frame); PopulateFrame(dest_frame);
zm_dump_video_frame(frame, "source frame before convert"); zm_dump_video_frame(frame, "source frame before convert");
zm_dump_video_frame(dest_frame, "dest frame before convert"); dest_frame->pts = frame->pts;
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
sws_convert_context = sws_getCachedContext( sws_convert_context = sws_getCachedContext(
sws_convert_context, sws_convert_context,
frame->width, frame->height, (AVPixelFormat)frame->format, frame->width, frame->height, (AVPixelFormat)frame->format,
width, height, format, width, height, format,
SWS_BICUBIC, nullptr, nullptr, nullptr); //SWS_BICUBIC,
SWS_POINT | SWS_BITEXACT,
nullptr, nullptr, nullptr);
if ( sws_convert_context == nullptr ) if ( sws_convert_context == nullptr )
Fatal("Unable to create conversion context"); Fatal("Unable to create conversion context");

View File

@ -177,6 +177,10 @@ LibvlcCamera::~LibvlcCamera() {
(*libvlc_release_f)(mLibvlcInstance); (*libvlc_release_f)(mLibvlcInstance);
mLibvlcInstance = nullptr; mLibvlcInstance = nullptr;
} }
if (libvlc_lib) {
dlclose(libvlc_lib);
libvlc_lib = nullptr;
}
if ( mOptArgV != nullptr ) { if ( mOptArgV != nullptr ) {
delete[] mOptArgV; delete[] mOptArgV;
} }

View File

@ -18,11 +18,11 @@ static int (*WaitForMessage_f)(rfbClient*, unsigned int) = nullptr;
static rfbBool (*HandleRFBServerMessage_f)(rfbClient*) = nullptr; static rfbBool (*HandleRFBServerMessage_f)(rfbClient*) = nullptr;
void bind_libvnc_symbols() { void bind_libvnc_symbols() {
if ( libvnc_lib != nullptr ) // Safe-check if (libvnc_lib != nullptr) // Safe-check
return; return;
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL); libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
if ( !libvnc_lib ) { if (!libvnc_lib) {
Error("Error loading libvncclient: %s", dlerror()); Error("Error loading libvncclient: %s", dlerror());
return; return;
} }
@ -43,14 +43,14 @@ static void GotFrameBufferUpdateCallback(rfbClient *rfb, int x, int y, int w, in
x,y,w,h, rfb->width, rfb->height, rfb->frameBuffer); x,y,w,h, rfb->width, rfb->height, rfb->frameBuffer);
} }
static char* GetPasswordCallback(rfbClient* cl){ static char* GetPasswordCallback(rfbClient* cl) {
Debug(1, "Getcredentials: %s", (*rfbClientGetClientData_f)(cl, &TAG_1)); Debug(1, "Getcredentials: %s", (*rfbClientGetClientData_f)(cl, &TAG_1));
return strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); return strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1));
} }
static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){
rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential));
if ( credentialType != rfbCredentialTypeUser ) { if (credentialType != rfbCredentialTypeUser) {
free(c); free(c);
return nullptr; return nullptr;
} }
@ -61,6 +61,21 @@ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){
return c; return c;
} }
static rfbBool resize(rfbClient* client) {
if (client->frameBuffer) {
Debug(1, "Freeing old frame buffer");
av_free(client->frameBuffer);
}
int bufferSize = 4*client->width*client->height;
// libVNC doesn't do alignment or padding in each line
//SWScale::GetBufferSize(AV_PIX_FMT_RGBA, client->width, client->height);
client->frameBuffer = (uint8_t *)av_malloc(bufferSize);
Debug(1, "Allocing new frame buffer %dx%d = %d", client->width, client->height, bufferSize);
return TRUE;
}
VncCamera::VncCamera( VncCamera::VncCamera(
const Monitor *monitor, const Monitor *monitor,
const std::string &host, const std::string &host,
@ -96,22 +111,20 @@ VncCamera::VncCamera(
mUser(user), mUser(user),
mPass(pass) mPass(pass)
{ {
Debug(2, "Host:%s Port: %s User: %s Pass:%s", mHost.c_str(), mPort.c_str(), mUser.c_str(), mPass.c_str()); if (colours == ZM_COLOUR_RGB32) {
if ( colours == ZM_COLOUR_RGB32 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA; subpixelorder = ZM_SUBPIX_ORDER_RGBA;
mImgPixFmt = AV_PIX_FMT_RGBA; mImgPixFmt = AV_PIX_FMT_RGBA;
} else if ( colours == ZM_COLOUR_RGB24 ) { } else if (colours == ZM_COLOUR_RGB24) {
subpixelorder = ZM_SUBPIX_ORDER_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB;
mImgPixFmt = AV_PIX_FMT_RGB24; mImgPixFmt = AV_PIX_FMT_RGB24;
} else if ( colours == ZM_COLOUR_GRAY8 ) { } else if (colours == ZM_COLOUR_GRAY8) {
subpixelorder = ZM_SUBPIX_ORDER_NONE; subpixelorder = ZM_SUBPIX_ORDER_NONE;
mImgPixFmt = AV_PIX_FMT_GRAY8; mImgPixFmt = AV_PIX_FMT_GRAY8;
} else { } else {
Panic("Unexpected colours: %d", colours); Panic("Unexpected colours: %d", colours);
} }
if ( capture ) { if (capture) {
Debug(3, "Initializing Client"); Debug(3, "Initializing Client");
bind_libvnc_symbols(); bind_libvnc_symbols();
scale.init(); scale.init();
@ -119,11 +132,15 @@ VncCamera::VncCamera(
} }
VncCamera::~VncCamera() { VncCamera::~VncCamera() {
if ( capture ) { if (capture and mRfb) {
if ( mRfb->frameBuffer ) if (mRfb->frameBuffer)
free(mRfb->frameBuffer); free(mRfb->frameBuffer);
(*rfbClientCleanup_f)(mRfb); (*rfbClientCleanup_f)(mRfb);
} }
if (libvnc_lib) {
dlclose(libvnc_lib);
libvnc_lib = nullptr;
}
} }
int VncCamera::PrimeCapture() { int VncCamera::PrimeCapture() {
@ -134,12 +151,9 @@ int VncCamera::PrimeCapture() {
mVncData.width = 0; mVncData.width = 0;
mVncData.height = 0; mVncData.height = 0;
mBufferSize = SWScale::GetBufferSize(AV_PIX_FMT_RGBA, width, height); // TODO, support 8bit or 24bit
mRfb = (*rfbGetClient_f)(8 /* bits per sample */, 3 /* samples per pixel */, 4 /* bytes Per Pixel */); mRfb = (*rfbGetClient_f)(8 /* bits per sample */, 3 /* samples per pixel */, 4 /* bytes Per Pixel */);
mRfb->frameBuffer = nullptr; mRfb->MallocFrameBuffer = resize;
//(uint8_t *)av_malloc(mBufferSize);
mRfb->canHandleNewFBSize = false;
(*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData); (*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData);
(*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str()); (*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str());
@ -150,16 +164,17 @@ int VncCamera::PrimeCapture() {
mRfb->GetCredential = GetCredentialsCallback; mRfb->GetCredential = GetCredentialsCallback;
mRfb->programName = "Zoneminder VNC Monitor"; mRfb->programName = "Zoneminder VNC Monitor";
if ( mRfb->serverHost ) free(mRfb->serverHost);
mRfb->serverHost = strdup(mHost.c_str()); mRfb->serverHost = strdup(mHost.c_str());
mRfb->serverPort = atoi(mPort.c_str()); mRfb->serverPort = atoi(mPort.c_str());
} }
if ( ! (*rfbInitClient_f)(mRfb, 0, nullptr) ) { if (!(*rfbInitClient_f)(mRfb, 0, nullptr)) {
/* IF rfbInitClient fails, it calls rdbClientCleanup which will free mRfb */ /* IF rfbInitClient fails, it calls rdbClientCleanup which will free mRfb */
Warning("Failed to Prime capture from %s", mHost.c_str()); Warning("Failed to Prime capture from %s", mHost.c_str());
mRfb = nullptr; mRfb = nullptr;
return -1; return -1;
} }
if ( ((unsigned int)mRfb->width != width) or ((unsigned int)mRfb->height != height) ) { if (((unsigned int)mRfb->width != width) or ((unsigned int)mRfb->height != height)) {
Warning("Specified dimensions do not match screen size monitor: (%dx%d) != vnc: (%dx%d)", Warning("Specified dimensions do not match screen size monitor: (%dx%d) != vnc: (%dx%d)",
width, height, mRfb->width, mRfb->height); width, height, mRfb->width, mRfb->height);
} }
@ -170,9 +185,9 @@ int VncCamera::PrimeCapture() {
int VncCamera::PreCapture() { int VncCamera::PreCapture() {
int rc = (*WaitForMessage_f)(mRfb, 500); int rc = (*WaitForMessage_f)(mRfb, 500);
if ( rc < 0 ) { if (rc < 0) {
return -1; return -1;
} else if ( !rc ) { } else if (!rc) {
return rc; return rc;
} }
rfbBool res = (*HandleRFBServerMessage_f)(mRfb); rfbBool res = (*HandleRFBServerMessage_f)(mRfb);
@ -181,12 +196,12 @@ int VncCamera::PreCapture() {
} }
int VncCamera::Capture(ZMPacket &zm_packet) { int VncCamera::Capture(ZMPacket &zm_packet) {
if ( !mVncData.buffer ) { if (!mVncData.buffer) {
Debug(1, "No buffer"); Debug(1, "No buffer");
return 0; return 0;
} }
if ( !zm_packet.image ) { if (!zm_packet.image) {
Debug(1, "Allocating image %dx%d %dcolours = %d", width, height, colours, pixels); Debug(1, "Allocating image %dx%d %dcolours = %d", width, height, colours, colours*pixels);
zm_packet.image = new Image(width, height, colours, subpixelorder); zm_packet.image = new Image(width, height, colours, subpixelorder);
} }
zm_packet.keyframe = 1; zm_packet.keyframe = 1;
@ -194,9 +209,20 @@ int VncCamera::Capture(ZMPacket &zm_packet) {
zm_packet.packet.stream_index = mVideoStreamId; zm_packet.packet.stream_index = mVideoStreamId;
uint8_t *directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder); uint8_t *directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder);
Debug(1, "scale src %p, %d, dest %p %d %d %dx%d %dx%d", mVncData.buffer,
mRfb->si.framebufferWidth * mRfb->si.framebufferHeight * 4,
directbuffer,
width * height * colours,
mImgPixFmt,
mRfb->si.framebufferWidth,
mRfb->si.framebufferHeight,
width,
height);
int rc = scale.Convert( int rc = scale.Convert(
mVncData.buffer, mVncData.buffer,
mRfb->si.framebufferHeight * mRfb->si.framebufferWidth * 4, mRfb->si.framebufferWidth * mRfb->si.framebufferHeight * 4,
//SWScale::GetBufferSize(AV_PIX_FMT_RGBA, mRfb->si.framebufferWidth, mRfb->si.framebufferHeight),
directbuffer, directbuffer,
width * height * colours, width * height * colours,
AV_PIX_FMT_RGBA, AV_PIX_FMT_RGBA,

View File

@ -26,7 +26,6 @@ class VncCamera : public Camera {
protected: protected:
rfbClient *mRfb; rfbClient *mRfb;
VncPrivateData mVncData; VncPrivateData mVncData;
int mBufferSize;
SWScale scale; SWScale scale;
AVPixelFormat mImgPixFmt; AVPixelFormat mImgPixFmt;
std::string mHost; std::string mHost;
@ -48,7 +47,7 @@ public:
int p_hue, int p_hue,
int p_colour, int p_colour,
bool p_capture, bool p_capture,
bool p_record_audio ); bool p_record_audio);
~VncCamera(); ~VncCamera();

View File

@ -341,7 +341,7 @@ LocalCamera::LocalCamera(
Debug(2, "V4L support enabled, using V4L%d api", v4l_version); Debug(2, "V4L support enabled, using V4L%d api", v4l_version);
} }
if ( !last_camera || channel != last_camera->channel ) { if ( (!last_camera) || (channel != last_camera->channel) ) {
// We are the first, or only, input that uses this channel // We are the first, or only, input that uses this channel
channel_prime = true; channel_prime = true;
channel_index = channel_count++; channel_index = channel_count++;
@ -383,19 +383,19 @@ LocalCamera::LocalCamera(
} }
#endif #endif
if ( capture ) { if (capture) {
if ( last_camera ) { if (last_camera) {
if ( (p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1) ) if ((p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1))
Fatal( "Different Video For Linux version used for monitors sharing same device" ); Fatal("Different Video For Linux version used for monitors sharing same device");
if ( standard != last_camera->standard ) if (standard != last_camera->standard)
Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); Warning("Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong");
if ( palette != last_camera->palette ) if (palette != last_camera->palette)
Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); Warning("Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong");
if ( width != last_camera->width || height != last_camera->height ) if (width != last_camera->width or height != last_camera->height)
Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); Warning("Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong");
} }
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
@ -677,7 +677,8 @@ LocalCamera::LocalCamera(
imgConversionContext = nullptr; imgConversionContext = nullptr;
} // end if capture and conversion_tye == swscale } // end if capture and conversion_tye == swscale
#endif #endif
get_VideoStream(); if ( capture and device_prime )
Initialise();
} // end LocalCamera::LocalCamera } // end LocalCamera::LocalCamera
LocalCamera::~LocalCamera() { LocalCamera::~LocalCamera() {
@ -1974,9 +1975,9 @@ int LocalCamera::Contrast( int p_contrast ) {
} }
int LocalCamera::PrimeCapture() { int LocalCamera::PrimeCapture() {
if ( primed ) return 1; get_VideoStream();
if ( !device_prime )
Initialise(); return 1;
Debug(2, "Priming capture"); Debug(2, "Priming capture");
#if ZM_HAS_V4L2 #if ZM_HAS_V4L2
@ -1994,8 +1995,10 @@ int LocalCamera::PrimeCapture() {
vid_buf.memory = v4l2_data.reqbufs.memory; vid_buf.memory = v4l2_data.reqbufs.memory;
vid_buf.index = frame; vid_buf.index = frame;
if ( vidioctl(vid_fd, VIDIOC_QBUF, &vid_buf) < 0 ) if (vidioctl(vid_fd, VIDIOC_QBUF, &vid_buf) < 0) {
Fatal("Failed to queue buffer %d: %s", frame, strerror(errno)); Error("Failed to queue buffer %d: %s", frame, strerror(errno));
return 0;
}
} }
v4l2_data.bufptr = nullptr; v4l2_data.bufptr = nullptr;
@ -2003,9 +2006,11 @@ int LocalCamera::PrimeCapture() {
//enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//enum v4l2_buf_type type = v4l2_data.fmt.type; //enum v4l2_buf_type type = v4l2_data.fmt.type;
enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type;
if ( vidioctl(vid_fd, VIDIOC_STREAMON, &type) < 0 ) if (vidioctl(vid_fd, VIDIOC_STREAMON, &type) < 0) {
Fatal("Failed to start capture stream: %s", strerror(errno)); Error("Failed to start capture stream: %s", strerror(errno));
return -1;
} }
} // end if v4l_version == 2
#endif // ZM_HAS_V4L2 #endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1 #if ZM_HAS_V4L1
if ( v4l_version == 1 ) { if ( v4l_version == 1 ) {
@ -2018,8 +2023,6 @@ int LocalCamera::PrimeCapture() {
} }
} }
#endif // ZM_HAS_V4L1 #endif // ZM_HAS_V4L1
mVideoStreamId = 0;
primed = true;
return 1; return 1;
} // end LocalCamera::PrimeCapture } // end LocalCamera::PrimeCapture
@ -2052,7 +2055,6 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
memset(&vid_buf, 0, sizeof(vid_buf)); memset(&vid_buf, 0, sizeof(vid_buf));
vid_buf.type = v4l2_data.fmt.type; vid_buf.type = v4l2_data.fmt.type;
//vid_buf.memory = V4L2_MEMORY_MMAP;
vid_buf.memory = v4l2_data.reqbufs.memory; vid_buf.memory = v4l2_data.reqbufs.memory;
Debug(3, "Capturing %d frames", captures_per_frame); Debug(3, "Capturing %d frames", captures_per_frame);
@ -2120,6 +2122,65 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame];
} }
#endif // ZM_HAS_V4L1 #endif // ZM_HAS_V4L1
#if ZM_HAS_V4L2
if ( v4l_version == 2 ) {
if ( channel_count > 1 ) {
int next_channel = (channel_index+1)%channel_count;
Debug(3, "Switching video source to %d", channels[next_channel]);
if ( vidioctl(vid_fd, VIDIOC_S_INPUT, &channels[next_channel]) < 0 ) {
Error("Failed to set camera source %d: %s", channels[next_channel], strerror(errno));
return -1;
}
v4l2_std_id stdId = standards[next_channel];
if ( vidioctl(vid_fd, VIDIOC_S_STD, &stdId) < 0 ) {
Error("Failed to set video format %d: %s", standards[next_channel], strerror(errno));
}
}
if ( v4l2_data.bufptr ) {
Debug(3, "Requeueing buffer %d", v4l2_data.bufptr->index);
if ( vidioctl(vid_fd, VIDIOC_QBUF, v4l2_data.bufptr) < 0 ) {
Error("Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno));
return -1;
}
} else {
Error("Unable to requeue buffer due to not v4l2_data")
}
}
#if ZM_HAS_V4L1
else
#endif // ZM_HAS_V4L1
#endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1
if ( v4l_version == 1 ) {
if ( channel_count > 1 ) {
Debug(3, "Switching video source");
int next_channel = (channel_index+1)%channel_count;
struct video_channel vid_src;
memset(&vid_src, 0, sizeof(vid_src));
vid_src.channel = channel;
if ( ioctl(vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) {
Error("Failed to get camera source %d: %s", channel, strerror(errno));
return -1;
}
vid_src.channel = channels[next_channel];
vid_src.norm = standards[next_channel];
vid_src.flags = 0;
vid_src.type = VIDEO_TYPE_CAMERA;
if ( ioctl(vid_fd, VIDIOCSCHAN, &vid_src) < 0 ) {
Error("Failed to set camera source %d: %s", channel, strerror(errno));
return -1;
}
}
Debug(3, "Requeueing frame %d", v4l1_data.active_frame);
if ( ioctl(vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame]) < 0 ) {
Error("Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno));
return -1;
}
v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames;
}
#endif // ZM_HAS_V4L1
} /* prime capture */ } /* prime capture */
@ -2185,69 +2246,8 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
} // end int LocalCamera::Capture() } // end int LocalCamera::Capture()
int LocalCamera::PostCapture() { int LocalCamera::PostCapture() {
return 1;
Debug(4, "Post-capturing"); Debug(4, "Post-capturing");
// Requeue the buffer unless we need to switch or are a duplicate camera on a channel
if ( channel_count > 1 || channel_prime ) {
#if ZM_HAS_V4L2
if ( v4l_version == 2 ) {
if ( channel_count > 1 ) {
int next_channel = (channel_index+1)%channel_count;
Debug(3, "Switching video source to %d", channels[next_channel]);
if ( vidioctl(vid_fd, VIDIOC_S_INPUT, &channels[next_channel]) < 0 ) {
Error("Failed to set camera source %d: %s", channels[next_channel], strerror(errno));
return -1;
}
v4l2_std_id stdId = standards[next_channel];
if ( vidioctl(vid_fd, VIDIOC_S_STD, &stdId) < 0 ) {
Error("Failed to set video format %d: %s", standards[next_channel], strerror(errno));
}
}
if ( v4l2_data.bufptr ) {
Debug(3, "Requeueing buffer %d", v4l2_data.bufptr->index);
if ( vidioctl(vid_fd, VIDIOC_QBUF, v4l2_data.bufptr) < 0 ) {
Error("Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno));
return -1;
}
} else {
Error("Unable to requeue buffer due to not v4l2_data")
}
}
#if ZM_HAS_V4L1
else
#endif // ZM_HAS_V4L1
#endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1
if ( v4l_version == 1 ) {
if ( channel_count > 1 ) {
Debug(3, "Switching video source");
int next_channel = (channel_index+1)%channel_count;
struct video_channel vid_src;
memset(&vid_src, 0, sizeof(vid_src));
vid_src.channel = channel;
if ( ioctl(vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) {
Error("Failed to get camera source %d: %s", channel, strerror(errno));
return -1;
}
vid_src.channel = channels[next_channel];
vid_src.norm = standards[next_channel];
vid_src.flags = 0;
vid_src.type = VIDEO_TYPE_CAMERA;
if ( ioctl(vid_fd, VIDIOCSCHAN, &vid_src) < 0 ) {
Error("Failed to set camera source %d: %s", channel, strerror(errno));
return -1;
}
}
Debug(3, "Requeueing frame %d", v4l1_data.active_frame);
if ( ioctl(vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame]) < 0 ) {
Error("Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno));
return -1;
}
v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames;
}
#endif // ZM_HAS_V4L1
}
return 0; return 0;
} }

View File

@ -21,9 +21,7 @@
#include "zm_db.h" #include "zm_db.h"
#include "zm_utils.h" #include "zm_utils.h"
#include <csignal>
#include <cstdarg>
#include <cstring>
#include <libgen.h> #include <libgen.h>
#include <syslog.h> #include <syslog.h>
#include <sys/time.h> #include <sys/time.h>
@ -31,6 +29,11 @@
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <sys/thr.h> #include <sys/thr.h>
#endif #endif
#include <cerrno>
#include <csignal>
#include <cstdarg>
#include <cstring>
bool Logger::smInitialised = false; bool Logger::smInitialised = false;
Logger *Logger::smInstance = nullptr; Logger *Logger::smInstance = nullptr;
@ -56,16 +59,15 @@ Logger::Logger() :
mEffectiveLevel(NOLOG), mEffectiveLevel(NOLOG),
mDbConnected(false), mDbConnected(false),
mLogPath(staticConfig.PATH_LOGS.c_str()), mLogPath(staticConfig.PATH_LOGS.c_str()),
//mLogFile( mLogPath+"/"+mId+".log" ), // mLogFile( mLogPath+"/"+mId+".log" ),
mLogFileFP(nullptr), mLogFileFP(nullptr),
mHasTerminal(false), mHasTerminal(false),
mFlush(false) { mFlush(false) {
if (smInstance) {
if ( smInstance ) {
Panic("Attempt to create second instance of Logger class"); Panic("Attempt to create second instance of Logger class");
} }
if ( !smInitialised ) { if (!smInitialised) {
smCodes[INFO] = "INF"; smCodes[INFO] = "INF";
smCodes[WARNING] = "WAR"; smCodes[WARNING] = "WAR";
smCodes[ERROR] = "ERR"; smCodes[ERROR] = "ERR";
@ -80,16 +82,16 @@ Logger::Logger() :
smSyslogPriorities[PANIC] = LOG_ERR; smSyslogPriorities[PANIC] = LOG_ERR;
char code[4] = ""; char code[4] = "";
for ( int i = DEBUG1; i <= DEBUG9; i++ ) { for (int i = DEBUG1; i <= DEBUG9; i++) {
snprintf(code, sizeof(code), "DB%d", i); snprintf(code, sizeof(code), "DB%d", i);
smCodes[i] = code; smCodes[i] = code;
smSyslogPriorities[i] = LOG_DEBUG; smSyslogPriorities[i] = LOG_DEBUG;
} }
smInitialised = true; smInitialised = true;
} } // end if ! smInitialised
if ( fileno(stderr) && isatty(fileno(stderr)) ) { if (fileno(stderr) && isatty(fileno(stderr))) {
mHasTerminal = true; mHasTerminal = true;
mTerminalLevel = WARNING; mTerminalLevel = WARNING;
} }
@ -100,14 +102,6 @@ Logger::~Logger() {
smCodes.clear(); smCodes.clear();
smSyslogPriorities.clear(); smSyslogPriorities.clear();
smInitialised = false; smInitialised = false;
#if 0
for ( StringMap::iterator itr = smCodes.begin(); itr != smCodes.end(); itr ++ ) {
smCodes.erase( itr );
}
for ( IntMap::iterator itr = smSyslogPriorities.begin(); itr != smSyslogPriorities.end(); itr ++ ) {
smSyslogPriorities.erase(itr);
}
#endif
} }
void Logger::initialise(const std::string &id, const Options &options) { void Logger::initialise(const std::string &id, const Options &options) {
@ -354,10 +348,10 @@ Logger::Level Logger::databaseLevel(Logger::Level databaseLevel) {
} }
Logger::Level Logger::fileLevel(Logger::Level fileLevel) { Logger::Level Logger::fileLevel(Logger::Level fileLevel) {
if ( fileLevel > NOOPT ) { if (fileLevel > NOOPT) {
fileLevel = limit(fileLevel); fileLevel = limit(fileLevel);
// Always close, because we may have changed file names // Always close, because we may have changed file names
if ( mFileLevel > NOLOG ) if (mFileLevel > NOLOG)
closeFile(); closeFile();
mFileLevel = fileLevel; mFileLevel = fileLevel;
// Don't try to open it here because it will create the log file even if we never write to it. // Don't try to open it here because it will create the log file even if we never write to it.
@ -366,13 +360,13 @@ Logger::Level Logger::fileLevel(Logger::Level fileLevel) {
} }
Logger::Level Logger::syslogLevel(Logger::Level syslogLevel) { Logger::Level Logger::syslogLevel(Logger::Level syslogLevel) {
if ( syslogLevel > NOOPT ) { if (syslogLevel > NOOPT) {
syslogLevel = limit(syslogLevel); syslogLevel = limit(syslogLevel);
if ( mSyslogLevel != syslogLevel ) { if (mSyslogLevel != syslogLevel) {
if ( mSyslogLevel > NOLOG ) if (mSyslogLevel > NOLOG)
closeSyslog(); closeSyslog();
mSyslogLevel = syslogLevel; mSyslogLevel = syslogLevel;
if ( mSyslogLevel > NOLOG ) if (mSyslogLevel > NOLOG)
openSyslog(); openSyslog();
} }
} }
@ -382,18 +376,18 @@ Logger::Level Logger::syslogLevel(Logger::Level syslogLevel) {
void Logger::logFile(const std::string &logFile) { void Logger::logFile(const std::string &logFile) {
bool addLogPid = false; bool addLogPid = false;
std::string tempLogFile = logFile; std::string tempLogFile = logFile;
if ( tempLogFile[tempLogFile.length()-1] == '+' ) { if (tempLogFile[tempLogFile.length()-1] == '+') {
tempLogFile.resize(tempLogFile.length()-1); tempLogFile.resize(tempLogFile.length()-1);
addLogPid = true; addLogPid = true;
} }
if ( addLogPid ) if (addLogPid)
mLogFile = stringtf("%s.%05d", tempLogFile.c_str(), getpid()); mLogFile = stringtf("%s.%05d", tempLogFile.c_str(), getpid());
else else
mLogFile = tempLogFile; mLogFile = tempLogFile;
} }
void Logger::openFile() { void Logger::openFile() {
if ( mLogFile.size() ) { if (mLogFile.size()) {
if ( (mLogFileFP = fopen(mLogFile.c_str(), "a")) == nullptr ) { if ( (mLogFileFP = fopen(mLogFile.c_str(), "a")) == nullptr ) {
mFileLevel = NOLOG; mFileLevel = NOLOG;
Error("fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno)); Error("fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno));
@ -404,9 +398,9 @@ void Logger::openFile() {
} }
void Logger::closeFile() { void Logger::closeFile() {
if ( mLogFileFP ) { if (mLogFileFP) {
fflush(mLogFileFP); fflush(mLogFileFP);
if ( fclose(mLogFileFP) < 0 ) { if (fclose(mLogFileFP) < 0) {
mLogFileFP = nullptr; mLogFileFP = nullptr;
Error("fclose(), error = %s", strerror(errno)); Error("fclose(), error = %s", strerror(errno));
} }
@ -415,7 +409,6 @@ void Logger::closeFile() {
} }
void Logger::closeDatabase() { void Logger::closeDatabase() {
} }
void Logger::openSyslog() { void Logger::openSyslog() {
@ -427,24 +420,21 @@ void Logger::closeSyslog() {
} }
void Logger::logPrint(bool hex, const char * const filepath, const int line, const int level, const char *fstring, ...) { void Logger::logPrint(bool hex, const char * const filepath, const int line, const int level, const char *fstring, ...) {
if (level > mEffectiveLevel) return;
if ( level > mEffectiveLevel ) { if (level < PANIC || level > DEBUG9)
return; Panic("Invalid logger level %d", level);
}
log_mutex.lock(); log_mutex.lock();
// Can we save some cycles by having these as members and not allocate them on the fly? I think so.
char timeString[64]; char timeString[64];
char logString[8192]; char logString[8192];
va_list argPtr; va_list argPtr;
struct timeval timeVal; struct timeval timeVal;
char *filecopy = strdup(filepath); const char *base = strrchr(filepath, '/');
const char * const file = basename(filecopy); const char *file = base ? base+1 : filepath;
const char *classString = smCodes[level].c_str(); const char *classString = smCodes[level].c_str();
if ( level < PANIC || level > DEBUG9 )
Panic("Invalid logger level %d", level);
gettimeofday(&timeVal, nullptr); gettimeofday(&timeVal, nullptr);
#if 0 #if 0
@ -473,12 +463,12 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
#else #else
#ifdef HAVE_SYSCALL #ifdef HAVE_SYSCALL
#ifdef __FreeBSD_kernel__ #ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id if ((syscall(SYS_thr_self, &tid)) < 0) // Thread/Process id
# else # else
// SOLARIS doesn't have SYS_gettid; don't assume // SOLARIS doesn't have SYS_gettid; don't assume
#ifdef SYS_gettid #ifdef SYS_gettid
if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id if ((tid = syscall(SYS_gettid)) < 0) // Thread/Process id
#endif // SYS_gettid #endif // SYS_gettid
#endif #endif
#endif // HAVE_SYSCALL #endif // HAVE_SYSCALL
@ -512,75 +502,63 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con
char *syslogEnd = logPtr; char *syslogEnd = logPtr;
strncpy(logPtr, "]\n", sizeof(logString)-(logPtr-logString)); strncpy(logPtr, "]\n", sizeof(logString)-(logPtr-logString));
if ( level <= mTerminalLevel ) { if (level <= mTerminalLevel) {
puts(logString); puts(logString);
fflush(stdout); fflush(stdout);
} }
if ( level <= mFileLevel ) { if (level <= mFileLevel) {
if ( !mLogFileFP ) { if (!mLogFileFP) {
// We do this here so that we only create the file if we ever write to it. // FIXME unlocking here is a problem. Another thread could sneak in.
log_mutex.unlock(); log_mutex.unlock();
// We do this here so that we only create the file if we ever write to it.
openFile(); openFile();
log_mutex.lock(); log_mutex.lock();
} }
if ( mLogFileFP ) { if (mLogFileFP) {
fputs(logString, mLogFileFP); fputs(logString, mLogFileFP);
if ( mFlush ) if (mFlush) fflush(mLogFileFP);
fflush(mLogFileFP);
} else { } else {
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
if ( level <= mDatabaseLevel ) { if (level <= mDatabaseLevel) {
if (db_mutex.try_lock_for(1)) { if (zmDbConnected) {
char escapedString[(strlen(syslogStart)*2)+1]; int syslogSize = syslogEnd-syslogStart;
mysql_real_escape_string(&dbconn, escapedString, syslogStart, strlen(syslogStart)); char escapedString[(syslogSize*2)+1];
mysql_real_escape_string(&dbconn, escapedString, syslogStart, syslogSize);
char sql[ZM_SQL_MED_BUFSIZ]; std::string sql_string = stringtf(
snprintf(sql, sizeof(sql),
"INSERT INTO `Logs` " "INSERT INTO `Logs` "
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )" "( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
" VALUES " " VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", "( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line
); );
if ( mysql_query(&dbconn, sql) ) { dbQueue.push(sql_string);
Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel(NOLOG);
Error("Can't insert log entry: sql(%s) error(%s)", sql, mysql_error(&dbconn));
databaseLevel(tempDatabaseLevel);
}
db_mutex.unlock();
} else { } else {
Level tempDatabaseLevel = mDatabaseLevel; puts("Db is closed");
databaseLevel(NOLOG);
Error("Can't insert log entry since the DB lock could not be obtained. Message: %s", syslogStart);
databaseLevel(tempDatabaseLevel);
} }
} // end if level <= mDatabaseLevel } // end if level <= mDatabaseLevel
if ( level <= mSyslogLevel ) { if (level <= mSyslogLevel) {
*syslogEnd = '\0'; *syslogEnd = '\0';
syslog(smSyslogPriorities[level], "%s [%s] [%s]", classString, mId.c_str(), syslogStart); syslog(smSyslogPriorities[level], "%s [%s] [%s]", classString, mId.c_str(), syslogStart);
} }
free(filecopy);
log_mutex.unlock(); log_mutex.unlock();
if ( level <= FATAL ) { if (level <= FATAL) {
logTerm(); logTerm();
zmDbClose(); zmDbClose();
if ( level <= PANIC ) if (level <= PANIC) abort();
abort();
exit(-1); exit(-1);
} }
} // end logPrint } // end logPrint
void logInit(const char *name, const Logger::Options &options) { void logInit(const char *name, const Logger::Options &options) {
if ( Logger::smInstance ) { if (Logger::smInstance) {
delete Logger::smInstance; delete Logger::smInstance;
Logger::smInstance = nullptr;
} }
Logger::smInstance = new Logger(); Logger::smInstance = new Logger();
@ -588,7 +566,7 @@ void logInit(const char *name, const Logger::Options &options) {
} }
void logTerm() { void logTerm() {
if ( Logger::smInstance ) { if (Logger::smInstance) {
delete Logger::smInstance; delete Logger::smInstance;
Logger::smInstance = nullptr; Logger::smInstance = nullptr;
} }

View File

@ -153,18 +153,12 @@ public:
void terminate(); void terminate();
const std::string &id(const std::string &id); const std::string &id(const std::string &id);
const std::string &id() const { const std::string &id() const { return mId; }
return mId;
}
Level level() const { Level level() const { return mLevel; }
return mLevel;
}
Level level(Level=NOOPT); Level level(Level=NOOPT);
bool debugOn() const { bool debugOn() const { return mEffectiveLevel >= DEBUG1; }
return mEffectiveLevel >= DEBUG1;
}
Level terminalLevel(Level=NOOPT); Level terminalLevel(Level=NOOPT);
Level databaseLevel(Level=NOOPT); Level databaseLevel(Level=NOOPT);

View File

@ -651,6 +651,7 @@ void Monitor::LoadCamera() {
switch (type) { switch (type) {
case LOCAL: { case LOCAL: {
#if ZM_HAS_V4L
int extras = (deinterlacing >> 24) & 0xff; int extras = (deinterlacing >> 24) & 0xff;
camera = ZM::make_unique<LocalCamera>(this, camera = ZM::make_unique<LocalCamera>(this,
@ -672,6 +673,9 @@ void Monitor::LoadCamera() {
record_audio, record_audio,
extras extras
); );
#else
Fatal("Not compiled with local v4l camera support");
#endif
break; break;
} }
case REMOTE: { case REMOTE: {
@ -1071,11 +1075,6 @@ Monitor::~Monitor() {
Close(); Close();
if (mem_ptr != nullptr) { if (mem_ptr != nullptr) {
std::lock_guard<std::mutex> lck(event_mutex);
if (event) {
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id());
closeEvent();
}
if (purpose != QUERY) { if (purpose != QUERY) {
shared_data->state = state = IDLE; shared_data->state = state = IDLE;
shared_data->last_read_index = image_buffer_count; shared_data->last_read_index = image_buffer_count;
@ -1317,25 +1316,17 @@ void Monitor::actionReload() {
void Monitor::actionEnable() { void Monitor::actionEnable() {
shared_data->action |= RELOAD; shared_data->action |= RELOAD;
db_mutex.lock(); char sql[ZM_SQL_SML_BUFSIZ];
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %d", id); snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %d", id);
if ( mysql_query(&dbconn, sql) ) { zmDbDo(sql);
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
} }
void Monitor::actionDisable() { void Monitor::actionDisable() {
shared_data->action |= RELOAD; shared_data->action |= RELOAD;
static char sql[ZM_SQL_SML_BUFSIZ]; char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %d", id); snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %d", id);
db_mutex.lock(); zmDbDo(sql);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
} }
void Monitor::actionSuspend() { void Monitor::actionSuspend() {
@ -1705,7 +1696,6 @@ 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;
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
// The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost, // The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost,
// and nothing else will update the status until zmc restarts. Since we are successfully capturing we can // and nothing else will update the status until zmc restarts. Since we are successfully capturing we can
@ -1715,10 +1705,7 @@ void Monitor::UpdateCaptureFPS() {
"VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE " "VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE "
"CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected'", "CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected'",
id, new_capture_fps, new_capture_bandwidth, new_capture_fps, new_capture_bandwidth); id, new_capture_fps, new_capture_bandwidth, new_capture_fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) { dbQueue.push(sql);
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
} // now != last_fps_time } // now != last_fps_time
} // end if report fps } // end if report fps
} // void Monitor::UpdateCaptureFPS() } // void Monitor::UpdateCaptureFPS()
@ -1759,11 +1746,7 @@ void Monitor::UpdateAnalysisFPS() {
"INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf)" "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf)"
" ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", " ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf",
id, new_analysis_fps, new_analysis_fps); id, new_analysis_fps, new_analysis_fps);
db_mutex.lock(); dbQueue.push(sql);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn));
}
db_mutex.unlock();
last_analysis_fps_time = now_double; last_analysis_fps_time = now_double;
last_motion_frame_count = motion_frame_count; last_motion_frame_count = motion_frame_count;
} else { } else {
@ -1844,7 +1827,7 @@ bool Monitor::Analyse() {
} // end if trigger_on } // end if trigger_on
if ( signal_change ) { if ( signal_change ) {
Debug(2, "Signal change"); 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";
@ -1954,7 +1937,7 @@ bool Monitor::Analyse() {
// 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 in mocord"); Debug(2, "Have event %" PRIu64 " in mocord", event->Id());
if ( section_length if ( section_length
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= 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 ) ) && ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) )
@ -2233,6 +2216,7 @@ bool Monitor::Analyse() {
shared_data->last_read_time = time(nullptr); shared_data->last_read_time = time(nullptr);
analysis_image_count++; analysis_image_count++;
UpdateAnalysisFPS(); UpdateAnalysisFPS();
packetqueue.clearPackets(snap);
return true; return true;
} // end Monitor::Analyse } // end Monitor::Analyse
@ -2338,7 +2322,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
for ( int i = 0; i < n_link_ids; i++ ) { for ( int i = 0; i < n_link_ids; i++ ) {
Debug(1, "Checking linked monitor %d", link_ids[i]); Debug(1, "Checking linked monitor %d", link_ids[i]);
db_mutex.lock(); std::lock_guard<std::mutex> lck(db_mutex);
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"SELECT `Id`, `Name` FROM `Monitors`" "SELECT `Id`, `Name` FROM `Monitors`"
@ -2347,14 +2331,12 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
" AND `Function` != 'Monitor'" " AND `Function` != 'Monitor'"
" AND `Enabled`=1", " AND `Enabled`=1",
link_ids[i]); link_ids[i]);
if ( mysql_query(&dbconn, sql) ) { if (mysql_query(&dbconn, sql)) {
db_mutex.unlock();
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
continue; continue;
} }
MYSQL_RES *result = mysql_store_result(&dbconn); MYSQL_RES *result = mysql_store_result(&dbconn);
db_mutex.unlock();
if ( !result ) { if ( !result ) {
Error("Can't use query result: %s", mysql_error(&dbconn)); Error("Can't use query result: %s", mysql_error(&dbconn));
continue; continue;
@ -2516,6 +2498,7 @@ int Monitor::Capture() {
Debug(2, "Queueing audio packet"); Debug(2, "Queueing audio packet");
packetqueue.queuePacket(packet); packetqueue.queuePacket(packet);
} else { } else {
Debug(4, "Not Queueing audio packet");
delete packet; delete packet;
} }
// Don't update last_write_index because that is used for live streaming // Don't update last_write_index because that is used for live streaming
@ -2934,10 +2917,16 @@ 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() {
std::lock_guard<std::mutex> lck(event_mutex);
if (event) {
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id());
closeEvent();
}
if (camera) camera->Close(); if (camera) camera->Close();
packetqueue.clear(); packetqueue.clear();
return 1; return 1;
} }
Monitor::Orientation Monitor::getOrientation() const { return orientation; } Monitor::Orientation Monitor::getOrientation() const { return orientation; }
// Wait for camera to get an image, and then assign it as the base reference image. // Wait for camera to get an image, and then assign it as the base reference image.

View File

@ -24,7 +24,11 @@
#include "zm_time.h" #include "zm_time.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <glob.h> #include <glob.h>
#include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef __FreeBSD__
#include <netinet/in.h>
#endif
const int MAX_SLEEP_USEC = 1000000; // 1 sec const int MAX_SLEEP_USEC = 1000000; // 1 sec

View File

@ -47,7 +47,7 @@ class MonitorStream : public StreamBase {
bool checkSwapPath(const char *path, bool create_path); bool checkSwapPath(const char *path, bool create_path);
bool sendFrame(const char *filepath, struct timeval *timestamp); bool sendFrame(const char *filepath, struct timeval *timestamp);
bool sendFrame(Image *image, struct timeval *timestamp); bool sendFrame(Image *image, struct timeval *timestamp);
void processCommand(const CmdMsg *msg); void processCommand(const CmdMsg *msg) override;
void SingleImage(int scale=100); void SingleImage(int scale=100);
void SingleImageRaw(int scale=100); void SingleImageRaw(int scale=100);
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H

View File

@ -21,6 +21,7 @@
#define ZM_MPEG_H #define ZM_MPEG_H
#include "zm_ffmpeg.h" #include "zm_ffmpeg.h"
#include <pthread.h>
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC

View File

@ -159,16 +159,17 @@ int ZMPacket::decode(AVCodecContext *ctx) {
return 0; return 0;
} }
int bytes_consumed = ret; int bytes_consumed = ret;
if ( ret > 0 ) if ( ret > 0 ) {
zm_dump_video_frame(in_frame, "got frame"); zm_dump_video_frame(in_frame, "got frame");
#if HAVE_LIBAVUTIL_HWCONTEXT_H #if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0) #if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
if ( ctx->sw_pix_fmt != in_frame->format ) { if ( fix_deprecated_pix_fmt(ctx->sw_pix_fmt) != fix_deprecated_pix_fmt(static_cast<AVPixelFormat>(in_frame->format)) ) {
Debug(1, "Have different format %s != %s.", Debug(1, "Have different format ctx->pix_fmt %s ?= ctx->sw_pix_fmt %s in_frame->format %s.",
av_get_pix_fmt_name(ctx->pix_fmt), av_get_pix_fmt_name(ctx->pix_fmt),
av_get_pix_fmt_name(ctx->sw_pix_fmt) av_get_pix_fmt_name(ctx->sw_pix_fmt),
av_get_pix_fmt_name(static_cast<AVPixelFormat>(in_frame->format))
); );
#if 0 #if 0
if ( target_format == AV_PIX_FMT_NONE and ctx->hw_frames_ctx and (image->Colours() == 4) ) { if ( target_format == AV_PIX_FMT_NONE and ctx->hw_frames_ctx and (image->Colours() == 4) ) {
@ -219,7 +220,12 @@ int ZMPacket::decode(AVCodecContext *ctx) {
av_frame_free(&new_frame); av_frame_free(&new_frame);
return 0; return 0;
} }
new_frame->pts = in_frame->pts; ret = av_frame_copy_props(new_frame, in_frame);
if ( ret < 0 ) {
Error("Unable to copy props: %s, continuing",
av_make_error_string(ret).c_str());
}
zm_dump_video_frame(new_frame, "After hwtransfer"); zm_dump_video_frame(new_frame, "After hwtransfer");
#if 0 #if 0
if ( new_frame->format == AV_PIX_FMT_RGB0 ) { if ( new_frame->format == AV_PIX_FMT_RGB0 ) {
@ -232,7 +238,6 @@ int ZMPacket::decode(AVCodecContext *ctx) {
} else } else
#endif #endif
#endif #endif
if ( ret > 0 ) {
Debug(2, "Same pix format %s so not hwtransferring. sw_pix_fmt is %s", Debug(2, "Same pix format %s so not hwtransferring. sw_pix_fmt is %s",
av_get_pix_fmt_name(ctx->pix_fmt), av_get_pix_fmt_name(ctx->pix_fmt),
av_get_pix_fmt_name(ctx->sw_pix_fmt) av_get_pix_fmt_name(ctx->sw_pix_fmt)
@ -242,7 +247,7 @@ int ZMPacket::decode(AVCodecContext *ctx) {
image->Assign(in_frame); image->Assign(in_frame);
} }
#endif #endif
} } // end if if ( ret > 0 ) {
return bytes_consumed; return bytes_consumed;
} // end ZMPacket::decode } // end ZMPacket::decode

View File

@ -99,7 +99,15 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) {
--(*iterator_it); --(*iterator_it);
} }
} // end foreach iterator } // end foreach iterator
mutex.unlock();
// We signal on every packet because someday we may analyze sound
Debug(4, "packetqueue queuepacket, unlocked signalling");
condition.notify_all();
return true;
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet)
void PacketQueue::clearPackets(ZMPacket *add_packet) {
// Only do queueCleaning if we are adding a video keyframe, so that we guarantee that there is one. // Only do queueCleaning if we are adding a video keyframe, so that we guarantee that there is one.
// No good. Have to satisfy two conditions: // No good. Have to satisfy two conditions:
// 1. packetqueue starts with a video keyframe // 1. packetqueue starts with a video keyframe
@ -109,14 +117,20 @@ bool PacketQueue::queuePacket(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 ( add_packet->packet.stream_index == video_stream_id if ( ! (
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
)
) { ) {
return;
}
std::unique_lock<std::mutex> lck(mutex);
packetqueue_iterator it = pktQueue.begin(); packetqueue_iterator it = pktQueue.begin();
packetqueue_iterator next_front = pktQueue.begin(); packetqueue_iterator next_front = pktQueue.begin();
@ -129,34 +143,30 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) {
// 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;
Debug(1, "Checking packet to see if we can delete them");
if ( !zm_packet->trylock() ) { if ( !zm_packet->trylock() ) {
Debug(1, "Have locked packet %d", zm_packet->image_index);
break; break;
} }
zm_packet->unlock(); zm_packet->unlock();
if ( is_there_an_iterator_pointing_to_packet(zm_packet) ) { if ( is_there_an_iterator_pointing_to_packet(zm_packet) ) {
Debug(4, "Found IT at beginning of queue. Threads not 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(1, "Have a video keyframe so breaking out"); Debug(1, "Have a video keyframe so setting next front to it");
next_front = it; next_front = it;
} }
} }
it++; it++;
} // end while } // end while
} // end if first packet not locked } // end if first packet not locked
Debug(1, "Resulting pointing at latest packet? %d, have next front? %d", Debug(1, "Resulting pointing at latest packet? %d, next front points to begin? %d",
( *it == add_packet ), ( *it == add_packet ),
( next_front == pktQueue.begin() ) ( next_front == pktQueue.begin() )
); );
if ( next_front != pktQueue.begin() ) { if ( next_front != pktQueue.begin() ) {
Debug(1, "Deleting packets");
// It is enough to delete the packets tested above. A subsequent queuePacket can clear a second set
while ( pktQueue.begin() != next_front ) { while ( pktQueue.begin() != next_front ) {
ZMPacket *zm_packet = *pktQueue.begin(); ZMPacket *zm_packet = *pktQueue.begin();
if ( !zm_packet ) { if ( !zm_packet ) {
@ -171,15 +181,10 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) {
delete zm_packet; delete zm_packet;
} }
} // end if have at least max_video_packet_count video packets remaining } // end if have at least max_video_packet_count video packets remaining
} // end if this is a video keyframe
mutex.unlock();
// We signal on every packet because someday we may analyze sound // We signal on every packet because someday we may analyze sound
Debug(4, "packetqueue queuepacket, unlocked signalling");
condition.notify_all();
return true; return;
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet) } // end voidPacketQueue::clearPackets(ZMPacket* zm_packet)
ZMPacket* PacketQueue::popPacket( ) { ZMPacket* PacketQueue::popPacket( ) {
Debug(4, "pktQueue size %d", pktQueue.size()); Debug(4, "pktQueue size %d", pktQueue.size());
@ -421,7 +426,7 @@ unsigned int PacketQueue::size() {
int PacketQueue::packet_count(int stream_id) { int PacketQueue::packet_count(int stream_id) {
if ( stream_id < 0 or stream_id > max_stream_id ) { if ( stream_id < 0 or stream_id > max_stream_id ) {
Error("Invalid stream_id %d", stream_id); Error("Invalid stream_id %d max is %d", stream_id, max_stream_id);
return -1; return -1;
} }
return packet_counts[stream_id]; return packet_counts[stream_id];
@ -500,6 +505,8 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it(
unsigned int pre_event_count unsigned int pre_event_count
) { ) {
std::unique_lock<std::mutex> lck(mutex);
packetqueue_iterator *it = new packetqueue_iterator; packetqueue_iterator *it = new packetqueue_iterator;
iterators.push_back(it); iterators.push_back(it);

View File

@ -63,6 +63,7 @@ class PacketQueue {
unsigned int get_packet_count(int stream_id) const { return packet_counts[stream_id]; }; unsigned int get_packet_count(int stream_id) const { return packet_counts[stream_id]; };
void clear_unwanted_packets(timeval *recording, int pre_event_count, int mVideoStreamId); void clear_unwanted_packets(timeval *recording, int pre_event_count, int mVideoStreamId);
void clearPackets(ZMPacket *);
int packet_count(int stream_id); int packet_count(int stream_id);
bool increment_it(packetqueue_iterator *it); bool increment_it(packetqueue_iterator *it);

View File

@ -22,6 +22,7 @@
#include "zm_utils.h" #include "zm_utils.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <sys/socket.h>
RemoteCamera::RemoteCamera( RemoteCamera::RemoteCamera(
const Monitor *monitor, const Monitor *monitor,

View File

@ -26,6 +26,7 @@
#include "zm_utils.h" #include "zm_utils.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h> #include <netdb.h>
#include <sys/socket.h>
#ifdef SOLARIS #ifdef SOLARIS
#include <sys/filio.h> // FIONREAD and friends #include <sys/filio.h> // FIONREAD and friends
@ -540,12 +541,10 @@ int RemoteCameraHttp::GetResponse() {
} }
case HEADERCONT : case HEADERCONT :
case SUBHEADERCONT : case SUBHEADERCONT :
{
// Ignore // Ignore
break; break;
} }
} }
}
} else } else
#endif // HAVE_LIBPCRE #endif // HAVE_LIBPCRE
{ {
@ -1062,7 +1061,6 @@ int RemoteCameraHttp::PreCapture() {
if ( mode == SINGLE_IMAGE ) { if ( mode == SINGLE_IMAGE ) {
if ( SendRequest() < 0 ) { if ( SendRequest() < 0 ) {
Error("Unable to send request"); Error("Unable to send request");
Disconnect();
return -1; return -1;
} }
} }
@ -1072,12 +1070,11 @@ int RemoteCameraHttp::PreCapture() {
int RemoteCameraHttp::Capture(ZMPacket &packet) { int RemoteCameraHttp::Capture(ZMPacket &packet) {
int content_length = GetResponse(); int content_length = GetResponse();
if ( content_length == 0 ) { if ( content_length == 0 ) {
Warning( "Unable to capture image, retrying" ); Warning("Unable to capture image, retrying");
return 0; return 0;
} }
if ( content_length < 0 ) { if ( content_length < 0 ) {
Error( "Unable to get response, disconnecting" ); Error("Unable to get response, disconnecting");
Disconnect();
return -1; return -1;
} }
@ -1094,7 +1091,6 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) {
case JPEG : case JPEG :
if ( !image->DecodeJpeg(buffer.extract(content_length), content_length, colours, subpixelorder) ) { if ( !image->DecodeJpeg(buffer.extract(content_length), content_length, colours, subpixelorder) ) {
Error("Unable to decode jpeg"); Error("Unable to decode jpeg");
Disconnect();
return -1; return -1;
} }
break; break;
@ -1102,7 +1098,6 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) {
if ( content_length != (long)image->Size() ) { if ( content_length != (long)image->Size() ) {
Error("Image length mismatch, expected %d bytes, content length was %d", Error("Image length mismatch, expected %d bytes, content length was %d",
image->Size(), content_length); image->Size(), content_length);
Disconnect();
return -1; return -1;
} }
image->Assign(width, height, colours, subpixelorder, buffer, imagesize); image->Assign(width, height, colours, subpixelorder, buffer, imagesize);
@ -1110,14 +1105,12 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) {
case X_RGBZ : case X_RGBZ :
if ( !image->Unzip( buffer.extract( content_length ), content_length ) ) { if ( !image->Unzip( buffer.extract( content_length ), content_length ) ) {
Error("Unable to unzip RGB image"); Error("Unable to unzip RGB image");
Disconnect();
return -1; return -1;
} }
image->Assign(width, height, colours, subpixelorder, buffer, imagesize); image->Assign(width, height, colours, subpixelorder, buffer, imagesize);
break; break;
default : default :
Error("Unexpected image format encountered"); Error("Unexpected image format encountered");
Disconnect();
return -1; return -1;
} }
return 1; return 1;

View File

@ -91,25 +91,7 @@ int SWScale::Convert(
AVFrame *out_frame AVFrame *out_frame
) { ) {
// THe J formats are deprecated, so we need to convert AVPixelFormat format = fix_deprecated_pix_fmt((AVPixelFormat)in_frame->format);
AVPixelFormat format;
switch ( in_frame->format ) {
case AV_PIX_FMT_YUVJ420P :
format = AV_PIX_FMT_YUV420P;
break;
case AV_PIX_FMT_YUVJ422P :
format = AV_PIX_FMT_YUV422P;
break;
case AV_PIX_FMT_YUVJ444P :
format = AV_PIX_FMT_YUV444P;
break;
case AV_PIX_FMT_YUVJ440P :
format = AV_PIX_FMT_YUV440P;
break;
default:
format = (AVPixelFormat)in_frame->format;
break;
}
/* Get the context */ /* Get the context */
swscale_ctx = sws_getCachedContext(swscale_ctx, swscale_ctx = sws_getCachedContext(swscale_ctx,
in_frame->width, in_frame->height, format, in_frame->width, in_frame->height, format,
@ -120,7 +102,9 @@ int SWScale::Convert(
return -6; return -6;
} }
/* Do the conversion */ /* Do the conversion */
if ( !sws_scale(swscale_ctx, in_frame->data, in_frame->linesize, 0, in_frame->height, out_frame->data, out_frame->linesize ) ) { if (!sws_scale(swscale_ctx,
in_frame->data, in_frame->linesize, 0, in_frame->height,
out_frame->data, out_frame->linesize)) {
Error("swscale conversion failed"); Error("swscale conversion failed");
return -10; return -10;
} }
@ -140,8 +124,9 @@ int SWScale::Convert(
unsigned int new_width, unsigned int new_width,
unsigned int new_height unsigned int new_height
) { ) {
Debug(1, "Convert: in_buffer %p in_buffer_size %d out_buffer %p size %d width %d height %d width %d height %d", Debug(1, "Convert: in_buffer %p in_buffer_size %d out_buffer %p size %d width %d height %d width %d height %d %d %d",
in_buffer, in_buffer_size, out_buffer, out_buffer_size, width, height, new_width, new_height); in_buffer, in_buffer_size, out_buffer, out_buffer_size, width, height, new_width, new_height,
in_pf, out_pf);
/* Parameter checking */ /* Parameter checking */
if ( in_buffer == nullptr ) { if ( in_buffer == nullptr ) {
Error("NULL Input buffer"); Error("NULL Input buffer");
@ -160,23 +145,7 @@ int SWScale::Convert(
return -3; return -3;
} }
// THe J formats are deprecated, so we need to convert in_pf = fix_deprecated_pix_fmt(in_pf);
switch ( in_pf ) {
case AV_PIX_FMT_YUVJ420P :
in_pf = AV_PIX_FMT_YUV420P;
break;
case AV_PIX_FMT_YUVJ422P :
in_pf = AV_PIX_FMT_YUV422P;
break;
case AV_PIX_FMT_YUVJ444P :
in_pf = AV_PIX_FMT_YUV444P;
break;
case AV_PIX_FMT_YUVJ440P :
in_pf = AV_PIX_FMT_YUV440P;
break;
default:
break;
}
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
/* Warn if the input or output pixelformat is not supported */ /* Warn if the input or output pixelformat is not supported */
@ -190,21 +159,24 @@ int SWScale::Convert(
} }
#endif #endif
int alignment = 1;
/* Check the buffer sizes */ /* Check the buffer sizes */
size_t insize = GetBufferSize(in_pf, width, height); size_t needed_insize = GetBufferSize(in_pf, width, height);
if ( insize != in_buffer_size ) { if ( needed_insize > in_buffer_size ) {
Debug(1, "The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); Debug(1, "The input buffer size does not match the expected size for the input format. Required: %d for %dx%d %d Available: %d",
needed_insize, width, height, in_pf, in_buffer_size);
} }
size_t outsize = GetBufferSize(out_pf, new_width, new_height); size_t needed_outsize = GetBufferSize(out_pf, new_width, new_height);
if ( outsize < out_buffer_size ) { if ( needed_outsize > out_buffer_size ) {
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); Error("The output buffer is undersized for the output format. Required: %d Available: %d", needed_outsize, out_buffer_size);
return -5; return -5;
} }
/* Get the context */ /* Get the context */
swscale_ctx = sws_getCachedContext( swscale_ctx = sws_getCachedContext(swscale_ctx,
swscale_ctx, width, height, in_pf, new_width, new_height, width, height, in_pf,
out_pf, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); new_width, new_height, out_pf,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
if ( swscale_ctx == nullptr ) { if ( swscale_ctx == nullptr ) {
Error("Failed getting swscale context"); Error("Failed getting swscale context");
return -6; return -6;
@ -212,8 +184,8 @@ int SWScale::Convert(
/* Fill in the buffers */ /* Fill in the buffers */
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
if ( av_image_fill_arrays(input_avframe->data, input_avframe->linesize, if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize,
(uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) { (uint8_t*) in_buffer, in_pf, width, height, alignment) <= 0) {
#else #else
if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer, if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer,
in_pf, width, height) <= 0) { in_pf, width, height) <= 0) {
@ -222,10 +194,10 @@ int SWScale::Convert(
return -7; return -7;
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
if ( av_image_fill_arrays(output_avframe->data, output_avframe->linesize, if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize,
out_buffer, out_pf, new_width, new_height, 1) <= 0) { out_buffer, out_pf, new_width, new_height, alignment) <= 0) {
#else #else
if ( avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, new_width, if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, new_width,
new_height) <= 0) { new_height) <= 0) {
#endif #endif
Error("Failed filling output frame with output buffer"); Error("Failed filling output frame with output buffer");
@ -235,7 +207,8 @@ int SWScale::Convert(
/* Do the conversion */ /* Do the conversion */
if ( !sws_scale(swscale_ctx, if ( !sws_scale(swscale_ctx,
input_avframe->data, input_avframe->linesize, input_avframe->data, input_avframe->linesize,
0, height, output_avframe->data, output_avframe->linesize ) ) { 0, height,
output_avframe->data, output_avframe->linesize) ) {
Error("swscale conversion failed"); Error("swscale conversion failed");
return -10; return -10;
} }
@ -298,7 +271,7 @@ int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_si
size_t SWScale::GetBufferSize(enum _AVPIXELFORMAT pf, unsigned int width, unsigned int height) { size_t SWScale::GetBufferSize(enum _AVPIXELFORMAT pf, unsigned int width, unsigned int height) {
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
return av_image_get_buffer_size(pf, width, height, 32); return av_image_get_buffer_size(pf, width, height, 1);
#else #else
return outsize = avpicture_get_size(pf, width,height); return outsize = avpicture_get_size(pf, width,height);
#endif #endif

View File

@ -21,6 +21,7 @@
#include "zm_logger.h" #include "zm_logger.h"
#include "zm_utils.h" #include "zm_utils.h"
#include <cerrno>
#include <cstring> #include <cstring>
#include <csignal> #include <csignal>
#include <sys/time.h> #include <sys/time.h>

View File

@ -190,12 +190,8 @@ bool VideoStore::open() {
#endif #endif
} }
video_out_ctx->time_base = video_in_ctx ? video_in_ctx->time_base : AV_TIME_BASE_Q; // When encoding, we are going to use the timestamp values instead of packet pts/dts
if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) {
Debug(2, "No timebase found in video in context, defaulting to Q which is microseconds");
video_out_ctx->time_base = AV_TIME_BASE_Q; video_out_ctx->time_base = AV_TIME_BASE_Q;
}
video_out_ctx->codec_id = codec_data[i].codec_id; video_out_ctx->codec_id = codec_data[i].codec_id;
video_out_ctx->pix_fmt = codec_data[i].pix_fmt; video_out_ctx->pix_fmt = codec_data[i].pix_fmt;
video_out_ctx->level = 32; video_out_ctx->level = 32;
@ -209,14 +205,6 @@ bool VideoStore::open() {
video_out_ctx->bit_rate = 2000000; video_out_ctx->bit_rate = 2000000;
video_out_ctx->gop_size = 12; video_out_ctx->gop_size = 12;
video_out_ctx->max_b_frames = 1; video_out_ctx->max_b_frames = 1;
ret = av_opt_set(video_out_ctx, "crf", "36", AV_OPT_SEARCH_CHILDREN);
if ( ret < 0 ) {
Error("Could not set 'crf' for output codec %s. %s",
codec_data[i].codec_name,
av_make_error_string(ret).c_str()
);
}
} else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) { } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) {
/* just for testing, we also add B frames */ /* just for testing, we also add B frames */
video_out_ctx->max_b_frames = 2; video_out_ctx->max_b_frames = 2;
@ -241,10 +229,17 @@ bool VideoStore::open() {
} }
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) {
if ( wanted_encoder != "" and wanted_encoder != "auto" ) {
Warning("Can't open video codec (%s) %s", Warning("Can't open video codec (%s) %s",
video_out_codec->name, video_out_codec->name,
av_make_error_string(ret).c_str() av_make_error_string(ret).c_str()
); );
} else {
Debug(1, "Can't open video codec (%s) %s",
video_out_codec->name,
av_make_error_string(ret).c_str()
);
}
video_out_codec = nullptr; video_out_codec = nullptr;
} }
@ -254,6 +249,7 @@ bool VideoStore::open() {
} }
//av_dict_free(&opts); //av_dict_free(&opts);
if ( video_out_codec ) break; if ( video_out_codec ) break;
avcodec_free_context(&video_out_ctx);
} // end foreach codec } // end foreach codec
if ( !video_out_codec ) { if ( !video_out_codec ) {
@ -1015,21 +1011,25 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
//zm_packet->out_frame->sample_aspect_ratio = (AVRational){ 0, 1 }; //zm_packet->out_frame->sample_aspect_ratio = (AVRational){ 0, 1 };
// Do this to allow the encoder to choose whether to use I/P/B frame // Do this to allow the encoder to choose whether to use I/P/B frame
//zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE; //zm_packet->out_frame->pict_type = AV_PICTURE_TYPE_NONE;
zm_packet->out_frame->key_frame = zm_packet->keyframe; //zm_packet->out_frame->key_frame = zm_packet->keyframe;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
zm_packet->out_frame->pkt_duration = 0; zm_packet->out_frame->pkt_duration = 0;
#endif #endif
int64_t in_pts = zm_packet->timestamp->tv_sec * (uint64_t)1000000 + zm_packet->timestamp->tv_usec;
if ( !video_first_pts ) { if ( !video_first_pts ) {
video_first_pts = zm_packet->timestamp->tv_sec * (uint64_t)1000000 + zm_packet->timestamp->tv_usec; video_first_pts = in_pts;
Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%d) usecs(%d)", Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%d) usecs(%d)",
video_first_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec); video_first_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec);
zm_packet->out_frame->pts = 0; zm_packet->out_frame->pts = 0;
} else { } else {
uint64_t useconds = ( zm_packet->timestamp->tv_sec * (uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_first_pts; uint64_t useconds = in_pts - video_first_pts;
zm_packet->out_frame->pts = av_rescale_q(useconds, video_in_stream->time_base, video_out_ctx->time_base); zm_packet->out_frame->pts = av_rescale_q(useconds, AV_TIME_BASE_Q, video_out_ctx->time_base);
Debug(2, " Setting pts for frame(%d) to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", Debug(2, " Setting pts for frame(%d) to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d) @ %d/%d",
frame_count, zm_packet->out_frame->pts, video_first_pts, useconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec); frame_count, zm_packet->out_frame->pts, video_first_pts, useconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec,
video_out_ctx->time_base.num,
video_out_ctx->time_base.den
);
} }
av_init_packet(&opkt); av_init_packet(&opkt);
@ -1097,7 +1097,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) {
} else { // Passthrough } else { // Passthrough
AVPacket *ipkt = &zm_packet->packet; AVPacket *ipkt = &zm_packet->packet;
Debug(3, "Doing passthrough, just copy packet"); ZM_DUMP_STREAM_PACKET(video_in_stream, (*ipkt), "Doing passthrough, just copy packet");
// Just copy it because the codec is the same // Just copy it because the codec is the same
av_init_packet(&opkt); av_init_packet(&opkt);
opkt.data = ipkt->data; opkt.data = ipkt->data;
@ -1149,10 +1149,8 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
audio_next_pts = audio_out_ctx->frame_size; audio_next_pts = audio_out_ctx->frame_size;
} }
Debug(3, "audio first_dts to %" PRId64, audio_first_dts);
// Need to adjust pts before feeding to decoder.... should really copy the pkt instead of modifying it // Need to adjust pts before feeding to decoder.... should really copy the pkt instead of modifying it
ipkt->pts -= audio_first_dts;
ipkt->dts -= audio_first_dts;
ZM_DUMP_STREAM_PACKET(audio_in_stream, (*ipkt), "after pts adjustment");
if ( audio_out_codec ) { if ( audio_out_codec ) {
// I wonder if we can get multiple frames per packet? Probably // I wonder if we can get multiple frames per packet? Probably
@ -1206,8 +1204,10 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
opkt.flags = ipkt->flags; opkt.flags = ipkt->flags;
opkt.duration = ipkt->duration; opkt.duration = ipkt->duration;
opkt.pts = ipkt->pts; opkt.pts = ipkt->pts - audio_first_dts;
opkt.dts = ipkt->dts; opkt.dts = ipkt->dts - audio_first_dts;
ZM_DUMP_STREAM_PACKET(audio_in_stream, (*ipkt), "after pts adjustment");
av_packet_rescale_ts(&opkt, audio_in_stream->time_base, audio_out_stream->time_base); av_packet_rescale_ts(&opkt, audio_in_stream->time_base, audio_out_stream->time_base);
ZM_DUMP_STREAM_PACKET(audio_out_stream, opkt, "after stream pts adjustment"); ZM_DUMP_STREAM_PACKET(audio_out_stream, opkt, "after stream pts adjustment");
write_packet(&opkt, audio_out_stream); write_packet(&opkt, audio_out_stream);

View File

@ -134,16 +134,11 @@ Zone::~Zone() {
void Zone::RecordStats(const Event *event) { void Zone::RecordStats(const Event *event) {
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
db_mutex.lock();
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", "INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d",
monitor->Id(), id, event->Id(), event->Frames(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score monitor->Id(), id, event->Id(), event->Frames(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score
); );
int rc = mysql_query(&dbconn, sql); zmDbDo(sql);
db_mutex.unlock();
if ( rc ) {
Error("Can't insert event stats: %s", mysql_error(&dbconn));
}
} // end void Zone::RecordStats( const Event *event ) } // end void Zone::RecordStats( const Event *event )
bool Zone::CheckOverloadCount() { bool Zone::CheckOverloadCount() {
@ -824,8 +819,10 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P
int Zone::Load(Monitor *monitor, Zone **&zones) { int Zone::Load(Monitor *monitor, Zone **&zones) {
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
MYSQL_RES *result;
db_mutex.lock(); { // scope for lock
std::lock_guard<std::mutex> lck(db_mutex);
snprintf(sql, sizeof(sql), "SELECT Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0," snprintf(sql, sizeof(sql), "SELECT Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,"
"MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels," "MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,"
"FilterX,FilterY,MinFilterPixels,MaxFilterPixels," "FilterX,FilterY,MinFilterPixels,MaxFilterPixels,"
@ -834,13 +831,12 @@ int Zone::Load(Monitor *monitor, Zone **&zones) {
" FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id()); " FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id());
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
db_mutex.unlock();
return 0; return 0;
} }
MYSQL_RES *result = mysql_store_result(&dbconn); result = mysql_store_result(&dbconn);
db_mutex.unlock(); }
if ( !result ) { if (!result) {
Error("Can't use query result: %s", mysql_error(&dbconn)); Error("Can't use query result: %s", mysql_error(&dbconn));
return 0; return 0;
} }
@ -848,7 +844,7 @@ int Zone::Load(Monitor *monitor, Zone **&zones) {
Debug(1, "Got %d zones for monitor %s", n_zones, monitor->Name()); Debug(1, "Got %d zones for monitor %s", n_zones, monitor->Name());
delete[] zones; delete[] zones;
zones = new Zone *[n_zones]; zones = new Zone *[n_zones];
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { for(int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
int col = 0; int col = 0;
int Id = atoi(dbrow[col++]); int Id = atoi(dbrow[col++]);

View File

@ -214,7 +214,6 @@ int main(int argc, char *argv[]) {
exit(-1); exit(-1);
} else { } else {
Debug(2, "%d monitors loaded", monitors.size()); Debug(2, "%d monitors loaded", monitors.size());
} }
Info("Starting Capture version %s", ZM_VERSION); Info("Starting Capture version %s", ZM_VERSION);
@ -230,10 +229,9 @@ int main(int argc, char *argv[]) {
sigaddset(&block_set, SIGUSR2); sigaddset(&block_set, SIGUSR2);
int result = 0; int result = 0;
int prime_capture_log_count = 0; int prime_capture_log_count = 0;
while ( !zm_terminate ) { while (!zm_terminate) {
result = 0; result = 0;
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
@ -247,37 +245,29 @@ int main(int argc, char *argv[]) {
monitor->setStartupTime(now); monitor->setStartupTime(now);
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,Status,CaptureFPS,AnalysisFPS) VALUES (%d, 'Running',0,0) ON DUPLICATE KEY UPDATE Status='Running',CaptureFPS=0,AnalysisFPS=0", "INSERT INTO Monitor_Status (MonitorId,Status,CaptureFPS,AnalysisFPS)"
" VALUES (%d, 'Running',0,0) ON DUPLICATE KEY UPDATE Status='Running',CaptureFPS=0,AnalysisFPS=0",
monitor->Id()); monitor->Id());
if (mysql_query(&dbconn, sql)) { zmDbDo(sql);
Error("Can't run query: %s", mysql_error(&dbconn));
}
}
for (const std::shared_ptr<Monitor> &monitor : monitors) { while (monitor->PrimeCapture() <= 0) {
// Outer primary loop, handles connection to camera
if (monitor->PrimeCapture() <= 0) {
if (prime_capture_log_count % 60) { if (prime_capture_log_count % 60) {
Error("Failed to prime capture of initial monitor"); Error("Failed to prime capture of initial monitor");
} else { } else {
Debug(1, "Failed to prime capture of initial monitor"); Debug(1, "Failed to prime capture of initial monitor");
} }
prime_capture_log_count ++; prime_capture_log_count ++;
monitor->disconnect(); if (zm_terminate) break;
if (!zm_terminate) { sleep(1);
Debug(1, "Sleeping");
sleep(5);
}
continue;
} }
if (zm_terminate) break;
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'", "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'",
monitor->Id()); monitor->Id());
if ( mysql_query(&dbconn, sql) ) { zmDbDo(sql);
Error("Can't run query: %s", mysql_error(&dbconn)); } // end foreach monitor
} if (zm_terminate) break;
}
#if HAVE_RTSP_SERVER #if HAVE_RTSP_SERVER
RTSPServerThread ** rtsp_server_threads = nullptr; RTSPServerThread ** rtsp_server_threads = nullptr;
@ -303,12 +293,12 @@ int main(int argc, char *argv[]) {
capture_delays[i], alarm_capture_delays[i]); capture_delays[i], alarm_capture_delays[i]);
Monitor::Function function = monitors[0]->GetFunction(); Monitor::Function function = monitors[0]->GetFunction();
if ( function != Monitor::MONITOR ) { if (function != Monitor::MONITOR) {
Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id()); Debug(1, "Starting an analysis thread for monitor (%d)", monitors[i]->Id());
analysis_threads.emplace_back(ZM::make_unique<AnalysisThread>(monitors[i])); analysis_threads.emplace_back(ZM::make_unique<AnalysisThread>(monitors[i]));
} }
#if HAVE_RTSP_SERVER #if HAVE_RTSP_SERVER
if ( rtsp_server_threads ) { if (rtsp_server_threads) {
rtsp_server_threads[i] = new RTSPServerThread(monitors[i]); rtsp_server_threads[i] = new RTSPServerThread(monitors[i]);
rtsp_server_threads[i]->addStream(monitors[i]->GetVideoStream(), monitors[i]->GetAudioStream()); rtsp_server_threads[i]->addStream(monitors[i]->GetVideoStream(), monitors[i]->GetAudioStream());
rtsp_server_threads[i]->start(); rtsp_server_threads[i]->start();
@ -366,17 +356,12 @@ int main(int argc, char *argv[]) {
} }
} // 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 foreach n_monitors } // end foreach n_monitors
if (result < 0) { if (result < 0 or zm_reload) {
// Failure, try reconnecting // Failure, try reconnecting
break; break;
} }
if ( zm_reload ) {
break;
}
} // end while ! zm_terminate and connected } // end while ! zm_terminate and connected
for (std::unique_ptr<AnalysisThread> &analysis_thread: analysis_threads) for (std::unique_ptr<AnalysisThread> &analysis_thread: analysis_threads)
@ -413,7 +398,7 @@ int main(int argc, char *argv[]) {
delete [] capture_delays; delete [] capture_delays;
delete [] last_capture_times; delete [] last_capture_times;
if (result < 0) { if (result < 0 and !zm_terminate) {
// Failure, try reconnecting // Failure, try reconnecting
Debug(1, "Sleeping for 5"); Debug(1, "Sleeping for 5");
sleep(5); sleep(5);
@ -435,9 +420,7 @@ int main(int argc, char *argv[]) {
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, 'Connected') ON DUPLICATE KEY UPDATE Status='NotRunning'",
monitor->Id()); monitor->Id());
if (mysql_query(&dbconn, sql)) { zmDbDo(sql);
Error("Can't run query: %s", mysql_error(&dbconn));
}
} }
Image::Deinitialise(); Image::Deinitialise();

@ -1 +1 @@
Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938

View File

@ -349,6 +349,8 @@ class Filter extends ZM_Object {
'![]' => 2, '![]' => 2,
'and' => 3, 'and' => 3,
'or' => 4, 'or' => 4,
'IS' => 2,
'IS NOT' => 2,
); );
for ( $i = 0; $i < count($terms); $i++ ) { for ( $i = 0; $i < count($terms); $i++ ) {
@ -476,6 +478,7 @@ class Filter extends ZM_Object {
} }
} # end if attr } # end if attr
$sqlValue = '';
if ( isset($term['op']) ) { if ( isset($term['op']) ) {
if ( empty($term['op']) ) { if ( empty($term['op']) ) {
$term['op'] = '='; $term['op'] = '=';
@ -507,11 +510,11 @@ class Filter extends ZM_Object {
case 'IS' : case 'IS' :
case 'IS NOT' : case 'IS NOT' :
if ( $term['val'] == 'Odd' ) { if ( $term['val'] == 'Odd' ) {
$sqlValue .= ' % 2 = 1'; $sqlValue = ' % 2 = 1';
} else if ( $term['val'] == 'Even' ) { } else if ( $term['val'] == 'Even' ) {
$sqlValue .= ' % 2 = 0'; $sqlValue = ' % 2 = 0';
} else { } else {
$sqlValue .= ' '.$term['op']; $sqlValue = ' '.$term['op'];
} }
break; break;
default : default :
@ -522,10 +525,10 @@ class Filter extends ZM_Object {
if ( !count($postfixStack) ) { if ( !count($postfixStack) ) {
$postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue);
break; break;
} elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { } else if ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) {
$postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue);
break; break;
} elseif ( $priorities[$term['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { } else if ( $priorities[$term['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
$postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue ); $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue );
break; break;
} else { } else {
@ -537,6 +540,7 @@ class Filter extends ZM_Object {
if ( isset($term['val']) ) { if ( isset($term['val']) ) {
$valueList = array(); $valueList = array();
foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) {
$value_upper = strtoupper($value);
switch ( $term['attr'] ) { switch ( $term['attr'] ) {
case 'MonitorName': case 'MonitorName':
case 'Name': case 'Name':
@ -551,9 +555,9 @@ class Filter extends ZM_Object {
case 'FilterServerId': case 'FilterServerId':
case 'StorageServerId': case 'StorageServerId':
case 'ServerId': case 'ServerId':
if ( $value == 'ZM_SERVER_ID' ) { if ( $value_upper == 'ZM_SERVER_ID' ) {
$value = ZM_SERVER_ID; $value = ZM_SERVER_ID;
} else if ( $value == 'NULL' ) { } else if ( $value_upper == 'NULL' ) {
} else { } else {
$value = dbEscape($value); $value = dbEscape($value);
@ -567,6 +571,7 @@ class Filter extends ZM_Object {
case 'DateTime': case 'DateTime':
case 'EndDateTime': case 'EndDateTime':
case 'StartDateTime': case 'StartDateTime':
if ( $value_upper != 'NULL' )
$value = "'".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."'"; $value = "'".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."'";
break; break;
case 'Date': case 'Date':
@ -580,7 +585,7 @@ class Filter extends ZM_Object {
$value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')';
break; break;
default : default :
if ( $value != 'NULL' ) if ( $value_upper != 'NULL' )
$value = dbEscape($value); $value = dbEscape($value);
} // end switch attribute } // end switch attribute
$valueList[] = $value; $valueList[] = $value;

View File

@ -68,7 +68,7 @@ class FilterTerm {
$vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val));
foreach ( $vals as $value ) { foreach ( $vals as $value ) {
$value_upper = strtoupper($value);
switch ( $this->attr ) { switch ( $this->attr ) {
case 'AlarmedZoneId': case 'AlarmedZoneId':
@ -96,36 +96,36 @@ class FilterTerm {
case 'ServerId': case 'ServerId':
if ( $value == 'ZM_SERVER_ID' ) { if ( $value == 'ZM_SERVER_ID' ) {
$value = ZM_SERVER_ID; $value = ZM_SERVER_ID;
} else if ( $value == 'NULL' ) { } else if ( $value_upper == 'NULL' ) {
} else { } else {
$value = dbEscape($value); $value = dbEscape($value);
} }
break; break;
case 'StorageId': case 'StorageId':
if ( $value != 'NULL' ) { if ( $value_upper != 'NULL' ) {
$value = dbEscape($value); $value = dbEscape($value);
} }
break; break;
case 'DateTime': case 'DateTime':
case 'StartDateTime': case 'StartDateTime':
case 'EndDateTime': case 'EndDateTime':
if ( $value != 'NULL' ) if ( $value_upper != 'NULL' )
$value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\'';
break; break;
case 'Date': case 'Date':
case 'StartDate': case 'StartDate':
case 'EndDate': case 'EndDate':
if ( $value == 'CURDATE()' or $value == 'NOW()' ) { if ( $value_upper == 'CURDATE()' or $value_upper == 'NOW()' ) {
$value = 'to_days('.$value.')'; $value = 'to_days('.$value.')';
} else if ( $value != 'NULL' ) { } else if ( $value_upper != 'NULL' ) {
$value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')';
} }
break; break;
case 'Time': case 'Time':
case 'StartTime': case 'StartTime':
case 'EndTime': case 'EndTime':
if ( $value != 'NULL' ) if ( $value_upper != 'NULL' )
$value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')';
break; break;
default : default :
@ -133,7 +133,7 @@ class FilterTerm {
$value = 1; $value = 1;
} else if ( $value == 'Even' ) { } else if ( $value == 'Even' ) {
$value = 0; $value = 0;
} else if ( $value != 'NULL' ) } else if ( $value_upper != 'NULL' )
$value = dbEscape($value); $value = dbEscape($value);
break; break;
} }

View File

@ -132,19 +132,7 @@ class Storage extends ZM_Object {
public function event_disk_space() { public function event_disk_space() {
# This isn't a function like this in php, so we have to add up the space used in each event. # This isn't a function like this in php, so we have to add up the space used in each event.
if ( (! property_exists($this, 'DiskSpace')) or (!isset($this->{'DiskSpace'})) ) { if ( (! property_exists($this, 'DiskSpace')) or (!isset($this->{'DiskSpace'})) ) {
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id())); $this->{'DiskSpace'} = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()));
do {
# Do in batches of 1000 so as to not useup all ram, Event will do caching though...
$events = Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null), array('limit'=>1000));
foreach ( $events as $Event ) {
$Event->Storage($this); // Prevent further db hit
# DiskSpace will update the event
$used += $Event->DiskSpace();
} #end foreach
Event::clear_cache();
} while ( count($events) == 1000 );
$this->{'DiskSpace'} = $used;
} }
return $this->{'DiskSpace'}; return $this->{'DiskSpace'};
} // end function event_disk_space } // end function event_disk_space

View File

@ -867,6 +867,7 @@ function xhtmlFooter() {
<script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.min.js"></script> <script src="skins/<?php echo $skin; ?>/js/jquery-ui-1.12.1/jquery-ui.min.js"></script>
<script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script> <script src="skins/<?php echo $skin; ?>/js/bootstrap.min.js"></script>
<?php echo output_script_if_exists(array( <?php echo output_script_if_exists(array(
'js/tableExport.min.js',
'js/bootstrap-table.min.js', 'js/bootstrap-table.min.js',
'js/bootstrap-table-locale-all.min.js', 'js/bootstrap-table-locale-all.min.js',
'js/bootstrap-table-export.min.js', 'js/bootstrap-table-export.min.js',

94
web/skins/classic/js/tableExport.min.js vendored Normal file
View File

@ -0,0 +1,94 @@
/*
tableExport.jquery.plugin
Version 1.10.21
Copyright (c) 2015-2020 hhurz, https://github.com/hhurz/tableExport.jquery.plugin
Based on https://github.com/kayalshri/tableExport.jquery.plugin
Licensed under the MIT License
*/
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(d,k,y){d instanceof String&&(d=String(d));for(var C=d.length,v=0;v<C;v++){var R=d[v];if(k.call(y,R,v,d))return{i:v,v:R}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(d,k,y){d!=Array.prototype&&d!=Object.prototype&&(d[k]=y.value)};
$jscomp.getGlobal=function(d){return"undefined"!=typeof window&&window===d?d:"undefined"!=typeof global&&null!=global?global:d};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(d,k,y,C){if(k){y=$jscomp.global;d=d.split(".");for(C=0;C<d.length-1;C++){var v=d[C];v in y||(y[v]={});y=y[v]}d=d[d.length-1];C=y[d];k=k(C);k!=C&&null!=k&&$jscomp.defineProperty(y,d,{configurable:!0,writable:!0,value:k})}};
$jscomp.polyfill("Array.prototype.find",function(d){return d?d:function(d,y){return $jscomp.findInternal(this,d,y).v}},"es6","es3");
(function(d){d.fn.tableExport=function(k){function y(b){var c=[];v(b,"thead").each(function(){c.push.apply(c,v(d(this),a.theadSelector).toArray())});return c}function C(b){var c=[];v(b,"tbody").each(function(){c.push.apply(c,v(d(this),a.tbodySelector).toArray())});a.tfootSelector.length&&v(b,"tfoot").each(function(){c.push.apply(c,v(d(this),a.tfootSelector).toArray())});return c}function v(b,a){var c=b[0].tagName,p=b.parents(c).length;return b.find(a).filter(function(){return p===d(this).closest(c).parents(c).length})}
function R(b){var a=[],e=0,p=0,f=0;d(b).find("thead").first().find("th").each(function(b,c){b=void 0!==d(c).attr("data-field");"undefined"!==typeof c.parentNode.rowIndex&&p!==c.parentNode.rowIndex&&(p=c.parentNode.rowIndex,e=f=0);var l=J(c);for(e+=l?l:1;f<e;)a[f]=b?d(c).attr("data-field"):f.toString(),f++});return a}function I(b){var a="undefined"!==typeof b[0].rowIndex,e=!1===a&&"undefined"!==typeof b[0].cellIndex,p=e||a?Ja(b):b.is(":visible"),f=b.attr("data-tableexport-display");e&&"none"!==f&&
"always"!==f&&(b=d(b[0].parentNode),a="undefined"!==typeof b[0].rowIndex,f=b.attr("data-tableexport-display"));a&&"none"!==f&&"always"!==f&&(f=b.closest("table").attr("data-tableexport-display"));return"none"!==f&&(!0===p||"always"===f)}function Ja(b){var a=[];U&&(a=K.filter(function(){var a=!1;this.nodeType===b[0].nodeType&&("undefined"!==typeof this.rowIndex&&this.rowIndex===b[0].rowIndex?a=!0:"undefined"!==typeof this.cellIndex&&this.cellIndex===b[0].cellIndex&&"undefined"!==typeof this.parentNode.rowIndex&&
"undefined"!==typeof b[0].parentNode.rowIndex&&this.parentNode.rowIndex===b[0].parentNode.rowIndex&&(a=!0));return a}));return!1===U||0===a.length}function ta(b,c,e){var p=!1;I(b)?0<a.ignoreColumn.length&&(-1!==d.inArray(e,a.ignoreColumn)||-1!==d.inArray(e-c,a.ignoreColumn)||S.length>e&&"undefined"!==typeof S[e]&&-1!==d.inArray(S[e],a.ignoreColumn))&&(p=!0):p=!0;return p}function E(b,c,e,p,f){if("function"===typeof f){var l=!1;"function"===typeof a.onIgnoreRow&&(l=a.onIgnoreRow(d(b),e));if(!1===l&&
(0===a.ignoreRow.length||-1===d.inArray(e,a.ignoreRow)&&-1===d.inArray(e-p,a.ignoreRow))&&I(d(b))){b=v(d(b),c);var q=b.length,h=0,u=0;b.each(function(){var b=d(this),a=J(this),c=T(this),l;d.each(G,function(){if(e>this.s.r&&e<=this.e.r&&h>=this.s.c&&h<=this.e.c)for(l=0;l<=this.e.c-this.s.c;++l)q++,u++,f(null,e,h++)});if(c||a)a=a||1,G.push({s:{r:e,c:h},e:{r:e+(c||1)-1,c:h+a-1}});!1===ta(b,q,u++)&&f(this,e,h++);if(1<a)for(l=0;l<a-1;++l)u++,f(null,e,h++)});d.each(G,function(){if(e>=this.s.r&&e<=this.e.r&&
h>=this.s.c&&h<=this.e.c)for(da=0;da<=this.e.c-this.s.c;++da)f(null,e,h++)})}}}function ua(b,a,e,d){if("undefined"!==typeof d.images&&(e=d.images[e],"undefined"!==typeof e)){a=a.getBoundingClientRect();var c=b.width/b.height,l=a.width/a.height,p=b.width,h=b.height,u=19.049976/25.4,g=0;l<=c?(h=Math.min(b.height,a.height),p=a.width*h/a.height):l>c&&(p=Math.min(b.width,a.width),h=a.height*p/a.width);p*=u;h*=u;h<b.height&&(g=(b.height-h)/2);try{d.doc.addImage(e.src,b.textPos.x,b.y+g,p,h)}catch(Pa){}b.textPos.x+=
p}}function va(b,c){if("string"===a.outputMode)return b.output();if("base64"===a.outputMode)return L(b.output());if("window"===a.outputMode)window.URL=window.URL||window.webkitURL,window.open(window.URL.createObjectURL(b.output("blob")));else try{var e=b.output("blob");saveAs(e,a.fileName+".pdf")}catch(p){ka(a.fileName+".pdf","data:application/pdf"+(c?"":";base64")+",",c?b.output("blob"):b.output())}}function wa(b,a,e){var c=0;"undefined"!==typeof e&&(c=e.colspan);if(0<=c){for(var d=b.width,l=b.textPos.x,
q=a.table.columns.indexOf(a.column),h=1;h<c;h++)d+=a.table.columns[q+h].width;1<c&&("right"===b.styles.halign?l=b.textPos.x+d-b.width:"center"===b.styles.halign&&(l=b.textPos.x+(d-b.width)/2));b.width=d;b.textPos.x=l;"undefined"!==typeof e&&1<e.rowspan&&(b.height*=e.rowspan);if("middle"===b.styles.valign||"bottom"===b.styles.valign)e=("string"===typeof b.text?b.text.split(/\r\n|\r|\n/g):b.text).length||1,2<e&&(b.textPos.y-=(2-1.15)/2*a.row.styles.fontSize*(e-2)/3);return!0}return!1}function xa(b,
a,e){"undefined"!==typeof b&&null!==b&&(b.hasAttribute("data-tableexport-canvas")?(a=(new Date).getTime(),d(b).attr("data-tableexport-canvas",a),e.images[a]={url:'[data-tableexport-canvas="'+a+'"]',src:null}):"undefined"!==a&&null!=a&&a.each(function(){if(d(this).is("img")){var a=ya(this.src);e.images[a]={url:this.src,src:this.src}}xa(b,d(this).children(),e)}))}function Ka(b,a){function c(b){if(b.url)if(b.src){var c=new Image;p=++f;c.crossOrigin="Anonymous";c.onerror=c.onload=function(){if(c.complete&&
(0===c.src.indexOf("data:image/")&&(c.width=b.width||c.width||0,c.height=b.height||c.height||0),c.width+c.height)){var e=document.createElement("canvas"),d=e.getContext("2d");e.width=c.width;e.height=c.height;d.drawImage(c,0,0);b.src=e.toDataURL("image/png")}--f||a(p)};c.src=b.url}else{var e=d(b.url);e.length&&(p=++f,html2canvas(e[0]).then(function(c){b.src=c.toDataURL("image/png");--f||a(p)}))}}var p=0,f=0;if("undefined"!==typeof b.images)for(var l in b.images)b.images.hasOwnProperty(l)&&c(b.images[l]);
(b=f)||(a(p),b=void 0);return b}function za(b,c,e){c.each(function(){if(d(this).is("div")){var c=ea(M(this,"background-color"),[255,255,255]),f=ea(M(this,"border-top-color"),[0,0,0]),l=fa(this,"border-top-width",a.jspdf.unit),q=this.getBoundingClientRect(),h=this.offsetLeft*e.wScaleFactor,u=this.offsetTop*e.hScaleFactor,g=q.width*e.wScaleFactor;q=q.height*e.hScaleFactor;e.doc.setDrawColor.apply(void 0,f);e.doc.setFillColor.apply(void 0,c);e.doc.setLineWidth(l);e.doc.rect(b.x+h,b.y+u,g,q,l?"FD":"F")}else d(this).is("img")&&
(c=ya(this.src),ua(b,this,c,e));za(b,d(this).children(),e)})}function Aa(b,c,e){if("function"===typeof e.onAutotableText)e.onAutotableText(e.doc,b,c);else{var p=b.textPos.x,f=b.textPos.y,l={halign:b.styles.halign,valign:b.styles.valign};if(c.length){for(c=c[0];c.previousSibling;)c=c.previousSibling;for(var q=!1,h=!1;c;){var u=c.innerText||c.textContent||"",g=u.length&&" "===u[0]?" ":"",k=1<u.length&&" "===u[u.length-1]?" ":"";!0!==a.preserve.leadingWS&&(u=g+la(u));!0!==a.preserve.trailingWS&&(u=ma(u)+
k);d(c).is("br")&&(p=b.textPos.x,f+=e.doc.internal.getFontSize());d(c).is("b")?q=!0:d(c).is("i")&&(h=!0);(q||h)&&e.doc.setFontType(q&&h?"bolditalic":q?"bold":"italic");if(g=e.doc.getStringUnitWidth(u)*e.doc.internal.getFontSize()){"linebreak"===b.styles.overflow&&p>b.textPos.x&&p+g>b.textPos.x+b.width&&(0<=".,!%*;:=-".indexOf(u.charAt(0))&&(k=u.charAt(0),g=e.doc.getStringUnitWidth(k)*e.doc.internal.getFontSize(),p+g<=b.textPos.x+b.width&&(e.doc.autoTableText(k,p,f,l),u=u.substring(1,u.length)),g=
e.doc.getStringUnitWidth(u)*e.doc.internal.getFontSize()),p=b.textPos.x,f+=e.doc.internal.getFontSize());if("visible"!==b.styles.overflow)for(;u.length&&p+g>b.textPos.x+b.width;)u=u.substring(0,u.length-1),g=e.doc.getStringUnitWidth(u)*e.doc.internal.getFontSize();e.doc.autoTableText(u,p,f,l);p+=g}if(q||h)d(c).is("b")?q=!1:d(c).is("i")&&(h=!1),e.doc.setFontType(q||h?q?"bold":"italic":"normal");c=c.nextSibling}b.textPos.x=p;b.textPos.y=f}else e.doc.autoTableText(b.text,b.textPos.x,b.textPos.y,l)}}
function V(b,a,e){return null==b?"":b.toString().replace(new RegExp(null==a?"":a.toString().replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),e)}function la(b){return null==b?"":b.toString().replace(/^\s+/,"")}function ma(b){return null==b?"":b.toString().replace(/\s+$/,"")}function La(b){if(0===a.date.html.length)return!1;a.date.pattern.lastIndex=0;var c=a.date.pattern.exec(b);if(null==c)return!1;b=+c[a.date.match_y];if(0>b||8099<b)return!1;var e=1*c[a.date.match_m];c=1*c[a.date.match_d];if(!isFinite(c))return!1;
var d=new Date(b,e-1,c,0,0,0);return d.getFullYear()===b&&d.getMonth()===e-1&&d.getDate()===c?new Date(Date.UTC(b,e-1,c,0,0,0)):!1}function na(b){b=b||"0";""!==a.numbers.html.thousandsSeparator&&(b=V(b,a.numbers.html.thousandsSeparator,""));"."!==a.numbers.html.decimalMark&&(b=V(b,a.numbers.html.decimalMark,"."));return"number"===typeof b||!1!==jQuery.isNumeric(b)?b:!1}function Ma(b){-1<b.indexOf("%")?(b=na(b.replace(/%/g,"")),!1!==b&&(b/=100)):b=!1;return b}function D(b,c,e,p){var f="",l="text";
if(null!==b){var q=d(b);q.removeData("teUserDefText");if(q[0].hasAttribute("data-tableexport-canvas"))var h="";else if(q[0].hasAttribute("data-tableexport-value"))h=(h=q.attr("data-tableexport-value"))?h+"":"",q.data("teUserDefText",1);else if(h=q.html(),"function"===typeof a.onCellHtmlData)h=a.onCellHtmlData(q,c,e,h),q.data("teUserDefText",1);else if(""!==h){b=d.parseHTML(h);var g=0,k=0;h="";d.each(b,function(){if(d(this).is("input"))h+=q.find("input").eq(g++).val();else if(d(this).is("select"))h+=
q.find("select option:selected").eq(k++).text();else if(d(this).is("br"))h+="<br>";else{if("undefined"===typeof d(this).html())h+=d(this).text();else if(void 0===jQuery().bootstrapTable||!1===d(this).hasClass("fht-cell")&&!1===d(this).hasClass("filterControl")&&0===q.parents(".detail-view").length)h+=d(this).html();if(d(this).is("a")){var b=q.find("a").attr("href")||"";f="function"===typeof a.onCellHtmlHyperlink?f+a.onCellHtmlHyperlink(q,c,e,b,h):"href"===a.htmlHyperlink?f+b:f+h;h=""}}})}if(h&&""!==
h&&!0===a.htmlContent)f=d.trim(h);else if(h&&""!==h)if(""!==q.attr("data-tableexport-cellformat")){var m=h.replace(/\n/g,"\u2028").replace(/(<\s*br([^>]*)>)/gi,"\u2060"),n=d("<div/>").html(m).contents();b=!1;m="";d.each(n.text().split("\u2028"),function(b,c){0<b&&(m+=" ");!0!==a.preserve.leadingWS&&(c=la(c));m+=!0!==a.preserve.trailingWS?ma(c):c});d.each(m.split("\u2060"),function(b,c){0<b&&(f+="\n");!0!==a.preserve.leadingWS&&(c=la(c));!0!==a.preserve.trailingWS&&(c=ma(c));f+=c.replace(/\u00AD/g,
"")});f=f.replace(/\u00A0/g," ");if("json"===a.type||"excel"===a.type&&"xmlss"===a.mso.fileFormat||!1===a.numbers.output)b=na(f),!1!==b&&(l="number",f=Number(b));else if(a.numbers.html.decimalMark!==a.numbers.output.decimalMark||a.numbers.html.thousandsSeparator!==a.numbers.output.thousandsSeparator)if(b=na(f),!1!==b){n=(""+b.substr(0>b?1:0)).split(".");1===n.length&&(n[1]="");var t=3<n[0].length?n[0].length%3:0;l="number";f=(0>b?"-":"")+(a.numbers.output.thousandsSeparator?(t?n[0].substr(0,t)+a.numbers.output.thousandsSeparator:
"")+n[0].substr(t).replace(/(\d{3})(?=\d)/g,"$1"+a.numbers.output.thousandsSeparator):n[0])+(n[1].length?a.numbers.output.decimalMark+n[1]:"")}}else f=h;!0===a.escape&&(f=escape(f));"function"===typeof a.onCellData&&(f=a.onCellData(q,c,e,f,l),q.data("teUserDefText",1))}void 0!==p&&(p.type=l);return f}function Ba(b){return 0<b.length&&!0===a.preventInjection&&0<="=+-@".indexOf(b.charAt(0))?"'"+b:b}function Na(b,a,e){return a+"-"+e.toLowerCase()}function ea(b,a){(b=/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(b))&&
(a=[parseInt(b[1]),parseInt(b[2]),parseInt(b[3])]);return a}function Ca(b){var a=M(b,"text-align"),e=M(b,"font-weight"),d=M(b,"font-style"),f="";"start"===a&&(a="rtl"===M(b,"direction")?"right":"left");700<=e&&(f="bold");"italic"===d&&(f+=d);""===f&&(f="normal");a={style:{align:a,bcolor:ea(M(b,"background-color"),[255,255,255]),color:ea(M(b,"color"),[0,0,0]),fstyle:f},colspan:J(b),rowspan:T(b)};null!==b&&(b=b.getBoundingClientRect(),a.rect={width:b.width,height:b.height});return a}function J(b){var a=
d(b).attr("data-tableexport-colspan");"undefined"===typeof a&&d(b).is("[colspan]")&&(a=d(b).attr("colspan"));return parseInt(a)||0}function T(b){var a=d(b).attr("data-tableexport-rowspan");"undefined"===typeof a&&d(b).is("[rowspan]")&&(a=d(b).attr("rowspan"));return parseInt(a)||0}function M(a,c){try{return window.getComputedStyle?(c=c.replace(/([a-z])([A-Z])/,Na),window.getComputedStyle(a,null).getPropertyValue(c)):a.currentStyle?a.currentStyle[c]:a.style[c]}catch(e){}return""}function fa(a,c,e){c=
M(a,c).match(/\d+/);if(null!==c){c=c[0];a=a.parentElement;var b=document.createElement("div");b.style.overflow="hidden";b.style.visibility="hidden";a.appendChild(b);b.style.width=100+e;e=100/b.offsetWidth;a.removeChild(b);return c*e}return 0}function Oa(a){for(var b=new ArrayBuffer(a.length),e=new Uint8Array(b),d=0;d!==a.length;++d)e[d]=a.charCodeAt(d)&255;return b}function oa(a){var b=a.c,e="";for(++b;b;b=Math.floor((b-1)/26))e=String.fromCharCode((b-1)%26+65)+e;return e+(""+(a.r+1))}function pa(a,
c){if("undefined"===typeof c||"number"===typeof c)return pa(a.s,a.e);"string"!==typeof a&&(a=oa(a));"string"!==typeof c&&(c=oa(c));return a===c?a:a+":"+c}function Da(a,c){var b=Number(a);if(isFinite(b))return b;var d=1;""!==c.thousandsSeparator&&(a=a.replace(new RegExp("([\\d])"+c.thousandsSeparator+"([\\d])","g"),"$1$2"));"."!==c.decimalMark&&(a=a.replace(new RegExp("([\\d])"+c.decimalMark+"([\\d])","g"),"$1.$2"));a=a.replace(/[$]/g,"").replace(/[%]/g,function(){d*=100;return""});if(isFinite(b=Number(a)))return b/
d;a=a.replace(/[(](.*)[)]/,function(a,b){d=-d;return b});return isFinite(b=Number(a))?b/d:b}function ya(a){var b=0,d;if(0===a.length)return b;var p=0;for(d=a.length;p<d;p++){var f=a.charCodeAt(p);b=(b<<5)-b+f;b|=0}return b}function N(b,c,d,p,f,l){var e=!0;"function"===typeof a.onBeforeSaveToFile&&(e=a.onBeforeSaveToFile(b,c,d,p,f),"boolean"!==typeof e&&(e=!0));if(e)try{if(Ea=new Blob([b],{type:d+";charset="+p}),saveAs(Ea,c,!1===l),"function"===typeof a.onAfterSaveToFile)a.onAfterSaveToFile(b,c)}catch(h){ka(c,
"data:"+d+(p.length?";charset="+p:"")+(f.length?";"+f:"")+",",l?"\ufeff"+b:b)}}function ka(b,c,d){var e=window.navigator.userAgent;if(!1!==b&&window.navigator.msSaveOrOpenBlob)window.navigator.msSaveOrOpenBlob(new Blob([d]),b);else if(!1!==b&&(0<e.indexOf("MSIE ")||e.match(/Trident.*rv\:11\./))){if(c=document.createElement("iframe")){document.body.appendChild(c);c.setAttribute("style","display:none");c.contentDocument.open("txt/plain","replace");c.contentDocument.write(d);c.contentDocument.close();
c.contentWindow.focus();switch(b.substr(b.lastIndexOf(".")+1)){case "doc":case "json":case "png":case "pdf":case "xls":case "xlsx":b+=".txt"}c.contentDocument.execCommand("SaveAs",!0,b);document.body.removeChild(c)}}else{var f=document.createElement("a");if(f){var l=null;f.style.display="none";!1!==b?f.download=b:f.target="_blank";"object"===typeof d?(window.URL=window.URL||window.webkitURL,e=[],e.push(d),l=window.URL.createObjectURL(new Blob(e,{type:c})),f.href=l):0<=c.toLowerCase().indexOf("base64,")?
f.href=c+L(d):f.href=c+encodeURIComponent(d);document.body.appendChild(f);if(document.createEvent)null===ha&&(ha=document.createEvent("MouseEvents")),ha.initEvent("click",!0,!1),f.dispatchEvent(ha);else if(document.createEventObject)f.fireEvent("onclick");else if("function"===typeof f.onclick)f.onclick();setTimeout(function(){l&&window.URL.revokeObjectURL(l);document.body.removeChild(f);if("function"===typeof a.onAfterSaveToFile)a.onAfterSaveToFile(d,b)},100)}}}function L(a){var b,d="",p=0;if("string"===
typeof a){a=a.replace(/\x0d\x0a/g,"\n");var f="";for(b=0;b<a.length;b++){var l=a.charCodeAt(b);128>l?f+=String.fromCharCode(l):(127<l&&2048>l?f+=String.fromCharCode(l>>6|192):(f+=String.fromCharCode(l>>12|224),f+=String.fromCharCode(l>>6&63|128)),f+=String.fromCharCode(l&63|128))}a=f}for(;p<a.length;){var q=a.charCodeAt(p++);f=a.charCodeAt(p++);b=a.charCodeAt(p++);l=q>>2;q=(q&3)<<4|f>>4;var h=(f&15)<<2|b>>6;var g=b&63;isNaN(f)?h=g=64:isNaN(b)&&(g=64);d=d+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(l)+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(q)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(h)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(g)}return d}var a={csvEnclosure:'"',csvSeparator:",",csvUseBOM:!0,date:{html:"dd/mm/yyyy"},displayTableName:!1,escape:!1,exportHiddenCells:!1,fileName:"tableExport",htmlContent:!1,htmlHyperlink:"content",ignoreColumn:[],ignoreRow:[],jsonScope:"all",jspdf:{orientation:"p",
unit:"pt",format:"a4",margins:{left:20,right:10,top:10,bottom:10},onDocCreated:null,autotable:{styles:{cellPadding:2,rowHeight:12,fontSize:8,fillColor:255,textColor:50,fontStyle:"normal",overflow:"ellipsize",halign:"inherit",valign:"middle"},headerStyles:{fillColor:[52,73,94],textColor:255,fontStyle:"bold",halign:"inherit",valign:"middle"},alternateRowStyles:{fillColor:245},tableExport:{doc:null,onAfterAutotable:null,onBeforeAutotable:null,onAutotableText:null,onTable:null,outputImages:!0}}},mso:{fileFormat:"xlshtml",
onMsoNumberFormat:null,pageFormat:"a4",pageOrientation:"portrait",rtl:!1,styles:[],worksheetName:"",xslx:{formatId:{date:14,numbers:2}}},numbers:{html:{decimalMark:".",thousandsSeparator:","},output:{decimalMark:".",thousandsSeparator:","}},onAfterSaveToFile:null,onBeforeSaveToFile:null,onCellData:null,onCellHtmlData:null,onCellHtmlHyperlink:null,onIgnoreRow:null,onTableExportBegin:null,onTableExportEnd:null,outputMode:"file",pdfmake:{enabled:!1,docDefinition:{pageOrientation:"portrait",defaultStyle:{font:"Roboto"}},
fonts:{}},preserve:{leadingWS:!1,trailingWS:!1},preventInjection:!0,sql:{tableEnclosure:"`",columnEnclosure:"`"},tbodySelector:"tr",tfootSelector:"tr",theadSelector:"tr",tableName:"Table",type:"csv"},O={a0:[2383.94,3370.39],a1:[1683.78,2383.94],a2:[1190.55,1683.78],a3:[841.89,1190.55],a4:[595.28,841.89],a5:[419.53,595.28],a6:[297.64,419.53],a7:[209.76,297.64],a8:[147.4,209.76],a9:[104.88,147.4],a10:[73.7,104.88],b0:[2834.65,4008.19],b1:[2004.09,2834.65],b2:[1417.32,2004.09],b3:[1000.63,1417.32],b4:[708.66,
1000.63],b5:[498.9,708.66],b6:[354.33,498.9],b7:[249.45,354.33],b8:[175.75,249.45],b9:[124.72,175.75],b10:[87.87,124.72],c0:[2599.37,3676.54],c1:[1836.85,2599.37],c2:[1298.27,1836.85],c3:[918.43,1298.27],c4:[649.13,918.43],c5:[459.21,649.13],c6:[323.15,459.21],c7:[229.61,323.15],c8:[161.57,229.61],c9:[113.39,161.57],c10:[79.37,113.39],dl:[311.81,623.62],letter:[612,792],"government-letter":[576,756],legal:[612,1008],"junior-legal":[576,360],ledger:[1224,792],tabloid:[792,1224],"credit-card":[153,
243]},B=this,ha=null,r=[],w=[],n=0,t="",S=[],G=[],Ea,K=[],U=!1;d.extend(!0,a,k);"xlsx"===a.type&&(a.mso.fileFormat=a.type,a.type="excel");"undefined"!==typeof a.excelFileFormat&&"undefined"===a.mso.fileFormat&&(a.mso.fileFormat=a.excelFileFormat);"undefined"!==typeof a.excelPageFormat&&"undefined"===a.mso.pageFormat&&(a.mso.pageFormat=a.excelPageFormat);"undefined"!==typeof a.excelPageOrientation&&"undefined"===a.mso.pageOrientation&&(a.mso.pageOrientation=a.excelPageOrientation);"undefined"!==typeof a.excelRTL&&
"undefined"===a.mso.rtl&&(a.mso.rtl=a.excelRTL);"undefined"!==typeof a.excelstyles&&"undefined"===a.mso.styles&&(a.mso.styles=a.excelstyles);"undefined"!==typeof a.onMsoNumberFormat&&"undefined"===a.mso.onMsoNumberFormat&&(a.mso.onMsoNumberFormat=a.onMsoNumberFormat);"undefined"!==typeof a.worksheetName&&"undefined"===a.mso.worksheetName&&(a.mso.worksheetName=a.worksheetName);a.mso.pageOrientation="l"===a.mso.pageOrientation.substr(0,1)?"landscape":"portrait";a.date.html=a.date.html||"";if(a.date.html.length){k=
[];k.dd="(3[01]|[12][0-9]|0?[1-9])";k.mm="(1[012]|0?[1-9])";k.yyyy="((?:1[6-9]|2[0-2])\\d{2})";k.yy="(\\d{2})";var z=a.date.html.match(/[^a-zA-Z0-9]/)[0];z=a.date.html.toLowerCase().split(z);a.date.regex="^\\s*";a.date.regex+=k[z[0]];a.date.regex+="(.)";a.date.regex+=k[z[1]];a.date.regex+="\\2";a.date.regex+=k[z[2]];a.date.regex+="\\s*$";a.date.pattern=new RegExp(a.date.regex,"g");k=z.indexOf("dd")+1;a.date.match_d=k+(1<k?1:0);k=z.indexOf("mm")+1;a.date.match_m=k+(1<k?1:0);k=(0<=z.indexOf("yyyy")?
z.indexOf("yyyy"):z.indexOf("yy"))+1;a.date.match_y=k+(1<k?1:0)}S=R(B);if("function"===typeof a.onTableExportBegin)a.onTableExportBegin();if("csv"===a.type||"tsv"===a.type||"txt"===a.type){var P="",Y=0;G=[];n=0;var qa=function(b,c,e){b.each(function(){t="";E(this,c,n,e+b.length,function(b,c,d){var e=t,f="";if(null!==b)if(b=D(b,c,d),c=null===b||""===b?"":b.toString(),"tsv"===a.type)b instanceof Date&&b.toLocaleString(),f=V(c,"\t"," ");else if(b instanceof Date)f=a.csvEnclosure+b.toLocaleString()+a.csvEnclosure;
else if(f=Ba(c),f=V(f,a.csvEnclosure,a.csvEnclosure+a.csvEnclosure),0<=f.indexOf(a.csvSeparator)||/[\r\n ]/g.test(f))f=a.csvEnclosure+f+a.csvEnclosure;t=e+(f+("tsv"===a.type?"\t":a.csvSeparator))});t=d.trim(t).substring(0,t.length-1);0<t.length&&(0<P.length&&(P+="\n"),P+=t);n++});return b.length};Y+=qa(d(B).find("thead").first().find(a.theadSelector),"th,td",Y);v(d(B),"tbody").each(function(){Y+=qa(v(d(this),a.tbodySelector),"td,th",Y)});a.tfootSelector.length&&qa(d(B).find("tfoot").first().find(a.tfootSelector),
"td,th",Y);P+="\n";if("string"===a.outputMode)return P;if("base64"===a.outputMode)return L(P);if("window"===a.outputMode){ka(!1,"data:text/"+("csv"===a.type?"csv":"plain")+";charset=utf-8,",P);return}N(P,a.fileName+"."+a.type,"text/"+("csv"===a.type?"csv":"plain"),"utf-8","","csv"===a.type&&a.csvUseBOM)}else if("sql"===a.type){n=0;G=[];var A="INSERT INTO "+a.sql.tableEnclosure+a.tableName+a.sql.tableEnclosure+" (";r=y(d(B));d(r).each(function(){E(this,"th,td",n,r.length,function(b,c,d){b=D(b,c,d)||
"";-1<b.indexOf(a.sql.columnEnclosure)&&(b=V(b.toString(),a.sql.columnEnclosure,a.sql.columnEnclosure+a.sql.columnEnclosure));A+=a.sql.columnEnclosure+b+a.sql.columnEnclosure+","});n++;A=d.trim(A).substring(0,A.length-1)});A+=") VALUES ";w=C(d(B));d(w).each(function(){t="";E(this,"td,th",n,r.length+w.length,function(a,c,d){a=D(a,c,d)||"";-1<a.indexOf("'")&&(a=V(a.toString(),"'","''"));t+="'"+a+"',"});3<t.length&&(A+="("+t,A=d.trim(A).substring(0,A.length-1),A+="),");n++});A=d.trim(A).substring(0,
A.length-1);A+=";";if("string"===a.outputMode)return A;if("base64"===a.outputMode)return L(A);N(A,a.fileName+".sql","application/sql","utf-8","",!1)}else if("json"===a.type){var W=[];G=[];r=y(d(B));d(r).each(function(){var a=[];E(this,"th,td",n,r.length,function(b,d,g){a.push(D(b,d,g))});W.push(a)});var ra=[];w=C(d(B));d(w).each(function(){var a={},c=0;E(this,"td,th",n,r.length+w.length,function(b,d,f){W.length?a[W[W.length-1][c]]=D(b,d,f):a[c]=D(b,d,f);c++});!1===d.isEmptyObject(a)&&ra.push(a);n++});
k="head"===a.jsonScope?JSON.stringify(W):"data"===a.jsonScope?JSON.stringify(ra):JSON.stringify({header:W,data:ra});if("string"===a.outputMode)return k;if("base64"===a.outputMode)return L(k);N(k,a.fileName+".json","application/json","utf-8","base64",!1)}else if("xml"===a.type){n=0;G=[];var Q='<?xml version="1.0" encoding="utf-8"?>';Q+="<tabledata><fields>";r=y(d(B));d(r).each(function(){E(this,"th,td",n,r.length,function(a,d,e){Q+="<field>"+D(a,d,e)+"</field>"});n++});Q+="</fields><data>";var Fa=
1;w=C(d(B));d(w).each(function(){var a=1;t="";E(this,"td,th",n,r.length+w.length,function(b,d,g){t+="<column-"+a+">"+D(b,d,g)+"</column-"+a+">";a++});0<t.length&&"<column-1></column-1>"!==t&&(Q+='<row id="'+Fa+'">'+t+"</row>",Fa++);n++});Q+="</data></tabledata>";if("string"===a.outputMode)return Q;if("base64"===a.outputMode)return L(Q);N(Q,a.fileName+".xml","application/xml","utf-8","base64",!1)}else if("excel"===a.type&&"xmlss"===a.mso.fileFormat){var sa=[],F=[];d(B).filter(function(){return I(d(this))}).each(function(){function b(a,
b,c){var e=[];d(a).each(function(){var b=0,f=0;t="";E(this,"td,th",n,c+a.length,function(a,c,l){if(null!==a){var h="";c=D(a,c,l);l="String";if(!1!==jQuery.isNumeric(c))l="Number";else{var g=Ma(c);!1!==g&&(c=g,l="Number",h+=' ss:StyleID="pct1"')}"Number"!==l&&(c=c.replace(/\n/g,"<br>"));g=J(a);a=T(a);d.each(e,function(){if(n>=this.s.r&&n<=this.e.r&&f>=this.s.c&&f<=this.e.c)for(var a=0;a<=this.e.c-this.s.c;++a)f++,b++});if(a||g)a=a||1,g=g||1,e.push({s:{r:n,c:f},e:{r:n+a-1,c:f+g-1}});1<g&&(h+=' ss:MergeAcross="'+
(g-1)+'"',f+=g-1);1<a&&(h+=' ss:MergeDown="'+(a-1)+'" ss:StyleID="rsp1"');0<b&&(h+=' ss:Index="'+(f+1)+'"',b=0);t+="<Cell"+h+'><Data ss:Type="'+l+'">'+d("<div />").text(c).html()+"</Data></Cell>\r";f++}});0<t.length&&(H+='<Row ss:AutoFitHeight="0">\r'+t+"</Row>\r");n++});return a.length}var c=d(this),e="";"string"===typeof a.mso.worksheetName&&a.mso.worksheetName.length?e=a.mso.worksheetName+" "+(F.length+1):"undefined"!==typeof a.mso.worksheetName[F.length]&&(e=a.mso.worksheetName[F.length]);e.length||
(e=c.find("caption").text()||"");e.length||(e="Table "+(F.length+1));e=d.trim(e.replace(/[\\\/[\]*:?'"]/g,"").substring(0,31));F.push(d("<div />").text(e).html());!1===a.exportHiddenCells&&(K=c.find("tr, th, td").filter(":hidden"),U=0<K.length);n=0;S=R(this);H="<Table>\r";e=b(y(c),"th,td",0);b(C(c),"td,th",e);H+="</Table>\r";sa.push(H)});k={};z={};for(var m,Z,X=0,da=F.length;X<da;X++)m=F[X],Z=k[m],Z=k[m]=null==Z?1:Z+1,2===Z&&(F[z[m]]=F[z[m]].substring(0,29)+"-1"),1<k[m]?F[X]=F[X].substring(0,29)+
"-"+k[m]:z[m]=X;k='<?xml version="1.0" encoding="UTF-8"?>\r<?mso-application progid="Excel.Sheet"?>\r<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"\r xmlns:o="urn:schemas-microsoft-com:office:office"\r xmlns:x="urn:schemas-microsoft-com:office:excel"\r xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"\r xmlns:html="http://www.w3.org/TR/REC-html40">\r<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">\r <Created>'+(new Date).toISOString()+'</Created>\r</DocumentProperties>\r<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">\r <AllowPNG/>\r</OfficeDocumentSettings>\r<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">\r <WindowHeight>9000</WindowHeight>\r <WindowWidth>13860</WindowWidth>\r <WindowTopX>0</WindowTopX>\r <WindowTopY>0</WindowTopY>\r <ProtectStructure>False</ProtectStructure>\r <ProtectWindows>False</ProtectWindows>\r</ExcelWorkbook>\r<Styles>\r <Style ss:ID="Default" ss:Name="Normal">\r <Alignment ss:Vertical="Bottom"/>\r <Borders/>\r <Font/>\r <Interior/>\r <NumberFormat/>\r <Protection/>\r </Style>\r <Style ss:ID="rsp1">\r <Alignment ss:Vertical="Center"/>\r </Style>\r <Style ss:ID="pct1">\r <NumberFormat ss:Format="Percent"/>\r </Style>\r</Styles>\r';
for(z=0;z<sa.length;z++)k+='<Worksheet ss:Name="'+F[z]+'" ss:RightToLeft="'+(a.mso.rtl?"1":"0")+'">\r'+sa[z],k=a.mso.rtl?k+'<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">\r<DisplayRightToLeft/>\r</WorksheetOptions>\r':k+'<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel"/>\r',k+="</Worksheet>\r";k+="</Workbook>\r";if("string"===a.outputMode)return k;if("base64"===a.outputMode)return L(k);N(k,a.fileName+".xml","application/xml","utf-8","base64",!1)}else if("excel"===
a.type&&"xlsx"===a.mso.fileFormat){var aa=[],Ga=XLSX.utils.book_new();d(B).filter(function(){return I(d(this))}).each(function(){for(var b=d(this),c={},e=this.getElementsByTagName("tr"),g={s:{r:0,c:0},e:{r:0,c:0}},f=[],l,q=[],h=0,u=0,k,m,n,t,r,w=XLSX.SSF.get_table();h<e.length&&1E7>u;++h)if(k=e[h],m=!1,"function"===typeof a.onIgnoreRow&&(m=a.onIgnoreRow(d(k),h)),!0!==m&&(0===a.ignoreRow.length||-1===d.inArray(h,a.ignoreRow)&&-1===d.inArray(h-e.length,a.ignoreRow))&&!1!==I(d(k))){var y=k.children,
B=0;for(k=0;k<y.length;++k)r=y[k],t=+J(r)||1,B+=t;var z=0;for(k=m=0;k<y.length;++k)if(r=y[k],t=+J(r)||1,l=k+z,!ta(d(r),B,l+(l<m?m-l:0))){z+=t-1;for(l=0;l<f.length;++l){var v=f[l];v.s.c==m&&v.s.r<=u&&u<=v.e.r&&(m=v.e.c+1,l=-1)}(0<(n=+T(r))||1<t)&&f.push({s:{r:u,c:m},e:{r:u+(n||1)-1,c:m+t-1}});var C={type:""};l=D(r,h,k+z,C);v={t:"s",v:l};var A="";if(""!==d(r).attr("data-tableexport-cellformat")){var x=parseInt(d(r).attr("data-tableexport-xlsxformatid")||0);0===x&&"function"===typeof a.mso.xslx.formatId.numbers&&
(x=a.mso.xslx.formatId.numbers(d(r),h,k+z));0===x&&"function"===typeof a.mso.xslx.formatId.date&&(x=a.mso.xslx.formatId.date(d(r),h,k+z));if(49===x||"@"===x)A="s";else if("number"===C.type||0<x&&14>x||36<x&&41>x||48===x)A="n";else if("date"===C.type||13<x&&37>x||44<x&&48>x||56===x)A="d"}else A="s";if(null!=l)if(0===l.length)v.t="z";else if(0!==l.trim().length&&"s"!==A)if("function"===C.type)v={f:l};else if("TRUE"===l)v={t:"b",v:!0};else if("FALSE"===l)v={t:"b",v:!1};else if(""===A&&d(r).find("a").length)l=
"href"!==a.htmlHyperlink?l:"",v={f:'=HYPERLINK("'+d(r).find("a").attr("href")+(l.length?'","'+l:"")+'")'};else if("n"===A||isFinite(Da(l,a.numbers.output))){if(r=Da(l,a.numbers.output),0===x&&"function"!==typeof a.mso.xslx.formatId.numbers&&(x=a.mso.xslx.formatId.numbers),isFinite(r)||isFinite(l))v={t:"n",v:isFinite(r)?r:l,z:"string"===typeof x?x:x in w?w[x]:"0.00"}}else if(!1!==(r=La(l))||"d"===A)0===x&&"function"!==typeof a.mso.xslx.formatId.date&&(x=a.mso.xslx.formatId.date),v={t:"d",v:!1!==r?
r:l,z:"string"===typeof x?x:x in w?w[x]:"m/d/yy"};c[oa({c:m,r:u})]=v;g.e.c<m&&(g.e.c=m);m+=t}++u}f.length&&(c["!merges"]=f);q.length&&(c["!rows"]=q);g.e.r=u-1;c["!ref"]=pa(g);1E7<=u&&(c["!fullref"]=pa((g.e.r=e.length-h+u-1,g)));e="";"string"===typeof a.mso.worksheetName&&a.mso.worksheetName.length?e=a.mso.worksheetName+" "+(aa.length+1):"undefined"!==typeof a.mso.worksheetName[aa.length]&&(e=a.mso.worksheetName[aa.length]);e.length||(e=b.find("caption").text()||"");e.length||(e="Table "+(aa.length+
1));e=d.trim(e.replace(/[\\\/[\]*:?'"]/g,"").substring(0,31));aa.push(e);XLSX.utils.book_append_sheet(Ga,c,e)});k=XLSX.write(Ga,{type:"binary",bookType:a.mso.fileFormat,bookSST:!1});N(Oa(k),a.fileName+"."+a.mso.fileFormat,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","UTF-8","",!1)}else if("excel"===a.type||"xls"===a.type||"word"===a.type||"doc"===a.type){k="excel"===a.type||"xls"===a.type?"excel":"word";z="excel"===k?"xls":"doc";m='xmlns:x="urn:schemas-microsoft-com:office:'+
k+'"';var H="",ba="";d(B).filter(function(){return I(d(this))}).each(function(){var b=d(this);""===ba&&(ba=a.mso.worksheetName||b.find("caption").text()||"Table",ba=d.trim(ba.replace(/[\\\/[\]*:?'"]/g,"").substring(0,31)));!1===a.exportHiddenCells&&(K=b.find("tr, th, td").filter(":hidden"),U=0<K.length);n=0;G=[];S=R(this);H+="<table><thead>";r=y(b);d(r).each(function(){var b=d(this);t="";E(this,"th,td",n,r.length,function(d,c,f){if(null!==d){var e="";t+="<th";if(a.mso.styles.length){var g=document.defaultView.getComputedStyle(d,
null),h=document.defaultView.getComputedStyle(b[0],null),k;for(k in a.mso.styles){var m=g[a.mso.styles[k]];""===m&&(m=h[a.mso.styles[k]]);""!==m&&"0px none rgb(0, 0, 0)"!==m&&"rgba(0, 0, 0, 0)"!==m&&(e+=""===e?'style="':";",e+=a.mso.styles[k]+":"+m)}}""!==e&&(t+=" "+e+'"');e=J(d);0<e&&(t+=' colspan="'+e+'"');e=T(d);0<e&&(t+=' rowspan="'+e+'"');t+=">"+D(d,c,f)+"</th>"}});0<t.length&&(H+="<tr>"+t+"</tr>");n++});H+="</thead><tbody>";w=C(b);d(w).each(function(){var b=d(this);t="";E(this,"td,th",n,r.length+
w.length,function(c,g,f){if(null!==c){var e=D(c,g,f),q="",h=d(c).attr("data-tableexport-msonumberformat");"undefined"===typeof h&&"function"===typeof a.mso.onMsoNumberFormat&&(h=a.mso.onMsoNumberFormat(c,g,f));"undefined"!==typeof h&&""!==h&&(q="style=\"mso-number-format:'"+h+"'");if(a.mso.styles.length){g=document.defaultView.getComputedStyle(c,null);f=document.defaultView.getComputedStyle(b[0],null);for(var k in a.mso.styles)h=g[a.mso.styles[k]],""===h&&(h=f[a.mso.styles[k]]),""!==h&&"0px none rgb(0, 0, 0)"!==
h&&"rgba(0, 0, 0, 0)"!==h&&(q+=""===q?'style="':";",q+=a.mso.styles[k]+":"+h)}t+="<td";""!==q&&(t+=" "+q+'"');q=J(c);0<q&&(t+=' colspan="'+q+'"');c=T(c);0<c&&(t+=' rowspan="'+c+'"');"string"===typeof e&&""!==e&&(e=Ba(e),e=e.replace(/\n/g,"<br>"));t+=">"+e+"</td>"}});0<t.length&&(H+="<tr>"+t+"</tr>");n++});a.displayTableName&&(H+="<tr><td></td></tr><tr><td></td></tr><tr><td>"+D(d("<p>"+a.tableName+"</p>"))+"</td></tr>");H+="</tbody></table>"});m='<html xmlns:o="urn:schemas-microsoft-com:office:office" '+
m+' xmlns="http://www.w3.org/TR/REC-html40">'+('<meta http-equiv="content-type" content="application/vnd.ms-'+k+'; charset=UTF-8">')+"<head>";"excel"===k&&(m+="\x3c!--[if gte mso 9]>",m+="<xml>",m+="<x:ExcelWorkbook>",m+="<x:ExcelWorksheets>",m+="<x:ExcelWorksheet>",m+="<x:Name>",m+=ba,m+="</x:Name>",m+="<x:WorksheetOptions>",m+="<x:DisplayGridlines/>",a.mso.rtl&&(m+="<x:DisplayRightToLeft/>"),m+="</x:WorksheetOptions>",m+="</x:ExcelWorksheet>",m+="</x:ExcelWorksheets>",m+="</x:ExcelWorkbook>",m+=
"</xml>",m+="<![endif]--\x3e");m+="<style>";m+="@page { size:"+a.mso.pageOrientation+"; mso-page-orientation:"+a.mso.pageOrientation+"; }";m+="@page Section1 {size:"+O[a.mso.pageFormat][0]+"pt "+O[a.mso.pageFormat][1]+"pt";m+="; margin:1.0in 1.25in 1.0in 1.25in;mso-header-margin:.5in;mso-footer-margin:.5in;mso-paper-source:0;}";m+="div.Section1 {page:Section1;}";m+="@page Section2 {size:"+O[a.mso.pageFormat][1]+"pt "+O[a.mso.pageFormat][0]+"pt";m+=";mso-page-orientation:"+a.mso.pageOrientation+";margin:1.25in 1.0in 1.25in 1.0in;mso-header-margin:.5in;mso-footer-margin:.5in;mso-paper-source:0;}";
m+="div.Section2 {page:Section2;}";m+="br {mso-data-placement:same-cell;}";m+="</style>";m+="</head>";m+="<body>";m+='<div class="Section'+("landscape"===a.mso.pageOrientation?"2":"1")+'">';m+=H;m+="</div>";m+="</body>";m+="</html>";if("string"===a.outputMode)return m;if("base64"===a.outputMode)return L(m);N(m,a.fileName+"."+z,"application/vnd.ms-"+k,"","base64",!1)}else if("png"===a.type)html2canvas(d(B)[0]).then(function(b){b=b.toDataURL();for(var c=atob(b.substring(22)),d=new ArrayBuffer(c.length),
g=new Uint8Array(d),f=0;f<c.length;f++)g[f]=c.charCodeAt(f);if("string"===a.outputMode)return c;if("base64"===a.outputMode)return L(b);"window"===a.outputMode?window.open(b):N(d,a.fileName+".png","image/png","","",!1)});else if("pdf"===a.type)if(!0===a.pdfmake.enabled){var ia={content:[]};d.extend(!0,ia,a.pdfmake.docDefinition);G=[];d(B).filter(function(){return I(d(this))}).each(function(){var a=d(this),c=[],e=[];n=0;var g=function(a,b,c){var f=0;d(a).each(function(){var a=[];E(this,b,n,c,function(b,
d,c){if("undefined"!==typeof b&&null!==b){var f=J(b),e=T(b);b=D(b,d,c)||" ";1<f||1<e?a.push({colSpan:f||1,rowSpan:e||1,text:b}):a.push(b)}else a.push(" ")});a.length&&e.push(a);f<a.length&&(f=a.length);n++});return f};r=y(a);for(var f=g(r,"th,td",r.length),l=c.length;l<f;l++)c.push("*");w=C(a);g(w,"th,td",r.length+w.length);ia.content.push({table:{headerRows:r.length,widths:c,body:e},pageBreak:ia.content.length?"before":void 0})});"undefined"!==typeof pdfMake&&"undefined"!==typeof pdfMake.createPdf&&
(pdfMake.fonts={Roboto:{normal:"Roboto-Regular.ttf",bold:"Roboto-Medium.ttf",italics:"Roboto-Italic.ttf",bolditalics:"Roboto-MediumItalic.ttf"}},pdfMake.vfs.hasOwnProperty("Mirza-Regular.ttf")?(a.pdfmake.docDefinition.defaultStyle.font="Mirza",d.extend(!0,pdfMake.fonts,{Mirza:{normal:"Mirza-Regular.ttf",bold:"Mirza-Bold.ttf",italics:"Mirza-Medium.ttf",bolditalics:"Mirza-SemiBold.ttf"}})):pdfMake.vfs.hasOwnProperty("gbsn00lp.ttf")?(a.pdfmake.docDefinition.defaultStyle.font="gbsn00lp",d.extend(!0,pdfMake.fonts,
{gbsn00lp:{normal:"gbsn00lp.ttf",bold:"gbsn00lp.ttf",italics:"gbsn00lp.ttf",bolditalics:"gbsn00lp.ttf"}})):pdfMake.vfs.hasOwnProperty("ZCOOLXiaoWei-Regular.ttf")&&(a.pdfmake.docDefinition.defaultStyle.font="ZCOOLXiaoWei",d.extend(!0,pdfMake.fonts,{ZCOOLXiaoWei:{normal:"ZCOOLXiaoWei-Regular.ttf",bold:"ZCOOLXiaoWei-Regular.ttf",italics:"ZCOOLXiaoWei-Regular.ttf",bolditalics:"ZCOOLXiaoWei-Regular.ttf"}})),d.extend(!0,pdfMake.fonts,a.pdfmake.fonts),pdfMake.createPdf(ia).getBuffer(function(b){N(b,a.fileName+
".pdf","application/pdf","","",!1)}))}else if(!1===a.jspdf.autotable){k={dim:{w:fa(d(B).first().get(0),"width","mm"),h:fa(d(B).first().get(0),"height","mm")},pagesplit:!1};var Ha=new jsPDF(a.jspdf.orientation,a.jspdf.unit,a.jspdf.format);Ha.addHTML(d(B).first(),a.jspdf.margins.left,a.jspdf.margins.top,k,function(){va(Ha,!1)})}else{var g=a.jspdf.autotable.tableExport;if("string"===typeof a.jspdf.format&&"bestfit"===a.jspdf.format.toLowerCase()){var ja="",ca="",Ia=0;d(B).each(function(){if(I(d(this))){var a=
fa(d(this).get(0),"width","pt");if(a>Ia){a>O.a0[0]&&(ja="a0",ca="l");for(var c in O)O.hasOwnProperty(c)&&O[c][1]>a&&(ja=c,ca="l",O[c][0]>a&&(ca="p"));Ia=a}}});a.jspdf.format=""===ja?"a4":ja;a.jspdf.orientation=""===ca?"w":ca}if(null==g.doc&&(g.doc=new jsPDF(a.jspdf.orientation,a.jspdf.unit,a.jspdf.format),g.wScaleFactor=1,g.hScaleFactor=1,"function"===typeof a.jspdf.onDocCreated))a.jspdf.onDocCreated(g.doc);!0===g.outputImages&&(g.images={});"undefined"!==typeof g.images&&(d(B).filter(function(){return I(d(this))}).each(function(){var b=
0;G=[];!1===a.exportHiddenCells&&(K=d(this).find("tr, th, td").filter(":hidden"),U=0<K.length);r=y(d(this));w=C(d(this));d(w).each(function(){E(this,"td,th",r.length+b,r.length+w.length,function(a){xa(a,d(a).children(),g)});b++})}),r=[],w=[]);Ka(g,function(){d(B).filter(function(){return I(d(this))}).each(function(){var b;n=0;G=[];!1===a.exportHiddenCells&&(K=d(this).find("tr, th, td").filter(":hidden"),U=0<K.length);S=R(this);g.columns=[];g.rows=[];g.teCells={};if("function"===typeof g.onTable&&
!1===g.onTable(d(this),a))return!0;a.jspdf.autotable.tableExport=null;var c=d.extend(!0,{},a.jspdf.autotable);a.jspdf.autotable.tableExport=g;c.margin={};d.extend(!0,c.margin,a.jspdf.margins);c.tableExport=g;"function"!==typeof c.beforePageContent&&(c.beforePageContent=function(a){if(1===a.pageCount){var b=a.table.rows.concat(a.table.headerRow);d.each(b,function(){0<this.height&&(this.height+=(2-1.15)/2*this.styles.fontSize,a.table.height+=(2-1.15)/2*this.styles.fontSize)})}});"function"!==typeof c.createdHeaderCell&&
(c.createdHeaderCell=function(a,b){a.styles=d.extend({},b.row.styles);if("undefined"!==typeof g.columns[b.column.dataKey]){var e=g.columns[b.column.dataKey];if("undefined"!==typeof e.rect){a.contentWidth=e.rect.width;if("undefined"===typeof g.heightRatio||0===g.heightRatio){var f=b.row.raw[b.column.dataKey].rowspan?b.row.raw[b.column.dataKey].rect.height/b.row.raw[b.column.dataKey].rowspan:b.row.raw[b.column.dataKey].rect.height;g.heightRatio=a.styles.rowHeight/f}f=b.row.raw[b.column.dataKey].rect.height*
g.heightRatio;f>a.styles.rowHeight&&(a.styles.rowHeight=f)}a.styles.halign="inherit"===c.headerStyles.halign?"center":c.headerStyles.halign;a.styles.valign=c.headerStyles.valign;"undefined"!==typeof e.style&&!0!==e.style.hidden&&("inherit"===c.headerStyles.halign&&(a.styles.halign=e.style.align),"inherit"===c.styles.fillColor&&(a.styles.fillColor=e.style.bcolor),"inherit"===c.styles.textColor&&(a.styles.textColor=e.style.color),"inherit"===c.styles.fontStyle&&(a.styles.fontStyle=e.style.fstyle))}});
"function"!==typeof c.createdCell&&(c.createdCell=function(a,b){b=g.teCells[b.row.index+":"+b.column.dataKey];a.styles.halign="inherit"===c.styles.halign?"center":c.styles.halign;a.styles.valign=c.styles.valign;"undefined"!==typeof b&&"undefined"!==typeof b.style&&!0!==b.style.hidden&&("inherit"===c.styles.halign&&(a.styles.halign=b.style.align),"inherit"===c.styles.fillColor&&(a.styles.fillColor=b.style.bcolor),"inherit"===c.styles.textColor&&(a.styles.textColor=b.style.color),"inherit"===c.styles.fontStyle&&
(a.styles.fontStyle=b.style.fstyle))});"function"!==typeof c.drawHeaderCell&&(c.drawHeaderCell=function(a,b){var d=g.columns[b.column.dataKey];return(!0!==d.style.hasOwnProperty("hidden")||!0!==d.style.hidden)&&0<=d.rowIndex?wa(a,b,d):!1});"function"!==typeof c.drawCell&&(c.drawCell=function(a,b){var c=g.teCells[b.row.index+":"+b.column.dataKey];if(!0!==("undefined"!==typeof c&&c.isCanvas))wa(a,b,c)&&(g.doc.rect(a.x,a.y,a.width,a.height,a.styles.fillStyle),"undefined"===typeof c||"undefined"!==typeof c.hasUserDefText&&
!0===c.hasUserDefText||"undefined"===typeof c.elements||!c.elements.length?Aa(a,{},g):(b=a.height/c.rect.height,b>g.hScaleFactor&&(g.hScaleFactor=b),g.wScaleFactor=a.width/c.rect.width,b=a.textPos.y,za(a,c.elements,g),a.textPos.y=b,Aa(a,c.elements,g)));else{c=c.elements[0];var e=d(c).attr("data-tableexport-canvas"),f=c.getBoundingClientRect();a.width=f.width*g.wScaleFactor;a.height=f.height*g.hScaleFactor;b.row.height=a.height;ua(a,c,e,g)}return!1});g.headerrows=[];r=y(d(this));d(r).each(function(){b=
0;g.headerrows[n]=[];E(this,"th,td",n,r.length,function(a,c,d){var e=Ca(a);e.title=D(a,c,d);e.key=b++;e.rowIndex=n;g.headerrows[n].push(e)});n++});if(0<n)for(var e=n-1;0<=e;)d.each(g.headerrows[e],function(){var a=this;0<e&&null===this.rect&&(a=g.headerrows[e-1][this.key]);null!==a&&0<=a.rowIndex&&(!0!==a.style.hasOwnProperty("hidden")||!0!==a.style.hidden)&&g.columns.push(a)}),e=0<g.columns.length?-1:e-1;var k=0;w=[];w=C(d(this));d(w).each(function(){var a=[];b=0;E(this,"td,th",n,r.length+w.length,
function(c,e,f){if("undefined"===typeof g.columns[b]){var h={title:"",key:b,style:{hidden:!0}};g.columns.push(h)}a.push(D(c,e,f));"undefined"!==typeof c&&null!==c?(h=Ca(c),h.isCanvas=c.hasAttribute("data-tableexport-canvas"),h.elements=h.isCanvas?d(c):d(c).children(),"undefined"!==typeof d(c).data("teUserDefText")&&(h.hasUserDefText=!0)):(h=d.extend(!0,{},g.teCells[k+":"+(b-1)]),h.colspan=-1);g.teCells[k+":"+b++]=h});a.length&&(g.rows.push(a),k++);n++});if("function"===typeof g.onBeforeAutotable)g.onBeforeAutotable(d(this),
g.columns,g.rows,c);g.doc.autoTable(g.columns,g.rows,c);if("function"===typeof g.onAfterAutotable)g.onAfterAutotable(d(this),c);a.jspdf.autotable.startY=g.doc.autoTableEndPosY()+c.margin.top});va(g.doc,"undefined"!==typeof g.images&&!1===jQuery.isEmptyObject(g.images));"undefined"!==typeof g.headerrows&&(g.headerrows.length=0);"undefined"!==typeof g.columns&&(g.columns.length=0);"undefined"!==typeof g.rows&&(g.rows.length=0);delete g.doc;g.doc=null})}if("function"===typeof a.onTableExportEnd)a.onTableExportEnd();
return this}})(jQuery);

View File

@ -45,7 +45,10 @@ function ajaxRequest(params) {
// rearrange the result into what bootstrap-table expects // rearrange the result into what bootstrap-table expects
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows}); params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
}) })
.fail(logAjaxFail); .fail(function(jqXHR) {
logAjaxFail(jqXHR);
$j('#eventTable').bootstrapTable('refresh');
});
} }
function processRows(rows) { function processRows(rows) {
@ -58,7 +61,7 @@ function processRows(rows) {
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=monitor&amp;mid=' + mid + '">' + 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 ) {
row.Cause = row.Cause + '<a href="#" class="objDetectLink" data-eid=' +eid+ '><div class="small text-nowrap text-muted"><u>' + row.Notes + '</u></div></div></a>'; row.Cause = row.Cause + '<a href="#" class="objDetectLink" data-eid=' +eid+ '><div class="small text-nowrap text-muted"><u>' + row.Notes + '</u></div></div></a>';
@ -118,7 +121,11 @@ function manageDelConfirmModalBtns() {
$j('#eventTable').bootstrapTable('refresh'); $j('#eventTable').bootstrapTable('refresh');
$j('#deleteConfirm').modal('hide'); $j('#deleteConfirm').modal('hide');
}) })
.fail(logAjaxFail); .fail( function(jqxhr) {
logAjaxFail(jqxhr);
$j('#eventTable').bootstrapTable('refresh');
$j('#deleteConfirm').modal('hide');
});
}); });
// Manage the CANCEL modal button // Manage the CANCEL modal button

View File

@ -871,6 +871,8 @@ include('_monitor_source_nvsocket.php');
'2048x1536'=>'2048x1536 3MP', '2048x1536'=>'2048x1536 3MP',
'2560x1440'=>'2560x1440 1440p QHD WQHD', '2560x1440'=>'2560x1440 1440p QHD WQHD',
'2592x1944'=>'2592x1944 5MP', '2592x1944'=>'2592x1944 5MP',
'2688x1520'=>'2688x1520 4MP',
'3072x2048'=>'3072x2048 6MP',
'3840x2160'=>'3840x2160 4K UHD', '3840x2160'=>'3840x2160 4K UHD',
); );
$selected = ''; $selected = '';
@ -993,7 +995,7 @@ $videowriter_encoders = array(
'mpeg1' => 'mpeg1', 'mpeg1' => 'mpeg1',
'mpeg2' => 'mpeg2', 'mpeg2' => 'mpeg2',
); );
echo htmlSelect( 'newMonitor[Encoder]', $videowriter_encoders, $monitor->Encoder() );?></td></tr> echo htmlSelect('newMonitor[Encoder]', $videowriter_encoders, $monitor->Encoder());?></td></tr>
<tr> <tr>
<td><?php echo translate('OutputContainer') ?></td> <td><?php echo translate('OutputContainer') ?></td>
<td> <td>
@ -1017,7 +1019,7 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor
<?php if ( $monitor->Type() == 'Ffmpeg' ) { ?> <?php if ( $monitor->Type() == 'Ffmpeg' ) { ?>
<input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( $monitor->RecordAudio() ) { ?> checked="checked"<?php } ?>/> <input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( $monitor->RecordAudio() ) { ?> checked="checked"<?php } ?>/>
<?php } else { ?> <?php } else { ?>
Audio recording only available with FFMPEG using H264 Passthrough Audio recording only available with FFMPEG
<input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo $monitor->RecordAudio() ? 1 : 0 ?>"/> <input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo $monitor->RecordAudio() ? 1 : 0 ?>"/>
<?php } ?> <?php } ?>
</td></tr> </td></tr>