diff --git a/src/zm_db.cpp b/src/zm_db.cpp index cc0797c12..7ddf60c48 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -108,8 +108,8 @@ bool zmDbConnect() { } void zmDbClose() { + std::lock_guard lck(db_mutex); if (zmDbConnected) { - 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() @@ -238,8 +238,13 @@ zmDbQueue::~zmDbQueue() { } void zmDbQueue::stop() { - mTerminate = true; + { + std::unique_lock lock(mMutex); + + mTerminate = true; + } mCondition.notify_all(); + if (mThread.joinable()) mThread.join(); } diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 3ef7ea887..b9a3ccbf2 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -699,3 +699,6 @@ Debug(1, "wakeing"); } } } +int Event::MonitorId() { + return monitor->Id(); +} diff --git a/src/zm_event.h b/src/zm_event.h index 2029c062a..1cbf794ef 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -192,5 +192,6 @@ class Event { void SavePreAlarmFrames() { EmptyPreAlarmFrames(); } + int MonitorId(); }; #endif // ZM_EVENT_H diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 35efa8774..a3662c654 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -98,7 +98,7 @@ void Image::update_function_pointers() { delta8_abgr = &std_delta8_abgr; delta8_gray8 = &std_delta8_gray8; blend = &std_blend; - Warning("Using slow std functions because pixels %d mod 4=%d", pixels, pixels%4); + Debug(1, "Using slow std functions because pixels %d mod 4=%d", pixels, pixels%4); } else { // Use either sse or neon, or loop unrolled version delta8_rgb = fptr_delta8_rgb; @@ -1090,14 +1090,16 @@ bool Image::WriteJpeg(const std::string &filename, const int &quality_override, SystemTimePoint timestamp, bool on_blocking_abort) const { - // jpeg libs are not thread safe - std::unique_lock lck(jpeg_mutex); if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) { Image temp_image(*this); temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort); } + + // jpeg libs are not thread safe + std::unique_lock lck(jpeg_mutex); + int quality = quality_override ? quality_override : config.jpeg_file_quality; jpeg_compress_struct *cinfo = writejpg_ccinfo[quality]; @@ -1167,7 +1169,7 @@ bool Image::WriteJpeg(const std::string &filename, } else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) { cinfo->in_color_space = JCS_EXT_XBGR; } else { - Warning("Unknwon subpixelorder %d", subpixelorder); + Warning("Unknown subpixelorder %d", subpixelorder); /* Assume RGBA */ cinfo->in_color_space = JCS_EXT_RGBX; } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 026fad766..853504a31 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1822,8 +1822,8 @@ bool Monitor::Analyse() { Debug(1, "Linked monitor %d %s is connected", linked_monitors[i]->Id(), linked_monitors[i]->Name()); if (linked_monitors[i]->hasAlarmed()) { - Debug(1, "Linked monitor %d %s is alarmed", - linked_monitors[i]->Id(), linked_monitors[i]->Name()); + Debug(1, "Linked monitor %d %s is alarmed score will be %d", + linked_monitors[i]->Id(), linked_monitors[i]->Name(), linked_monitors[i]->lastFrameScore()); if (!event) { if (first_link) { if (cause.length()) @@ -1948,6 +1948,9 @@ bool Monitor::Analyse() { } // end if active and doing motion detection + // Set this before any state changes so that it's value is picked up immediately by linked monitors + shared_data->last_frame_score = score; + // If motion detecting, score will be > 0 on motion, but if skipping frames, might not be. So also test snap->score if ((score > 0) or ((snap->score > 0) and (function != MONITOR))) { if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) { @@ -2160,7 +2163,6 @@ bool Monitor::Analyse() { last_signal = signal; } // end if videostream } // end if signal - shared_data->last_frame_score = score; } else { Debug(3, "trigger == off"); if (event) { @@ -2766,7 +2768,11 @@ Event * Monitor::openEvent( if (!event_start_command.empty()) { if (fork() == 0) { - execlp(event_start_command.c_str(), event_start_command.c_str(), std::to_string(event->Id()).c_str(), nullptr); + execlp(event_start_command.c_str(), + event_start_command.c_str(), + std::to_string(event->Id()).c_str(), + std::to_string(event->MonitorId()).c_str(), + nullptr); Error("Error execing %s", event_start_command.c_str()); } } @@ -2806,11 +2812,15 @@ void Monitor::closeEvent() { Debug(1, "Starting thread to close event"); close_event_thread = std::thread([](Event *e, const std::string &command){ int64_t event_id = e->Id(); + int monitor_id = e->MonitorId(); delete e; if (!command.empty()) { if (fork() == 0) { - execlp(command.c_str(), command.c_str(), std::to_string(event_id).c_str(), nullptr); + execlp(command.c_str(), command.c_str(), + std::to_string(event_id).c_str(), + std::to_string(monitor_id).c_str(), // monitor id + nullptr); Error("Error execing %s", command.c_str()); } } diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 86b9b97e0..110f1665d 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -26,7 +26,7 @@ function MonitorStream(monitorData) { this.buttons = {}; // index by name this.setButton = function(name, element) { - this.buttons.name = element; + this.buttons[name] = element; }; this.element = null; @@ -380,6 +380,8 @@ function MonitorStream(monitorData) { if ('enableAlarmButton' in this.buttons) { this.buttons.enableAlarmButton.addClass('disabled'); this.buttons.enableAlarmButton.prop('title', disableAlarmsStr); + } else { + console.log('enableAlarmButton not found in buttons'); } if ('forceAlarmButton' in this.buttons) { if (streamStatus.forced) { @@ -390,8 +392,11 @@ function MonitorStream(monitorData) { this.buttons.forceAlarmButton.prop('title', forceAlarmStr); } this.buttons.forceAlarmButton.prop('disabled', false); + } else { + console.log('forceAlarmButton not found in buttons'); } } else { + console.log("streamStatus not enabled"); if ('enableAlarmButton' in this.buttons) { this.buttons.enableAlarmButton.removeClass('disabled'); this.buttons.enableAlarmButton.prop('title', enableAlarmsStr); @@ -462,6 +467,8 @@ function MonitorStream(monitorData) { this.alarmCommand = function(command) { if (this.ajaxQueue) { + console.log("Aborting in progress ajax for alarm"); + // Doing this for responsiveness, but we could be aborting something important. Need smarter logic this.ajaxQueue.abort(); } const alarmCmdParms = Object.assign({}, this.streamCmdParms); @@ -485,9 +492,6 @@ function MonitorStream(monitorData) { } this.streamCmdReq = function(streamCmdParms) { - if (this.ajaxQueue) { - this.ajaxQueue.abort(); - } this.ajaxQueue = jQuery.ajaxQueue({url: this.url, data: streamCmdParms, dataType: "json"}) .done(this.getStreamCmdResponse.bind(this)) .fail(this.onFailure.bind(this)); diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 21026c3c8..4af160389 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -833,6 +833,14 @@ function initPage() { monitorStream.setButton('enableAlarmButton', enableAlmBtn); monitorStream.setButton('forceAlarmButton', forceAlmBtn); monitorStream.setButton('zoomOutButton', $j('zoomOutBtn')); + if (canEdit.Monitors) { + // Will be enabled by streamStatus ajax + enableAlmBtn.on('click', cmdAlarm); + forceAlmBtn.on('click', cmdForce); + } else { + forceAlmBtn.prop('title', forceAlmBtn.prop('title') + ': disabled because cannot edit Monitors'); + enableAlmBtn.prop('title', enableAlmBtn.prop('title') + ': disabled because cannot edit Monitors'); + } /* if (streamMode == 'single') { diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index fdcdae06d..8ceb06ab2 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -159,8 +159,8 @@ xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); - - + +