diff --git a/src/zm_comms.h b/src/zm_comms.h index 22b9515d8..30c217ddc 100644 --- a/src/zm_comms.h +++ b/src/zm_comms.h @@ -22,6 +22,7 @@ #include "zm_exception.h" #include "zm_logger.h" +#include #include #include #include diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 595a5b91a..f7cd609bf 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -22,6 +22,7 @@ #include "zm_db.h" #include "zm_logger.h" #include "zm_utils.h" +#include #include #include #include diff --git a/src/zm_db.cpp b/src/zm_db.cpp index bb8da3a1f..9ffaed26a 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -19,10 +19,12 @@ #include "zm_db.h" #include "zm_logger.h" +#include "zm_signal.h" #include MYSQL dbconn; -RecursiveMutex db_mutex; +std::mutex db_mutex; +zmDbQueue dbQueue; bool zmDbConnected = false; @@ -106,46 +108,36 @@ bool zmDbConnect() { void zmDbClose() { if (zmDbConnected) { - db_mutex.lock(); + std::lock_guard lck(db_mutex); mysql_close(&dbconn); // mysql_init() call implicitly mysql_library_init() but // mysql_close() does not call mysql_library_end() mysql_library_end(); zmDbConnected = false; - db_mutex.unlock(); } } MYSQL_RES * zmDbFetch(const char * query) { - if ( !zmDbConnected ) { - Error("Not connected."); - return nullptr; - } - db_mutex.lock(); - // Might have been disconnected while we waited for the lock - if ( !zmDbConnected ) { - db_mutex.unlock(); + std::lock_guard lck(db_mutex); + if (!zmDbConnected) { Error("Not connected."); return nullptr; } - if ( mysql_query(&dbconn, query) ) { - db_mutex.unlock(); + if (mysql_query(&dbconn, query)) { Error("Can't run query: %s", mysql_error(&dbconn)); return nullptr; } - Debug(4, "Success running query: %s", query); 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); } - db_mutex.unlock(); return result; } // end MYSQL_RES * zmDbFetch(const char * query); zmDbRow *zmDbFetchOne(const char *query) { zmDbRow *row = new zmDbRow(); - if ( row->fetch(query) ) { + if (row->fetch(query)) { return row; } delete row; @@ -154,10 +146,10 @@ zmDbRow *zmDbFetchOne(const char *query) { MYSQL_RES *zmDbRow::fetch(const char *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); - if ( n_rows != 1 ) { + if (n_rows != 1) { Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query); mysql_free_result(result_set); result_set = nullptr; @@ -165,7 +157,7 @@ MYSQL_RES *zmDbRow::fetch(const char *query) { } row = mysql_fetch_row(result_set); - if ( !row ) { + if (!row) { mysql_free_result(result_set); result_set = nullptr; 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) { - db_mutex.lock(); + std::lock_guard lck(db_mutex); + if (!zmDbConnected) + return 0; int rc; - while ( rc = mysql_query(&dbconn, query) ) { - db_mutex.unlock(); + while ((rc = mysql_query(&dbconn, query)) and !zm_terminate) { 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; - - db_mutex.lock(); + } } - db_mutex.unlock(); return 1; } int zmDbDoInsert(const char *query) { - db_mutex.lock(); + std::lock_guard lck(db_mutex); + if (!zmDbConnected) return 0; int rc; - while ( rc = mysql_query(&dbconn, query) ) { - db_mutex.unlock(); + while ( (rc = mysql_query(&dbconn, query)) and !zm_terminate) { Error("Can't run query %s: %s", query, mysql_error(&dbconn)); if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) return 0; - - db_mutex.lock(); } int id = mysql_insert_id(&dbconn); - db_mutex.unlock(); return id; } zmDbRow::~zmDbRow() { - if ( result_set ) { + if (result_set) { mysql_free_result(result_set); result_set = 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 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 lock(mMutex); + mQueue.push(sql); + mCondition.notify_all(); +} diff --git a/src/zm_db.h b/src/zm_db.h index 22e684c67..a4e2daf25 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -23,6 +23,26 @@ #include "zm_thread.h" #include #include +#include +#include +#include +#include +#include + +class zmDbQueue { + private: + std::queue 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 { private: @@ -42,7 +62,8 @@ class zmDbRow { }; extern MYSQL dbconn; -extern RecursiveMutex db_mutex; +extern std::mutex db_mutex; +extern zmDbQueue dbQueue; extern bool zmDbConnected; @@ -51,7 +72,7 @@ void zmDbClose(); int zmDbDo(const char *query); int zmDbDoInsert(const char *query); -MYSQL_RES * zmDbFetch( const char *query ); -zmDbRow *zmDbFetchOne( const char *query ); +MYSQL_RES * zmDbFetch(const char *query); +zmDbRow *zmDbFetchOne(const char *query); #endif // ZM_DB_H diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 58c9cd234..aebd3f93e 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -251,30 +251,25 @@ Event::~Event() { frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id); - db_mutex.lock(); - while ( mysql_query(&dbconn, sql) && !zm_terminate ) { - db_mutex.unlock(); - Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn)); - sleep(1); - db_mutex.lock(); - } - if ( !mysql_affected_rows(&dbconn) ) { - // Name might have been changed during recording, so just do the update without changing the name. - snprintf(sql, sizeof(sql), - "UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, - end_time.tv_sec, - delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, - frames, alarm_frames, - tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, - id); + { // scope for lock + std::lock_guard lck(db_mutex); while ( mysql_query(&dbconn, sql) && !zm_terminate ) { - db_mutex.unlock(); 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 - db_mutex.unlock(); + if ( !mysql_affected_rows(&dbconn) ) { + // Name might have been changed during recording, so just do the update without changing the name. + snprintf(sql, sizeof(sql), + "UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, + end_time.tv_sec, + delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, + frames, alarm_frames, + tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, + id); + while ( mysql_query(&dbconn, sql) && !zm_terminate ) { + Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn)); + } + } // end if no changed rows due to Name change during recording + } } // Event::~Event() void Event::createNotes(std::string ¬es) { @@ -427,11 +422,7 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) { 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); - db_mutex.lock(); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't insert event: %s", mysql_error(&dbconn)); - } - db_mutex.unlock(); + zmDbDo(sql); #endif } // end if update } // void Event::updateNotes(const StringSetMap &newNoteSetMap) @@ -495,14 +486,7 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str if ( frameCount ) { *(frame_insert_values-1) = '\0'; - db_mutex.lock(); - 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); - } + zmDbDo(frame_insert_sql); last_db_frame = frames; } else { 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) { - 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", have_video_keyframe, packet->codec_type, (packet->codec_type == AVMEDIA_TYPE_VIDEO), packet->keyframe); ZM_DUMP_PACKET(packet->packet, "Adding to event"); @@ -552,38 +538,9 @@ void Event::WriteDbFrames() { delete frame; } *(frame_insert_values_ptr-1) = '\0'; // The -1 is for the extra , added for values above - db_mutex.lock(); - 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); - } + zmDbDo(frame_insert_sql); } // 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) { if (!timestamp.tv_sec) { 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, id ); - db_mutex.lock(); - 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(); + zmDbDo(sql); } else { 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); diff --git a/src/zm_event.h b/src/zm_event.h index a3eec79ed..7ba4b03ba 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -134,7 +134,6 @@ class Event { Image **images, struct timeval **timestamps); void WriteDbFrames(); - void UpdateFramesDelta(double offset); bool SetPath(Storage *storage); public: diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 961c4f69b..da1263384 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -27,6 +27,9 @@ #include "zm_storage.h" #include #include +#ifdef __FreeBSD__ +#include +#endif const std::string EventStream::StreamMode_Strings[4] = { "None", @@ -348,7 +351,7 @@ void EventStream::processCommand(const CmdMsg *msg) { curr_frame_id = 1; } else { 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; diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 636ef3726..ef873c25d 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -90,7 +90,7 @@ class EventStream : public StreamBase { bool loadInitialEventData(int monitor_id, time_t event_time); bool checkEventLoaded(); - void processCommand(const CmdMsg *msg); + void processCommand(const CmdMsg *msg) override; bool sendFrame(int delta_us); public: diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 1d7bfd065..6ff053c34 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -64,11 +64,15 @@ void log_libav_callback(void *ptr, int level, const char *fmt, va_list vargs) { if ( log ) { char logString[8192]; - vsnprintf(logString, sizeof(logString)-1, fmt, vargs); - int length = strlen(logString); - // ffmpeg logs have a carriage return, so replace it with terminator - logString[length-1] = 0; - log->logPrint(false, __FILE__, __LINE__, log_level, logString); + int length = vsnprintf(logString, sizeof(logString)-1, fmt, vargs); + if ( length > 0 ) { + if ( static_cast(length) > sizeof(logString)-1 ) length = sizeof(logString)-1; + // ffmpeg logs have a carriage return, so replace it with terminator + logString[length-1] = 0; + 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; case AV_PIX_FMT_NONE : case AV_PIX_FMT_YUVJ420P : - default: return AV_PIX_FMT_YUV420P; + default: + return fmt; } } diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index cd5f4cbed..ec22bfc33 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -369,10 +369,12 @@ void zm_dump_codecpar(const AVCodecParameters *par); if (logDebugging()) { \ double pts_time = static_cast(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, \ text, \ pkt.pts, \ + stream->time_base.num, \ + stream->time_base.den, \ pts_time, \ pkt.dts, \ pkt.size, \ diff --git a/src/zm_fifo.h b/src/zm_fifo.h index 42c313051..e99d56dd2 100644 --- a/src/zm_fifo.h +++ b/src/zm_fifo.h @@ -44,7 +44,6 @@ int zmFifoDbgInit(Monitor * monitor); class FifoStream : public StreamBase { private: char * stream_path; - int fd; int total_read; int bytes_read; unsigned int frame_count; @@ -59,12 +58,11 @@ class FifoStream : public StreamBase { StreamType stream_type; bool sendMJEGFrames(); bool sendRAWFrames(); - void processCommand(const CmdMsg *msg) {} + void processCommand(const CmdMsg *msg) override {} public: FifoStream() : stream_path(nullptr), - fd(0), total_read(0), bytes_read(0), frame_count(0), diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 581235eba..f697ecb8f 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -276,13 +276,15 @@ void Image::Assign(const AVFrame *frame) { AVFrame *dest_frame = zm_av_frame_alloc(); PopulateFrame(dest_frame); 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 sws_convert_context = sws_getCachedContext( sws_convert_context, frame->width, frame->height, (AVPixelFormat)frame->format, width, height, format, - SWS_BICUBIC, nullptr, nullptr, nullptr); + //SWS_BICUBIC, + SWS_POINT | SWS_BITEXACT, + nullptr, nullptr, nullptr); if ( sws_convert_context == nullptr ) Fatal("Unable to create conversion context"); diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index 2cab3ee80..fd4d077b8 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -177,6 +177,10 @@ LibvlcCamera::~LibvlcCamera() { (*libvlc_release_f)(mLibvlcInstance); mLibvlcInstance = nullptr; } + if (libvlc_lib) { + dlclose(libvlc_lib); + libvlc_lib = nullptr; + } if ( mOptArgV != nullptr ) { delete[] mOptArgV; } diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index 4344b4997..c00a37e30 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -18,11 +18,11 @@ static int (*WaitForMessage_f)(rfbClient*, unsigned int) = nullptr; static rfbBool (*HandleRFBServerMessage_f)(rfbClient*) = nullptr; void bind_libvnc_symbols() { - if ( libvnc_lib != nullptr ) // Safe-check + if (libvnc_lib != nullptr) // Safe-check return; libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL); - if ( !libvnc_lib ) { + if (!libvnc_lib) { Error("Error loading libvncclient: %s", dlerror()); 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); } -static char* GetPasswordCallback(rfbClient* cl){ +static char* GetPasswordCallback(rfbClient* cl) { Debug(1, "Getcredentials: %s", (*rfbClientGetClientData_f)(cl, &TAG_1)); return strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); } static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); - if ( credentialType != rfbCredentialTypeUser ) { + if (credentialType != rfbCredentialTypeUser) { free(c); return nullptr; } @@ -61,6 +61,21 @@ static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ 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( const Monitor *monitor, const std::string &host, @@ -96,22 +111,20 @@ VncCamera::VncCamera( mUser(user), 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; mImgPixFmt = AV_PIX_FMT_RGBA; - } else if ( colours == ZM_COLOUR_RGB24 ) { + } else if (colours == ZM_COLOUR_RGB24) { subpixelorder = ZM_SUBPIX_ORDER_RGB; mImgPixFmt = AV_PIX_FMT_RGB24; - } else if ( colours == ZM_COLOUR_GRAY8 ) { + } else if (colours == ZM_COLOUR_GRAY8) { subpixelorder = ZM_SUBPIX_ORDER_NONE; mImgPixFmt = AV_PIX_FMT_GRAY8; } else { Panic("Unexpected colours: %d", colours); } - if ( capture ) { + if (capture) { Debug(3, "Initializing Client"); bind_libvnc_symbols(); scale.init(); @@ -119,11 +132,15 @@ VncCamera::VncCamera( } VncCamera::~VncCamera() { - if ( capture ) { - if ( mRfb->frameBuffer ) + if (capture and mRfb) { + if (mRfb->frameBuffer) free(mRfb->frameBuffer); (*rfbClientCleanup_f)(mRfb); } + if (libvnc_lib) { + dlclose(libvnc_lib); + libvnc_lib = nullptr; + } } int VncCamera::PrimeCapture() { @@ -134,12 +151,9 @@ int VncCamera::PrimeCapture() { mVncData.width = 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->frameBuffer = nullptr; - //(uint8_t *)av_malloc(mBufferSize); - mRfb->canHandleNewFBSize = false; + mRfb->MallocFrameBuffer = resize; (*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData); (*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str()); @@ -150,16 +164,17 @@ int VncCamera::PrimeCapture() { mRfb->GetCredential = GetCredentialsCallback; mRfb->programName = "Zoneminder VNC Monitor"; + if ( mRfb->serverHost ) free(mRfb->serverHost); mRfb->serverHost = strdup(mHost.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 */ Warning("Failed to Prime capture from %s", mHost.c_str()); mRfb = nullptr; 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)", width, height, mRfb->width, mRfb->height); } @@ -170,9 +185,9 @@ int VncCamera::PrimeCapture() { int VncCamera::PreCapture() { int rc = (*WaitForMessage_f)(mRfb, 500); - if ( rc < 0 ) { + if (rc < 0) { return -1; - } else if ( !rc ) { + } else if (!rc) { return rc; } rfbBool res = (*HandleRFBServerMessage_f)(mRfb); @@ -181,12 +196,12 @@ int VncCamera::PreCapture() { } int VncCamera::Capture(ZMPacket &zm_packet) { - if ( !mVncData.buffer ) { + if (!mVncData.buffer) { Debug(1, "No buffer"); return 0; } - if ( !zm_packet.image ) { - Debug(1, "Allocating image %dx%d %dcolours = %d", width, height, colours, pixels); + if (!zm_packet.image) { + Debug(1, "Allocating image %dx%d %dcolours = %d", width, height, colours, colours*pixels); zm_packet.image = new Image(width, height, colours, subpixelorder); } zm_packet.keyframe = 1; @@ -194,9 +209,20 @@ int VncCamera::Capture(ZMPacket &zm_packet) { zm_packet.packet.stream_index = mVideoStreamId; 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( 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, width * height * colours, AV_PIX_FMT_RGBA, diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h index 089603dad..c1f847991 100644 --- a/src/zm_libvnc_camera.h +++ b/src/zm_libvnc_camera.h @@ -26,7 +26,6 @@ class VncCamera : public Camera { protected: rfbClient *mRfb; VncPrivateData mVncData; - int mBufferSize; SWScale scale; AVPixelFormat mImgPixFmt; std::string mHost; @@ -48,7 +47,7 @@ public: int p_hue, int p_colour, bool p_capture, - bool p_record_audio ); + bool p_record_audio); ~VncCamera(); diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index 616b90545..f515539a1 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -341,7 +341,7 @@ LocalCamera::LocalCamera( 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 channel_prime = true; channel_index = channel_count++; @@ -383,19 +383,19 @@ LocalCamera::LocalCamera( } #endif - if ( capture ) { - if ( last_camera ) { - 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" ); + if (capture) { + if (last_camera) { + 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"); - if ( standard != last_camera->standard ) - Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + if (standard != last_camera->standard) + Warning("Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong"); - if ( palette != last_camera->palette ) - Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + if (palette != last_camera->palette) + 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 ) - Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + 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"); } #if HAVE_LIBSWSCALE @@ -677,7 +677,8 @@ LocalCamera::LocalCamera( imgConversionContext = nullptr; } // end if capture and conversion_tye == swscale #endif - get_VideoStream(); + if ( capture and device_prime ) + Initialise(); } // end LocalCamera::LocalCamera LocalCamera::~LocalCamera() { @@ -1974,9 +1975,9 @@ int LocalCamera::Contrast( int p_contrast ) { } int LocalCamera::PrimeCapture() { - if ( primed ) return 1; - - Initialise(); + get_VideoStream(); + if ( !device_prime ) + return 1; Debug(2, "Priming capture"); #if ZM_HAS_V4L2 @@ -1994,8 +1995,10 @@ int LocalCamera::PrimeCapture() { vid_buf.memory = v4l2_data.reqbufs.memory; vid_buf.index = frame; - if ( vidioctl(vid_fd, VIDIOC_QBUF, &vid_buf) < 0 ) - Fatal("Failed to queue buffer %d: %s", frame, strerror(errno)); + if (vidioctl(vid_fd, VIDIOC_QBUF, &vid_buf) < 0) { + Error("Failed to queue buffer %d: %s", frame, strerror(errno)); + return 0; + } } 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_data.fmt.type; enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; - if ( vidioctl(vid_fd, VIDIOC_STREAMON, &type) < 0 ) - Fatal("Failed to start capture stream: %s", strerror(errno)); - } + if (vidioctl(vid_fd, VIDIOC_STREAMON, &type) < 0) { + Error("Failed to start capture stream: %s", strerror(errno)); + return -1; + } + } // end if v4l_version == 2 #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { @@ -2018,8 +2023,6 @@ int LocalCamera::PrimeCapture() { } } #endif // ZM_HAS_V4L1 - mVideoStreamId = 0; - primed = true; return 1; } // end LocalCamera::PrimeCapture @@ -2052,7 +2055,6 @@ int LocalCamera::Capture(ZMPacket &zm_packet) { memset(&vid_buf, 0, sizeof(vid_buf)); vid_buf.type = v4l2_data.fmt.type; - //vid_buf.memory = V4L2_MEMORY_MMAP; vid_buf.memory = v4l2_data.reqbufs.memory; 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]; } #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 */ @@ -2185,69 +2246,8 @@ int LocalCamera::Capture(ZMPacket &zm_packet) { } // end int LocalCamera::Capture() int LocalCamera::PostCapture() { + return 1; 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; } diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index d10840848..429b99bcf 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -21,9 +21,7 @@ #include "zm_db.h" #include "zm_utils.h" -#include -#include -#include + #include #include #include @@ -31,6 +29,11 @@ #ifdef __FreeBSD__ #include #endif +#include +#include +#include +#include + bool Logger::smInitialised = false; Logger *Logger::smInstance = nullptr; @@ -56,16 +59,15 @@ Logger::Logger() : mEffectiveLevel(NOLOG), mDbConnected(false), mLogPath(staticConfig.PATH_LOGS.c_str()), - //mLogFile( mLogPath+"/"+mId+".log" ), + // mLogFile( mLogPath+"/"+mId+".log" ), mLogFileFP(nullptr), mHasTerminal(false), mFlush(false) { - - if ( smInstance ) { + if (smInstance) { Panic("Attempt to create second instance of Logger class"); } - if ( !smInitialised ) { + if (!smInitialised) { smCodes[INFO] = "INF"; smCodes[WARNING] = "WAR"; smCodes[ERROR] = "ERR"; @@ -80,16 +82,16 @@ Logger::Logger() : smSyslogPriorities[PANIC] = LOG_ERR; char code[4] = ""; - for ( int i = DEBUG1; i <= DEBUG9; i++ ) { + for (int i = DEBUG1; i <= DEBUG9; i++) { snprintf(code, sizeof(code), "DB%d", i); smCodes[i] = code; smSyslogPriorities[i] = LOG_DEBUG; } smInitialised = true; - } + } // end if ! smInitialised - if ( fileno(stderr) && isatty(fileno(stderr)) ) { + if (fileno(stderr) && isatty(fileno(stderr))) { mHasTerminal = true; mTerminalLevel = WARNING; } @@ -100,14 +102,6 @@ Logger::~Logger() { smCodes.clear(); smSyslogPriorities.clear(); 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) { @@ -184,7 +178,7 @@ void Logger::initialise(const std::string &id, const Options &options) { } } } - } // end foreach target + } // end foreach target } else { // if we don't have debug turned on, then the max effective log level is INFO if ( tempSyslogLevel > INFO ) tempSyslogLevel = INFO; @@ -192,7 +186,7 @@ void Logger::initialise(const std::string &id, const Options &options) { if ( tempTerminalLevel > INFO ) tempTerminalLevel = INFO; if ( tempDatabaseLevel > INFO ) tempDatabaseLevel = INFO; if ( tempLevel > INFO ) tempLevel = INFO; - } // end if config.log_debug + } // end if config.log_debug logFile(tempLogFile); @@ -354,11 +348,11 @@ Logger::Level Logger::databaseLevel(Logger::Level databaseLevel) { } Logger::Level Logger::fileLevel(Logger::Level fileLevel) { - if ( fileLevel > NOOPT ) { + if (fileLevel > NOOPT) { fileLevel = limit(fileLevel); // Always close, because we may have changed file names - if ( mFileLevel > NOLOG ) - closeFile(); + if (mFileLevel > NOLOG) + closeFile(); mFileLevel = fileLevel; // 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) { - if ( syslogLevel > NOOPT ) { + if (syslogLevel > NOOPT) { syslogLevel = limit(syslogLevel); - if ( mSyslogLevel != syslogLevel ) { - if ( mSyslogLevel > NOLOG ) + if (mSyslogLevel != syslogLevel) { + if (mSyslogLevel > NOLOG) closeSyslog(); mSyslogLevel = syslogLevel; - if ( mSyslogLevel > NOLOG ) + if (mSyslogLevel > NOLOG) openSyslog(); } } @@ -382,31 +376,31 @@ Logger::Level Logger::syslogLevel(Logger::Level syslogLevel) { void Logger::logFile(const std::string &logFile) { bool addLogPid = false; std::string tempLogFile = logFile; - if ( tempLogFile[tempLogFile.length()-1] == '+' ) { + if (tempLogFile[tempLogFile.length()-1] == '+') { tempLogFile.resize(tempLogFile.length()-1); addLogPid = true; } - if ( addLogPid ) + if (addLogPid) mLogFile = stringtf("%s.%05d", tempLogFile.c_str(), getpid()); else mLogFile = tempLogFile; } void Logger::openFile() { - if ( mLogFile.size() ) { - if ( (mLogFileFP = fopen(mLogFile.c_str(), "a")) == nullptr ) { - mFileLevel = NOLOG; - Error("fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno)); - } + if (mLogFile.size()) { + if ( (mLogFileFP = fopen(mLogFile.c_str(), "a")) == nullptr ) { + mFileLevel = NOLOG; + Error("fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno)); + } } else { puts("Called Logger::openFile() without a filename"); } } void Logger::closeFile() { - if ( mLogFileFP ) { + if (mLogFileFP) { fflush(mLogFileFP); - if ( fclose(mLogFileFP) < 0 ) { + if (fclose(mLogFileFP) < 0) { mLogFileFP = nullptr; Error("fclose(), error = %s", strerror(errno)); } @@ -415,7 +409,6 @@ void Logger::closeFile() { } void Logger::closeDatabase() { - } 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, ...) { - - if ( level > mEffectiveLevel ) { - return; - } + if (level > mEffectiveLevel) return; + if (level < PANIC || level > DEBUG9) + Panic("Invalid logger level %d", level); 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 logString[8192]; va_list argPtr; struct timeval timeVal; - char *filecopy = strdup(filepath); - const char * const file = basename(filecopy); + const char *base = strrchr(filepath, '/'); + const char *file = base ? base+1 : filepath; const char *classString = smCodes[level].c_str(); - if ( level < PANIC || level > DEBUG9 ) - Panic("Invalid logger level %d", level); - gettimeofday(&timeVal, nullptr); #if 0 @@ -473,12 +463,12 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con #else #ifdef HAVE_SYSCALL #ifdef __FreeBSD_kernel__ - if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id + if ((syscall(SYS_thr_self, &tid)) < 0) // Thread/Process id # else // SOLARIS doesn't have SYS_gettid; don't assume #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 #endif // HAVE_SYSCALL @@ -512,75 +502,63 @@ void Logger::logPrint(bool hex, const char * const filepath, const int line, con char *syslogEnd = logPtr; strncpy(logPtr, "]\n", sizeof(logString)-(logPtr-logString)); - if ( level <= mTerminalLevel ) { + if (level <= mTerminalLevel) { puts(logString); fflush(stdout); } - if ( level <= mFileLevel ) { - if ( !mLogFileFP ) { - // We do this here so that we only create the file if we ever write to it. + if (level <= mFileLevel) { + if (!mLogFileFP) { + // FIXME unlocking here is a problem. Another thread could sneak in. log_mutex.unlock(); + // We do this here so that we only create the file if we ever write to it. openFile(); log_mutex.lock(); } - if ( mLogFileFP ) { + if (mLogFileFP) { fputs(logString, mLogFileFP); - if ( mFlush ) - fflush(mLogFileFP); + if (mFlush) fflush(mLogFileFP); } else { puts("Logging to file, but failed to open it\n"); } } // end if level <= mFileLevel - if ( level <= mDatabaseLevel ) { - if (db_mutex.try_lock_for(1)) { - char escapedString[(strlen(syslogStart)*2)+1]; - mysql_real_escape_string(&dbconn, escapedString, syslogStart, strlen(syslogStart)); + if (level <= mDatabaseLevel) { + if (zmDbConnected) { + int syslogSize = syslogEnd-syslogStart; + char escapedString[(syslogSize*2)+1]; + mysql_real_escape_string(&dbconn, escapedString, syslogStart, syslogSize); - char sql[ZM_SQL_MED_BUFSIZ]; - snprintf(sql, sizeof(sql), + std::string sql_string = stringtf( "INSERT INTO `Logs` " "( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )" - " VALUES " - "( %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 - ); - if ( mysql_query(&dbconn, sql) ) { - Level tempDatabaseLevel = mDatabaseLevel; - databaseLevel(NOLOG); - Error("Can't insert log entry: sql(%s) error(%s)", sql, mysql_error(&dbconn)); - databaseLevel(tempDatabaseLevel); - } - db_mutex.unlock(); + " VALUES " + "( %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 + ); + dbQueue.push(sql_string); } else { - Level tempDatabaseLevel = mDatabaseLevel; - databaseLevel(NOLOG); - Error("Can't insert log entry since the DB lock could not be obtained. Message: %s", syslogStart); - databaseLevel(tempDatabaseLevel); + puts("Db is closed"); } } // end if level <= mDatabaseLevel - if ( level <= mSyslogLevel ) { + if (level <= mSyslogLevel) { *syslogEnd = '\0'; syslog(smSyslogPriorities[level], "%s [%s] [%s]", classString, mId.c_str(), syslogStart); } - free(filecopy); log_mutex.unlock(); - if ( level <= FATAL ) { + if (level <= FATAL) { logTerm(); zmDbClose(); - if ( level <= PANIC ) - abort(); + if (level <= PANIC) abort(); exit(-1); } } // end logPrint void logInit(const char *name, const Logger::Options &options) { - if ( Logger::smInstance ) { + if (Logger::smInstance) { delete Logger::smInstance; - Logger::smInstance = nullptr; } Logger::smInstance = new Logger(); @@ -588,7 +566,7 @@ void logInit(const char *name, const Logger::Options &options) { } void logTerm() { - if ( Logger::smInstance ) { + if (Logger::smInstance) { delete Logger::smInstance; Logger::smInstance = nullptr; } diff --git a/src/zm_logger.h b/src/zm_logger.h index 6350449a5..ee358b206 100644 --- a/src/zm_logger.h +++ b/src/zm_logger.h @@ -153,18 +153,12 @@ public: void terminate(); const std::string &id(const std::string &id); - const std::string &id() const { - return mId; - } + const std::string &id() const { return mId; } - Level level() const { - return mLevel; - } + Level level() const { return mLevel; } Level level(Level=NOOPT); - bool debugOn() const { - return mEffectiveLevel >= DEBUG1; - } + bool debugOn() const { return mEffectiveLevel >= DEBUG1; } Level terminalLevel(Level=NOOPT); Level databaseLevel(Level=NOOPT); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 9715b9361..509f6ebcc 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -651,6 +651,7 @@ void Monitor::LoadCamera() { switch (type) { case LOCAL: { +#if ZM_HAS_V4L int extras = (deinterlacing >> 24) & 0xff; camera = ZM::make_unique(this, @@ -672,6 +673,9 @@ void Monitor::LoadCamera() { record_audio, extras ); +#else + Fatal("Not compiled with local v4l camera support"); +#endif break; } case REMOTE: { @@ -1071,11 +1075,6 @@ Monitor::~Monitor() { Close(); if (mem_ptr != nullptr) { - std::lock_guard lck(event_mutex); - if (event) { - Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id()); - closeEvent(); - } if (purpose != QUERY) { shared_data->state = state = IDLE; shared_data->last_read_index = image_buffer_count; @@ -1317,25 +1316,17 @@ void Monitor::actionReload() { void Monitor::actionEnable() { shared_data->action |= RELOAD; - db_mutex.lock(); - static char sql[ZM_SQL_SML_BUFSIZ]; + char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %d", id); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - db_mutex.unlock(); + zmDbDo(sql); } void Monitor::actionDisable() { 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); - db_mutex.lock(); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - db_mutex.unlock(); + zmDbDo(sql); } void Monitor::actionSuspend() { @@ -1705,7 +1696,6 @@ void Monitor::UpdateCaptureFPS() { last_fps_time = now_double; last_capture_image_count = image_count; - db_mutex.lock(); static char sql[ZM_SQL_SML_BUFSIZ]; // The reason we update the Status as well is because if mysql restarts, the Monitor_Status table is lost, // and nothing else will update the status until zmc restarts. Since we are successfully capturing we can @@ -1715,10 +1705,7 @@ void Monitor::UpdateCaptureFPS() { "VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE " "CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected'", id, new_capture_fps, new_capture_bandwidth, new_capture_fps, new_capture_bandwidth); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - db_mutex.unlock(); + dbQueue.push(sql); } // now != last_fps_time } // end if report fps } // void Monitor::UpdateCaptureFPS() @@ -1759,11 +1746,7 @@ void Monitor::UpdateAnalysisFPS() { "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf)" " ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, new_analysis_fps, new_analysis_fps); - db_mutex.lock(); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - db_mutex.unlock(); + dbQueue.push(sql); last_analysis_fps_time = now_double; last_motion_frame_count = motion_frame_count; } else { @@ -1844,7 +1827,7 @@ bool Monitor::Analyse() { } // end if trigger_on if ( signal_change ) { - Debug(2, "Signal change"); + Debug(2, "Signal change, new signal is %d", signal); const char *signalText = "Unknown"; if ( !signal ) { 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 ( event ) { - Debug(2, "Have event in mocord"); + Debug(2, "Have event %" PRIu64 " in mocord", event->Id()); if ( section_length && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) && ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) ) @@ -2233,6 +2216,7 @@ bool Monitor::Analyse() { shared_data->last_read_time = time(nullptr); analysis_image_count++; UpdateAnalysisFPS(); + packetqueue.clearPackets(snap); return true; } // end Monitor::Analyse @@ -2338,7 +2322,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) { for ( int i = 0; i < n_link_ids; i++ ) { Debug(1, "Checking linked monitor %d", link_ids[i]); - db_mutex.lock(); + std::lock_guard lck(db_mutex); static char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), "SELECT `Id`, `Name` FROM `Monitors`" @@ -2347,14 +2331,12 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) { " AND `Function` != 'Monitor'" " AND `Enabled`=1", link_ids[i]); - if ( mysql_query(&dbconn, sql) ) { - db_mutex.unlock(); + if (mysql_query(&dbconn, sql)) { Error("Can't run query: %s", mysql_error(&dbconn)); continue; } MYSQL_RES *result = mysql_store_result(&dbconn); - db_mutex.unlock(); if ( !result ) { Error("Can't use query result: %s", mysql_error(&dbconn)); continue; @@ -2368,11 +2350,11 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) { Warning("Can't link to monitor %d, invalid id, function or not enabled", link_ids[i]); } mysql_free_result(result); - } // end foreach link_id + } // end foreach link_id n_linked_monitors = count; - } // end if has link_ids - } // end if p_linked_monitors -} // end void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) + } // end if has link_ids + } // end if p_linked_monitors +} // end void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) std::vector> Monitor::LoadMonitors(std::string sql, Purpose purpose) { Debug(1, "Loading Monitors with %s", sql.c_str()); @@ -2516,6 +2498,7 @@ int Monitor::Capture() { Debug(2, "Queueing audio packet"); packetqueue.queuePacket(packet); } else { + Debug(4, "Not Queueing audio packet"); delete packet; } // 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::PostCapture() const { return camera->PostCapture(); } int Monitor::Close() { + std::lock_guard lck(event_mutex); + if (event) { + Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name, image_count, event->Id()); + closeEvent(); + } if (camera) camera->Close(); packetqueue.clear(); return 1; } + Monitor::Orientation Monitor::getOrientation() const { return orientation; } // Wait for camera to get an image, and then assign it as the base reference image. diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 4bbc8d3a6..09cdb5f5c 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -24,7 +24,11 @@ #include "zm_time.h" #include #include +#include #include +#ifdef __FreeBSD__ +#include +#endif const int MAX_SLEEP_USEC = 1000000; // 1 sec diff --git a/src/zm_monitorstream.h b/src/zm_monitorstream.h index 241c8cf2b..0c1f92777 100644 --- a/src/zm_monitorstream.h +++ b/src/zm_monitorstream.h @@ -47,7 +47,7 @@ class MonitorStream : public StreamBase { bool checkSwapPath(const char *path, bool create_path); bool sendFrame(const char *filepath, 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 SingleImageRaw(int scale=100); #ifdef HAVE_ZLIB_H diff --git a/src/zm_mpeg.h b/src/zm_mpeg.h index 6e75f8743..4999f1328 100644 --- a/src/zm_mpeg.h +++ b/src/zm_mpeg.h @@ -21,6 +21,7 @@ #define ZM_MPEG_H #include "zm_ffmpeg.h" +#include #if HAVE_LIBAVCODEC diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 2126c295c..133747ea2 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -159,90 +159,95 @@ int ZMPacket::decode(AVCodecContext *ctx) { return 0; } int bytes_consumed = ret; - if ( ret > 0 ) + if ( ret > 0 ) { zm_dump_video_frame(in_frame, "got frame"); #if HAVE_LIBAVUTIL_HWCONTEXT_H #if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0) - if ( ctx->sw_pix_fmt != in_frame->format ) { - Debug(1, "Have different format %s != %s.", - av_get_pix_fmt_name(ctx->pix_fmt), - av_get_pix_fmt_name(ctx->sw_pix_fmt) - ); + if ( fix_deprecated_pix_fmt(ctx->sw_pix_fmt) != fix_deprecated_pix_fmt(static_cast(in_frame->format)) ) { + 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->sw_pix_fmt), + av_get_pix_fmt_name(static_cast(in_frame->format)) + ); #if 0 - if ( target_format == AV_PIX_FMT_NONE and ctx->hw_frames_ctx and (image->Colours() == 4) ) { - // Look for rgb0 in list of supported formats - enum AVPixelFormat *formats; - if ( 0 <= av_hwframe_transfer_get_formats( - ctx->hw_frames_ctx, - AV_HWFRAME_TRANSFER_DIRECTION_FROM, - &formats, - 0 - ) ) { - for (int i = 0; formats[i] != AV_PIX_FMT_NONE; i++) { - Debug(1, "Available dest formats %d %s", - formats[i], - av_get_pix_fmt_name(formats[i]) - ); - if ( formats[i] == AV_PIX_FMT_RGB0 ) { - target_format = formats[i]; - break; - } // endif RGB0 - } // end foreach support format - av_freep(&formats); - } // endif success getting list of formats - } // end if target_format not set + if ( target_format == AV_PIX_FMT_NONE and ctx->hw_frames_ctx and (image->Colours() == 4) ) { + // Look for rgb0 in list of supported formats + enum AVPixelFormat *formats; + if ( 0 <= av_hwframe_transfer_get_formats( + ctx->hw_frames_ctx, + AV_HWFRAME_TRANSFER_DIRECTION_FROM, + &formats, + 0 + ) ) { + for (int i = 0; formats[i] != AV_PIX_FMT_NONE; i++) { + Debug(1, "Available dest formats %d %s", + formats[i], + av_get_pix_fmt_name(formats[i]) + ); + if ( formats[i] == AV_PIX_FMT_RGB0 ) { + target_format = formats[i]; + break; + } // endif RGB0 + } // end foreach support format + av_freep(&formats); + } // endif success getting list of formats + } // end if target_format not set #endif - AVFrame *new_frame = zm_av_frame_alloc(); + AVFrame *new_frame = zm_av_frame_alloc(); #if 0 - if ( target_format == AV_PIX_FMT_RGB0 ) { - if ( image ) { - if ( 0 > image->PopulateFrame(new_frame) ) { - delete new_frame; - new_frame = zm_av_frame_alloc(); - delete image; - image = nullptr; - new_frame->format = target_format; + if ( target_format == AV_PIX_FMT_RGB0 ) { + if ( image ) { + if ( 0 > image->PopulateFrame(new_frame) ) { + delete new_frame; + new_frame = zm_av_frame_alloc(); + delete image; + image = nullptr; + new_frame->format = target_format; + } } } - } #endif - /* retrieve data from GPU to CPU */ - zm_dump_video_frame(in_frame, "Before hwtransfer"); - ret = av_hwframe_transfer_data(new_frame, in_frame, 0); - if ( ret < 0 ) { - Error("Unable to transfer frame: %s, continuing", - av_make_error_string(ret).c_str()); - av_frame_free(&in_frame); - av_frame_free(&new_frame); - return 0; - } - new_frame->pts = in_frame->pts; - zm_dump_video_frame(new_frame, "After hwtransfer"); + /* retrieve data from GPU to CPU */ + zm_dump_video_frame(in_frame, "Before hwtransfer"); + ret = av_hwframe_transfer_data(new_frame, in_frame, 0); + if ( ret < 0 ) { + Error("Unable to transfer frame: %s, continuing", + av_make_error_string(ret).c_str()); + av_frame_free(&in_frame); + av_frame_free(&new_frame); + return 0; + } + 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"); #if 0 - if ( new_frame->format == AV_PIX_FMT_RGB0 ) { - new_frame->format = AV_PIX_FMT_RGBA; - zm_dump_video_frame(new_frame, "After hwtransfer setting to rgba"); - } + if ( new_frame->format == AV_PIX_FMT_RGB0 ) { + new_frame->format = AV_PIX_FMT_RGBA; + zm_dump_video_frame(new_frame, "After hwtransfer setting to rgba"); + } #endif - av_frame_free(&in_frame); - in_frame = new_frame; - } else + av_frame_free(&in_frame); + in_frame = new_frame; + } else #endif #endif - if ( ret > 0 ) { - 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->sw_pix_fmt) - ); + 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->sw_pix_fmt) + ); #if 0 if ( image ) { image->Assign(in_frame); } #endif - } + } // end if if ( ret > 0 ) { return bytes_consumed; } // end ZMPacket::decode diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index cd60b5c40..c89347692 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -99,7 +99,15 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) { --(*iterator_it); } } // 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. // No good. Have to satisfy two conditions: // 1. packetqueue starts with a video keyframe @@ -109,77 +117,74 @@ bool PacketQueue::queuePacket(ZMPacket* add_packet) { // // So start at the beginning, counting video packets until the next keyframe. // Then if deleting those packets doesn't break 1 and 2, then go ahead and delete them. - if ( add_packet->packet.stream_index == video_stream_id + if ( ! ( + add_packet->packet.stream_index == video_stream_id and add_packet->keyframe and (packet_counts[video_stream_id] > max_video_packet_count) and *(pktQueue.begin()) != add_packet + ) ) { - packetqueue_iterator it = pktQueue.begin(); - packetqueue_iterator next_front = pktQueue.begin(); + return; + } + std::unique_lock lck(mutex); - // First packet is special because we know it is a video keyframe and only need to check for lock - ZMPacket *zm_packet = *it; - if ( zm_packet->trylock() ) { - ++it; + packetqueue_iterator it = pktQueue.begin(); + packetqueue_iterator next_front = pktQueue.begin(); + + // First packet is special because we know it is a video keyframe and only need to check for lock + ZMPacket *zm_packet = *it; + if ( zm_packet->trylock() ) { + ++it; + zm_packet->unlock(); + + // 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 ) { + zm_packet = *it; + if ( !zm_packet->trylock() ) { + break; + } zm_packet->unlock(); - // 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 ) { - zm_packet = *it; - Debug(1, "Checking packet to see if we can delete them"); - if ( !zm_packet->trylock() ) { - Debug(1, "Have locked packet %d", zm_packet->image_index); - break; - } - zm_packet->unlock(); - - if ( is_there_an_iterator_pointing_to_packet(zm_packet) ) { - Debug(4, "Found IT at beginning of queue. Threads not keeping up"); - break; - } - - if ( zm_packet->packet.stream_index == video_stream_id ) { - if ( zm_packet->keyframe ) { - Debug(1, "Have a video keyframe so breaking out"); - next_front = it; - } - } - it++; - } // end while - } // end if first packet not locked - Debug(1, "Resulting pointing at latest packet? %d, have next front? %d", - ( *it == add_packet ), - ( 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 ) { - ZMPacket *zm_packet = *pktQueue.begin(); - if ( !zm_packet ) { - Error("NULL zm_packet in queue"); - continue; - } - - Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%d", - zm_packet->packet.stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count, pktQueue.size()); - pktQueue.pop_front(); - packet_counts[zm_packet->packet.stream_index] -= 1; - delete zm_packet; + if ( is_there_an_iterator_pointing_to_packet(zm_packet) ) { + Warning("Found iterator at beginning of queue. Some thread isn't keeping up"); + break; } - } // end if have at least max_video_packet_count video packets remaining - } // end if this is a video keyframe - mutex.unlock(); + if ( zm_packet->packet.stream_index == video_stream_id ) { + if ( zm_packet->keyframe ) { + Debug(1, "Have a video keyframe so setting next front to it"); + next_front = it; + } + } + it++; + } // end while + } // end if first packet not locked + Debug(1, "Resulting pointing at latest packet? %d, next front points to begin? %d", + ( *it == add_packet ), + ( next_front == pktQueue.begin() ) + ); + if ( next_front != pktQueue.begin() ) { + while ( pktQueue.begin() != next_front ) { + ZMPacket *zm_packet = *pktQueue.begin(); + if ( !zm_packet ) { + Error("NULL zm_packet in queue"); + continue; + } + + Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%d", + zm_packet->packet.stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count, pktQueue.size()); + pktQueue.pop_front(); + packet_counts[zm_packet->packet.stream_index] -= 1; + delete zm_packet; + } + } // end if have at least max_video_packet_count video packets remaining // 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) + return; +} // end voidPacketQueue::clearPackets(ZMPacket* zm_packet) ZMPacket* PacketQueue::popPacket( ) { Debug(4, "pktQueue size %d", pktQueue.size()); @@ -421,11 +426,11 @@ unsigned int PacketQueue::size() { int PacketQueue::packet_count(int 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 packet_counts[stream_id]; -} // end int PacketQueue::packet_count(int stream_id) +} // end int PacketQueue::packet_count(int stream_id) // Returns a packet. Packet will be locked @@ -461,7 +466,7 @@ ZMPacket *PacketQueue::get_packet(packetqueue_iterator *it) { } Debug(2, "Locked packet, unlocking packetqueue mutex"); return p; -} // end ZMPacket *PacketQueue::get_packet(it) +} // end ZMPacket *PacketQueue::get_packet(it) bool PacketQueue::increment_it(packetqueue_iterator *it) { Debug(2, "Incrementing %p, queue size %d, end? %d", it, pktQueue.size(), ((*it) == pktQueue.end())); @@ -500,6 +505,8 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( unsigned int pre_event_count ) { + std::unique_lock lck(mutex); + packetqueue_iterator *it = new packetqueue_iterator; iterators.push_back(it); diff --git a/src/zm_packetqueue.h b/src/zm_packetqueue.h index 4cbd0739d..cd88648b9 100644 --- a/src/zm_packetqueue.h +++ b/src/zm_packetqueue.h @@ -63,6 +63,7 @@ class PacketQueue { 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 clearPackets(ZMPacket *); int packet_count(int stream_id); bool increment_it(packetqueue_iterator *it); diff --git a/src/zm_remote_camera.cpp b/src/zm_remote_camera.cpp index ff26b580c..c1a252c3d 100644 --- a/src/zm_remote_camera.cpp +++ b/src/zm_remote_camera.cpp @@ -22,6 +22,7 @@ #include "zm_utils.h" #include #include +#include RemoteCamera::RemoteCamera( const Monitor *monitor, diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index b520e21e9..5304a77ac 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -26,6 +26,7 @@ #include "zm_utils.h" #include #include +#include #ifdef SOLARIS #include // FIONREAD and friends @@ -540,10 +541,8 @@ int RemoteCameraHttp::GetResponse() { } case HEADERCONT : case SUBHEADERCONT : - { // Ignore break; - } } } } else @@ -1062,7 +1061,6 @@ int RemoteCameraHttp::PreCapture() { if ( mode == SINGLE_IMAGE ) { if ( SendRequest() < 0 ) { Error("Unable to send request"); - Disconnect(); return -1; } } @@ -1072,12 +1070,11 @@ int RemoteCameraHttp::PreCapture() { int RemoteCameraHttp::Capture(ZMPacket &packet) { int content_length = GetResponse(); if ( content_length == 0 ) { - Warning( "Unable to capture image, retrying" ); + Warning("Unable to capture image, retrying"); return 0; } if ( content_length < 0 ) { - Error( "Unable to get response, disconnecting" ); - Disconnect(); + Error("Unable to get response, disconnecting"); return -1; } @@ -1094,7 +1091,6 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) { case JPEG : if ( !image->DecodeJpeg(buffer.extract(content_length), content_length, colours, subpixelorder) ) { Error("Unable to decode jpeg"); - Disconnect(); return -1; } break; @@ -1102,7 +1098,6 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) { if ( content_length != (long)image->Size() ) { Error("Image length mismatch, expected %d bytes, content length was %d", image->Size(), content_length); - Disconnect(); return -1; } image->Assign(width, height, colours, subpixelorder, buffer, imagesize); @@ -1110,14 +1105,12 @@ int RemoteCameraHttp::Capture(ZMPacket &packet) { case X_RGBZ : if ( !image->Unzip( buffer.extract( content_length ), content_length ) ) { Error("Unable to unzip RGB image"); - Disconnect(); return -1; } image->Assign(width, height, colours, subpixelorder, buffer, imagesize); break; default : Error("Unexpected image format encountered"); - Disconnect(); return -1; } return 1; diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index c30428007..2390155c2 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -91,25 +91,7 @@ int SWScale::Convert( AVFrame *out_frame ) { - // THe J formats are deprecated, so we need to convert - 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; - } + AVPixelFormat format = fix_deprecated_pix_fmt((AVPixelFormat)in_frame->format); /* Get the context */ swscale_ctx = sws_getCachedContext(swscale_ctx, in_frame->width, in_frame->height, format, @@ -120,7 +102,9 @@ int SWScale::Convert( return -6; } /* 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"); return -10; } @@ -140,8 +124,9 @@ int SWScale::Convert( unsigned int new_width, 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", - in_buffer, in_buffer_size, out_buffer, out_buffer_size, width, height, new_width, 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 %d %d", + in_buffer, in_buffer_size, out_buffer, out_buffer_size, width, height, new_width, new_height, + in_pf, out_pf); /* Parameter checking */ if ( in_buffer == nullptr ) { Error("NULL Input buffer"); @@ -160,23 +145,7 @@ int SWScale::Convert( return -3; } - // THe J formats are deprecated, so we need to convert - 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; - } + in_pf = fix_deprecated_pix_fmt(in_pf); #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) /* Warn if the input or output pixelformat is not supported */ @@ -190,21 +159,24 @@ int SWScale::Convert( } #endif + int alignment = 1; /* Check the buffer sizes */ - size_t insize = GetBufferSize(in_pf, width, height); - if ( 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); + size_t needed_insize = GetBufferSize(in_pf, width, height); + if ( needed_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); - if ( outsize < out_buffer_size ) { - Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); + size_t needed_outsize = GetBufferSize(out_pf, new_width, new_height); + if ( needed_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; } /* Get the context */ - swscale_ctx = sws_getCachedContext( - swscale_ctx, width, height, in_pf, new_width, new_height, - out_pf, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); + swscale_ctx = sws_getCachedContext(swscale_ctx, + width, height, in_pf, + new_width, new_height, out_pf, + SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); if ( swscale_ctx == nullptr ) { Error("Failed getting swscale context"); return -6; @@ -212,8 +184,8 @@ int SWScale::Convert( /* Fill in the buffers */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - if ( av_image_fill_arrays(input_avframe->data, input_avframe->linesize, - (uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) { + if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize, + (uint8_t*) in_buffer, in_pf, width, height, alignment) <= 0) { #else if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer, in_pf, width, height) <= 0) { @@ -222,10 +194,10 @@ int SWScale::Convert( return -7; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - if ( av_image_fill_arrays(output_avframe->data, output_avframe->linesize, - out_buffer, out_pf, new_width, new_height, 1) <= 0) { + if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, + out_buffer, out_pf, new_width, new_height, alignment) <= 0) { #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) { #endif Error("Failed filling output frame with output buffer"); @@ -235,7 +207,8 @@ int SWScale::Convert( /* Do the conversion */ if ( !sws_scale(swscale_ctx, 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"); 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) { #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 return outsize = avpicture_get_size(pf, width,height); #endif diff --git a/src/zm_thread.cpp b/src/zm_thread.cpp index f6c6cdb7f..63a8bdbb2 100644 --- a/src/zm_thread.cpp +++ b/src/zm_thread.cpp @@ -21,6 +21,7 @@ #include "zm_logger.h" #include "zm_utils.h" +#include #include #include #include diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 98bf8692e..0a3cc6002 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -190,12 +190,8 @@ bool VideoStore::open() { #endif } - video_out_ctx->time_base = video_in_ctx ? video_in_ctx->time_base : AV_TIME_BASE_Q; - 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; - } - + // When encoding, we are going to use the timestamp values instead of packet pts/dts + video_out_ctx->time_base = AV_TIME_BASE_Q; video_out_ctx->codec_id = codec_data[i].codec_id; video_out_ctx->pix_fmt = codec_data[i].pix_fmt; video_out_ctx->level = 32; @@ -209,14 +205,6 @@ bool VideoStore::open() { video_out_ctx->bit_rate = 2000000; video_out_ctx->gop_size = 12; 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 ) { /* just for testing, we also add B frames */ 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 ) { - Warning("Can't open video codec (%s) %s", - video_out_codec->name, - av_make_error_string(ret).c_str() - ); + if ( wanted_encoder != "" and wanted_encoder != "auto" ) { + Warning("Can't open video codec (%s) %s", + video_out_codec->name, + 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; } @@ -254,6 +249,7 @@ bool VideoStore::open() { } //av_dict_free(&opts); if ( video_out_codec ) break; + avcodec_free_context(&video_out_ctx); } // end foreach 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 }; // 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->key_frame = zm_packet->keyframe; + //zm_packet->out_frame->key_frame = zm_packet->keyframe; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) zm_packet->out_frame->pkt_duration = 0; #endif + int64_t in_pts = zm_packet->timestamp->tv_sec * (uint64_t)1000000 + zm_packet->timestamp->tv_usec; 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)", video_first_pts, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec); zm_packet->out_frame->pts = 0; } else { - uint64_t useconds = ( zm_packet->timestamp->tv_sec * (uint64_t)1000000 + zm_packet->timestamp->tv_usec ) - video_first_pts; - zm_packet->out_frame->pts = av_rescale_q(useconds, video_in_stream->time_base, video_out_ctx->time_base); - Debug(2, " Setting pts for frame(%d) to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%d) usecs(%d)", - frame_count, zm_packet->out_frame->pts, video_first_pts, useconds, zm_packet->timestamp->tv_sec, zm_packet->timestamp->tv_usec); + uint64_t useconds = in_pts - video_first_pts; + 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) @ %d/%d", + 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); @@ -1097,7 +1097,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { } else { // Passthrough 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 av_init_packet(&opkt); opkt.data = ipkt->data; @@ -1149,10 +1149,8 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) { 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 - 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 ) { // 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.duration = ipkt->duration; - opkt.pts = ipkt->pts; - opkt.dts = ipkt->dts; + opkt.pts = ipkt->pts - audio_first_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); ZM_DUMP_STREAM_PACKET(audio_out_stream, opkt, "after stream pts adjustment"); write_packet(&opkt, audio_out_stream); diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index e0a7c3ecb..21d99295d 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -134,16 +134,11 @@ Zone::~Zone() { void Zone::RecordStats(const Event *event) { static char sql[ZM_SQL_MED_BUFSIZ]; - db_mutex.lock(); 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", 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); - db_mutex.unlock(); - if ( rc ) { - Error("Can't insert event stats: %s", mysql_error(&dbconn)); - } + zmDbDo(sql); } // end void Zone::RecordStats( const Event *event ) bool Zone::CheckOverloadCount() { @@ -824,23 +819,24 @@ bool Zone::ParseZoneString(const char *zone_string, int &zone_id, int &colour, P int Zone::Load(Monitor *monitor, Zone **&zones) { static char sql[ZM_SQL_MED_BUFSIZ]; + MYSQL_RES *result; - db_mutex.lock(); - snprintf(sql, sizeof(sql), "SELECT Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0," - "MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels," - "FilterX,FilterY,MinFilterPixels,MaxFilterPixels," - "MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs," - "OverloadFrames,ExtendAlarmFrames" - " FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id()); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - db_mutex.unlock(); - return 0; + { // scope for lock + std::lock_guard lck(db_mutex); + snprintf(sql, sizeof(sql), "SELECT Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0," + "MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels," + "FilterX,FilterY,MinFilterPixels,MaxFilterPixels," + "MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs," + "OverloadFrames,ExtendAlarmFrames" + " FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id()); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + return 0; + } + + 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)); 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()); delete[] 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 Id = atoi(dbrow[col++]); diff --git a/src/zmc.cpp b/src/zmc.cpp index 4b1370442..3f0b37bfe 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -214,7 +214,6 @@ int main(int argc, char *argv[]) { exit(-1); } else { Debug(2, "%d monitors loaded", monitors.size()); - } Info("Starting Capture version %s", ZM_VERSION); @@ -230,10 +229,9 @@ int main(int argc, char *argv[]) { sigaddset(&block_set, SIGUSR2); int result = 0; - int prime_capture_log_count = 0; - while ( !zm_terminate ) { + while (!zm_terminate) { result = 0; static char sql[ZM_SQL_SML_BUFSIZ]; @@ -247,37 +245,29 @@ int main(int argc, char *argv[]) { monitor->setStartupTime(now); 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", - monitor->Id()); - if (mysql_query(&dbconn, sql)) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - } + "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()); + zmDbDo(sql); - for (const std::shared_ptr &monitor : monitors) { - - // Outer primary loop, handles connection to camera - if (monitor->PrimeCapture() <= 0) { + while (monitor->PrimeCapture() <= 0) { if (prime_capture_log_count % 60) { Error("Failed to prime capture of initial monitor"); } else { Debug(1, "Failed to prime capture of initial monitor"); } prime_capture_log_count ++; - monitor->disconnect(); - if (!zm_terminate) { - Debug(1, "Sleeping"); - sleep(5); - } - continue; + if (zm_terminate) break; + sleep(1); } + if (zm_terminate) break; + snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'", - monitor->Id()); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - } + monitor->Id()); + zmDbDo(sql); + } // end foreach monitor + if (zm_terminate) break; #if HAVE_RTSP_SERVER RTSPServerThread ** rtsp_server_threads = nullptr; @@ -303,12 +293,12 @@ int main(int argc, char *argv[]) { capture_delays[i], alarm_capture_delays[i]); 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()); analysis_threads.emplace_back(ZM::make_unique(monitors[i])); } #if HAVE_RTSP_SERVER - if ( rtsp_server_threads ) { + if (rtsp_server_threads) { rtsp_server_threads[i] = new RTSPServerThread(monitors[i]); rtsp_server_threads[i]->addStream(monitors[i]->GetVideoStream(), monitors[i]->GetAudioStream()); rtsp_server_threads[i]->start(); @@ -366,17 +356,12 @@ int main(int argc, char *argv[]) { } } // end if has a last_capture time last_capture_times[i] = now; - } // end foreach n_monitors - if (result < 0) { + if (result < 0 or zm_reload) { // Failure, try reconnecting break; } - - if ( zm_reload ) { - break; - } } // end while ! zm_terminate and connected for (std::unique_ptr &analysis_thread: analysis_threads) @@ -413,7 +398,7 @@ int main(int argc, char *argv[]) { delete [] capture_delays; delete [] last_capture_times; - if (result < 0) { + if (result < 0 and !zm_terminate) { // Failure, try reconnecting Debug(1, "Sleeping for 5"); sleep(5); @@ -435,9 +420,7 @@ int main(int argc, char *argv[]) { snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%d, 'Connected') ON DUPLICATE KEY UPDATE Status='NotRunning'", monitor->Id()); - if (mysql_query(&dbconn, sql)) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } + zmDbDo(sql); } Image::Deinitialise(); diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 0bd63fb46..14292374c 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef +Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938 diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 934c66aa8..b8e2d4088 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -349,6 +349,8 @@ class Filter extends ZM_Object { '![]' => 2, 'and' => 3, 'or' => 4, + 'IS' => 2, + 'IS NOT' => 2, ); for ( $i = 0; $i < count($terms); $i++ ) { @@ -476,6 +478,7 @@ class Filter extends ZM_Object { } } # end if attr + $sqlValue = ''; if ( isset($term['op']) ) { if ( empty($term['op']) ) { $term['op'] = '='; @@ -507,11 +510,11 @@ class Filter extends ZM_Object { case 'IS' : case 'IS NOT' : if ( $term['val'] == 'Odd' ) { - $sqlValue .= ' % 2 = 1'; + $sqlValue = ' % 2 = 1'; } else if ( $term['val'] == 'Even' ) { - $sqlValue .= ' % 2 = 0'; + $sqlValue = ' % 2 = 0'; } else { - $sqlValue .= ' '.$term['op']; + $sqlValue = ' '.$term['op']; } break; default : @@ -522,10 +525,10 @@ class Filter extends ZM_Object { if ( !count($postfixStack) ) { $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); 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); 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 ); break; } else { @@ -537,6 +540,7 @@ class Filter extends ZM_Object { if ( isset($term['val']) ) { $valueList = array(); foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { + $value_upper = strtoupper($value); switch ( $term['attr'] ) { case 'MonitorName': case 'Name': @@ -551,9 +555,9 @@ class Filter extends ZM_Object { case 'FilterServerId': case 'StorageServerId': case 'ServerId': - if ( $value == 'ZM_SERVER_ID' ) { + if ( $value_upper == 'ZM_SERVER_ID' ) { $value = ZM_SERVER_ID; - } else if ( $value == 'NULL' ) { + } else if ( $value_upper == 'NULL' ) { } else { $value = dbEscape($value); @@ -567,7 +571,8 @@ class Filter extends ZM_Object { case 'DateTime': case 'EndDateTime': case 'StartDateTime': - $value = "'".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."'"; + if ( $value_upper != 'NULL' ) + $value = "'".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."'"; break; case 'Date': case 'EndDate': @@ -580,7 +585,7 @@ class Filter extends ZM_Object { $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; default : - if ( $value != 'NULL' ) + if ( $value_upper != 'NULL' ) $value = dbEscape($value); } // end switch attribute $valueList[] = $value; diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php index 42f939128..c6c3cf312 100644 --- a/web/includes/FilterTerm.php +++ b/web/includes/FilterTerm.php @@ -68,7 +68,7 @@ class FilterTerm { $vals = is_array($this->val) ? $this->val : preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $this->val)); foreach ( $vals as $value ) { - + $value_upper = strtoupper($value); switch ( $this->attr ) { case 'AlarmedZoneId': @@ -96,36 +96,36 @@ class FilterTerm { case 'ServerId': if ( $value == 'ZM_SERVER_ID' ) { $value = ZM_SERVER_ID; - } else if ( $value == 'NULL' ) { + } else if ( $value_upper == 'NULL' ) { } else { $value = dbEscape($value); } break; case 'StorageId': - if ( $value != 'NULL' ) { + if ( $value_upper != 'NULL' ) { $value = dbEscape($value); } break; case 'DateTime': case 'StartDateTime': case 'EndDateTime': - if ( $value != 'NULL' ) + if ( $value_upper != 'NULL' ) $value = '\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\''; break; case 'Date': case 'StartDate': case 'EndDate': - if ( $value == 'CURDATE()' or $value == 'NOW()' ) { + if ( $value_upper == 'CURDATE()' or $value_upper == 'NOW()' ) { $value = 'to_days('.$value.')'; - } else if ( $value != 'NULL' ) { + } else if ( $value_upper != 'NULL' ) { $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; } break; case 'Time': case 'StartTime': case 'EndTime': - if ( $value != 'NULL' ) + if ( $value_upper != 'NULL' ) $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; default : @@ -133,7 +133,7 @@ class FilterTerm { $value = 1; } else if ( $value == 'Even' ) { $value = 0; - } else if ( $value != 'NULL' ) + } else if ( $value_upper != 'NULL' ) $value = dbEscape($value); break; } diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 71bc021d2..11519b0e1 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -132,19 +132,7 @@ class Storage extends ZM_Object { 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. 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())); - - 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; + $this->{'DiskSpace'} = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id())); } return $this->{'DiskSpace'}; } // end function event_disk_space diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 503f3f155..734eb1b11 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -867,6 +867,7 @@ function xhtmlFooter() { 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=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;hb.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]*)>)/gi,"\u2060"),n=d("
").html(m).contents();b=!1;m="";d.each(n.text().split("\u2028"),function(b,c){0b?1:0)).split(".");1===n.length&&(n[1]="");var t=3b?"-":"")+(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 0l?f+=String.fromCharCode(l):(127l?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>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"+D(a,d,e)+""});n++});Q+="";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+=""+D(b,d,g)+"";a++});0"!==t&&(Q+=''+t+"",Fa++);n++});Q+="";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,"
"));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'+d("
").text(c).html()+"\r";f++}});0\r'+t+"\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("
").text(e).html());!1===a.exportHiddenCells&&(K=c.find("tr, th, td").filter(":hidden"),U=0\r";e=b(y(c),"th,td",0);b(C(c),"td,th",e);H+="\r";sa.push(H)});k={};z={};for(var m,Z,X=0,da=F.length;X\r\r\r\r '+(new Date).toISOString()+'\r\r\r \r\r\r 9000\r 13860\r 0\r 0\r False\r False\r\r\r \r \r \r\r'; +for(z=0;z\r'+sa[z],k=a.mso.rtl?k+'\r\r\r':k+'\r',k+="\r";k+="\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();hu;++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;kx||36x||48===x)A="n";else if("date"===C.type||13x||44x||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";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+=""}});0"+t+"");n++});H+="";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+=""));t+=">"+e+""}});0"+t+"");n++});a.displayTableName&&(H+=""+D(d("

"+a.tableName+"

"))+"");H+=""});m=''+('')+"";"excel"===k&&(m+="\x3c!--[if gte mso 9]>",m+="",m+="",m+="",m+="",m+="",m+=ba,m+="",m+="",m+="",a.mso.rtl&&(m+=""),m+="",m+="",m+="",m+="",m+= +"",m+="";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+="";m+="";m+="";m+='
';m+=H;m+="
";m+="";m+="";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;fIa){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=0a.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' + eid + ''; row.Name = '' + row.Name + '' + '
' + archived + emailed + '
'; - if ( canEdit.Monitors ) row.Monitor = '' + row.Monitor + ''; + if ( canEdit.Monitors ) row.Monitor = '' + row.Monitor + ''; if ( canEdit.Events ) row.Cause = '' + row.Cause + ''; if ( row.Notes.indexOf('detected:') >= 0 ) { row.Cause = row.Cause + '
' + row.Notes + '
'; @@ -118,7 +121,11 @@ function manageDelConfirmModalBtns() { $j('#eventTable').bootstrapTable('refresh'); $j('#deleteConfirm').modal('hide'); }) - .fail(logAjaxFail); + .fail( function(jqxhr) { + logAjaxFail(jqxhr); + $j('#eventTable').bootstrapTable('refresh'); + $j('#deleteConfirm').modal('hide'); + }); }); // Manage the CANCEL modal button diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 70e22fda7..62801c6de 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -871,6 +871,8 @@ include('_monitor_source_nvsocket.php'); '2048x1536'=>'2048x1536 3MP', '2560x1440'=>'2560x1440 1440p QHD WQHD', '2592x1944'=>'2592x1944 5MP', + '2688x1520'=>'2688x1520 4MP', + '3072x2048'=>'3072x2048 6MP', '3840x2160'=>'3840x2160 4K UHD', ); $selected = ''; @@ -993,7 +995,7 @@ $videowriter_encoders = array( 'mpeg1' => 'mpeg1', 'mpeg2' => 'mpeg2', ); - echo htmlSelect( 'newMonitor[Encoder]', $videowriter_encoders, $monitor->Encoder() );?> + echo htmlSelect('newMonitor[Encoder]', $videowriter_encoders, $monitor->Encoder());?> @@ -1017,7 +1019,7 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor Type() == 'Ffmpeg' ) { ?> RecordAudio() ) { ?> checked="checked"/> - Audio recording only available with FFMPEG using H264 Passthrough + Audio recording only available with FFMPEG