From 146ea4822d6986950c5b19741dd44c05ca0c4f1e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 21 Jan 2022 09:44:44 -0500 Subject: [PATCH 01/31] Bump the db queue limit to 30 before we warn. I only have 1 server that gets over 20 and it is still ok. --- src/zm_db.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_db.cpp b/src/zm_db.cpp index f0b13d538..cc0797c12 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -251,7 +251,7 @@ void zmDbQueue::process() { mCondition.wait(lock); } while (!mQueue.empty()) { - if (mQueue.size() > 20) { + if (mQueue.size() > 30) { Logger *log = Logger::fetch(); Logger::Level db_level = log->databaseLevel(); log->databaseLevel(Logger::NOLOG); From 3cc243b9a8992a3a70a5e2bb67b61e9c3831a09c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 24 Jan 2022 09:23:19 -0500 Subject: [PATCH 02/31] Don't freshing config when doing update. That is it's own command --- scripts/zmupdate.pl.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index d096c70b5..f62b14316 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -444,11 +444,6 @@ if ( $version ) { print( "\nUpgrading database to version ".ZM_VERSION."\n" ); -# Update config first of all - migratePaths(); - ZoneMinder::Config::loadConfigFromDB(); - ZoneMinder::Config::saveConfigToDB(); - my $cascade = undef; if ( $cascade || $version eq "1.19.0" ) { # Patch the database From 26ae5052f4bd19e34857223887439b171e65a8ca Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Jan 2022 11:55:21 -0500 Subject: [PATCH 03/31] Fix fail to get Sources in RTSP. the string msg although initially reserved to ZM_NETWORK_BUFSIZ, after use it's capacity is changed whatever it's contents are. So need to re-reserve. --- src/zm_comms.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_comms.h b/src/zm_comms.h index 7e7329d5d..ac772ae26 100644 --- a/src/zm_comms.h +++ b/src/zm_comms.h @@ -245,6 +245,7 @@ class Socket : public CommsBase { } virtual ssize_t recv(std::string &msg) const { + msg.reserve(ZM_NETWORK_BUFSIZ); std::vector buffer(msg.capacity()); ssize_t nBytes; if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) { From cf82d767de9976d39f8f98a6cc5bfe29a4ce9416 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Jan 2022 14:24:52 -0500 Subject: [PATCH 04/31] Remove the decoding code, just populate the av_packet. This fixes rtsp decoding because we weren't copying the decoded frame to shm raw image. --- dep/RtspServer | 2 +- src/zm_remote_camera_rtsp.cpp | 62 ++++++++++++++--------------------- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/dep/RtspServer b/dep/RtspServer index cd7fd49be..1b40f1661 160000 --- a/dep/RtspServer +++ b/dep/RtspServer @@ -1 +1 @@ -Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878 +Subproject commit 1b40f1661f93f50fd5805f239d1e466a3bcf888f diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 8cdecdf94..b49862845 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -168,8 +168,10 @@ int RemoteCameraRtsp::PrimeCapture() { } } // end foreach stream - if ( mVideoStreamId == -1 ) - Fatal("Unable to locate video stream"); + if ( mVideoStreamId == -1 ) { + Error("Unable to locate video stream"); + return -1; + } if ( mAudioStreamId == -1 ) Debug(3, "Unable to locate audio stream"); @@ -179,17 +181,22 @@ int RemoteCameraRtsp::PrimeCapture() { // Find the decoder for the video stream AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id); - if ( codec == nullptr ) - Panic("Unable to locate codec %d decoder", mVideoCodecContext->codec_id); + if ( codec == nullptr ) { + Error("Unable to locate codec %d decoder", mVideoCodecContext->codec_id); + return -1; + } // Open codec - if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) - Panic("Can't open codec"); + if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) { + Error("Can't open codec"); + return -1; + } int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1); if ( (unsigned int)pSize != imagesize ) { - Fatal("Image size mismatch. Required: %d Available: %llu", pSize, imagesize); + Error("Image size mismatch. Required: %d Available: %llu", pSize, imagesize); + return -1; } return 1; @@ -208,18 +215,13 @@ int RemoteCameraRtsp::PreCapture() { int RemoteCameraRtsp::Capture(std::shared_ptr &zm_packet) { int frameComplete = false; AVPacket *packet = &zm_packet->packet; - if ( !zm_packet->image ) { - Debug(1, "Allocating image %dx%d %d colours %d", width, height, colours, subpixelorder); - zm_packet->image = new Image(width, height, colours, subpixelorder); - } - while (!frameComplete) { buffer.clear(); if (!rtspThread || rtspThread->IsStopped()) return -1; - if ( rtspThread->getFrame(buffer) ) { + if (rtspThread->getFrame(buffer)) { Debug(3, "Read frame %d bytes", buffer.size()); Hexdump(4, buffer.head(), 16); @@ -254,36 +256,20 @@ int RemoteCameraRtsp::Capture(std::shared_ptr &zm_packet) { //while ( (!frameComplete) && (buffer.size() > 0) ) { if ( buffer.size() > 0 ) { - packet->data = buffer.head(); + packet->data = (uint8_t*)av_malloc(buffer.size()); + memcpy(packet->data, buffer.head(), buffer.size()); + //packet->data = buffer.head(); packet->size = buffer.size(); bytes += packet->size; + buffer -= packet->size; struct timeval now; - gettimeofday(&now, NULL); + gettimeofday(&now, nullptr); packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec; - - int bytes_consumed = zm_packet->decode(mVideoCodecContext); - if ( bytes_consumed < 0 ) { - Error("Error while decoding frame %d", frameCount); - //Hexdump(Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size()); - } - buffer -= packet->size; - if ( bytes_consumed ) { - zm_dump_video_frame(zm_packet->in_frame, "remote_rtsp_decode"); - if (!mVideoStream->codecpar->width) { - zm_dump_codec(mVideoCodecContext); - zm_dump_codecpar(mVideoStream->codecpar); - mVideoStream->codecpar->width = zm_packet->in_frame->width; - mVideoStream->codecpar->height = zm_packet->in_frame->height; - zm_dump_codecpar(mVideoStream->codecpar); - } - zm_packet->codec_type = mVideoCodecContext->codec_type; - zm_packet->stream = mVideoStream; - frameComplete = true; - Debug(2, "Frame: %d - %d/%d", frameCount, bytes_consumed, buffer.size()); - packet->data = nullptr; - packet->size = 0; - } + zm_packet->codec_type = mVideoCodecContext->codec_type; + zm_packet->stream = mVideoStream; + frameComplete = true; + Debug(2, "Frame: %d - %d/%d", frameCount, packet->size, buffer.size()); } } /* getFrame() */ } // end while true From c8c09e560f071d2a5bb7ba4a04447c1a765e7879 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Jan 2022 14:25:13 -0500 Subject: [PATCH 05/31] Fix mTerminate not being initialised. --- src/zm_rtp_source.cpp | 4 +++- src/zm_rtp_source.h | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/zm_rtp_source.cpp b/src/zm_rtp_source.cpp index 1862c1886..56ca2cf0d 100644 --- a/src/zm_rtp_source.cpp +++ b/src/zm_rtp_source.cpp @@ -45,8 +45,10 @@ RtpSource::RtpSource( mFrame(65536), mFrameCount(0), mFrameGood(true), + prevM(false), mFrameReady(false), - mFrameProcessed(false) + mFrameProcessed(false), + mTerminate(false) { char hostname[256] = ""; gethostname(hostname, sizeof(hostname)); diff --git a/src/zm_rtp_source.h b/src/zm_rtp_source.h index a39e8225f..71be9af2c 100644 --- a/src/zm_rtp_source.h +++ b/src/zm_rtp_source.h @@ -91,8 +91,6 @@ private: bool mFrameGood; bool prevM; - bool mTerminate; - bool mFrameReady; std::condition_variable mFrameReadyCv; std::mutex mFrameReadyMutex; @@ -100,6 +98,7 @@ private: bool mFrameProcessed; std::condition_variable mFrameProcessedCv; std::mutex mFrameProcessedMutex; + bool mTerminate; private: void init(uint16_t seq); From 38da3b4d52dc21c2443bf4a295cd0c636f5683e7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Jan 2022 14:25:27 -0500 Subject: [PATCH 06/31] add some brackets to make logic more clear --- src/zm_rtp_ctrl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_rtp_ctrl.cpp b/src/zm_rtp_ctrl.cpp index 25d34f0ff..a82ff2b2a 100644 --- a/src/zm_rtp_ctrl.cpp +++ b/src/zm_rtp_ctrl.cpp @@ -277,7 +277,7 @@ void RtpCtrlThread::Run() { TimePoint last_receive = std::chrono::steady_clock::now(); bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true. - while (!mTerminate && select.wait() >= 0) { + while (!mTerminate && (select.wait() >= 0)) { TimePoint now = std::chrono::steady_clock::now(); zm::Select::CommsList readable = select.getReadable(); if ( readable.size() == 0 ) { From 4d90a816f86730ec1f6336f9e27eef0abffba2c6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Jan 2022 16:22:33 -0500 Subject: [PATCH 07/31] Put a lock around jpeg writing. libjpeg is not thread safe --- src/zm_image.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index a4aad8b61..7260791a5 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -24,6 +24,7 @@ #include "zm_utils.h" #include #include +#include #include #include @@ -80,6 +81,8 @@ imgbufcpy_fptr_t fptr_imgbufcpy; /* Font */ static ZmFont font; +std::mutex jpeg_mutex; + void Image::update_function_pointers() { /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ if ( pixels % 16 || pixels % 12 ) { @@ -1083,6 +1086,9 @@ 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); @@ -1366,6 +1372,8 @@ bool Image::EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_overr return temp_image.EncodeJpeg(outbuffer, outbuffer_size, quality_override); } + std::unique_lock lck(jpeg_mutex); + int quality = quality_override ? quality_override : config.jpeg_stream_quality; struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality]; From 961256d2e78bb6c84912e621ca0fdb0b9b6cd54d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 25 Jan 2022 16:24:21 -0500 Subject: [PATCH 08/31] terminate when zm_terminate is set. Do a countdown instead of countup. Sleeo for 10000 microseconds instead of 100. This restores the old value --- src/zm_remote_camera_rtsp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index b49862845..ffb4a061d 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -22,6 +22,7 @@ #include "zm_config.h" #include "zm_monitor.h" #include "zm_packet.h" +#include "zm_signal.h" RemoteCameraRtsp::RemoteCameraRtsp( const Monitor *monitor, @@ -126,8 +127,8 @@ int RemoteCameraRtsp::Disconnect() { int RemoteCameraRtsp::PrimeCapture() { Debug(2, "Waiting for sources"); - for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) { - std::this_thread::sleep_for(Microseconds(100)); + for (int i = 100; i && !zm_terminate && !rtspThread->hasSources(); i--) { + std::this_thread::sleep_for(Microseconds(10000)); } if (!rtspThread->hasSources()) { @@ -218,7 +219,7 @@ int RemoteCameraRtsp::Capture(std::shared_ptr &zm_packet) { while (!frameComplete) { buffer.clear(); - if (!rtspThread || rtspThread->IsStopped()) + if (!rtspThread || rtspThread->IsStopped() || zm_terminate) return -1; if (rtspThread->getFrame(buffer)) { From 138bada0951453e989029bcc1751fee5b4902943 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 26 Jan 2022 09:27:29 -0500 Subject: [PATCH 09/31] Add libdatetime-perl. Some Control modules need it --- distros/ubuntu2004/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/ubuntu2004/control b/distros/ubuntu2004/control index 37c0e5e51..dda0e7852 100644 --- a/distros/ubuntu2004/control +++ b/distros/ubuntu2004/control @@ -42,7 +42,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libswscale5|libswscale4 ,libswresample3|libswresample2 ,ffmpeg - ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl + ,libdatetime-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdbd-mysql-perl ,libphp-serialization-perl ,libmodule-load-conditional-perl From 42e24614d67fd12f47d10eeaf17b609c08faa37d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 26 Jan 2022 11:01:09 -0500 Subject: [PATCH 10/31] Include filename in debugs when writing out jpegs --- src/zm_event.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e14c1a343..e0ffdff46 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -423,7 +423,7 @@ void Event::AddFrame(Image *image, // If this is the first frame, we should add a thumbnail to the event directory if ((frames == 1) || (score > max_score)) { write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it. - Debug(1, "Writing snapshot"); + Debug(1, "Writing snapshot to %s", snapshot_file.c_str()); WriteFrameImage(image, timestamp, snapshot_file.c_str()); } else { Debug(1, "Not Writing snapshot because frames %d score %d > max %d", frames, score, max_score); @@ -435,17 +435,19 @@ void Event::AddFrame(Image *image, if (!alarm_frame_written) { write_to_db = true; // OD processing will need it, so the db needs to know about it alarm_frame_written = true; - Debug(1, "Writing alarm image"); - WriteFrameImage(image, timestamp, alarm_file.c_str()); + Debug(1, "Writing alarm image to %s", alarm_file.c_str()); + if (!WriteFrameImage(image, timestamp, alarm_file.c_str())) { + Error("Failed to write alarm frame image to %s", alarm_file.c_str()); + } } else { Debug(3, "Not Writing alarm image because alarm frame already written"); } if (alarm_image and (save_jpegs & 2)) { std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames); - Debug(1, "Writing analysis frame %d", frames); + Debug(1, "Writing analysis frame %d to %s", frames, event_file.c_str()); if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) { - Error("Failed to write analysis frame image"); + Error("Failed to write analysis frame image to %s", event_file.c_str()); } } } // end if is an alarm frame From fe8747e5e79b288f9d594ed75d69ca9cdf4922e2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 26 Jan 2022 11:45:04 -0500 Subject: [PATCH 11/31] Make ONVIF inputs 100% --- web/skins/classic/css/base/views/monitor.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/css/base/views/monitor.css b/web/skins/classic/css/base/views/monitor.css index f827af5ca..28afbf480 100644 --- a/web/skins/classic/css/base/views/monitor.css +++ b/web/skins/classic/css/base/views/monitor.css @@ -15,7 +15,11 @@ input[name="newMonitor[Path]"], input[name="newMonitor[SecondPath]"], input[name="newMonitor[LabelFormat]"], input[name="newMonitor[ControlDevice]"], -input[name="newMonitor[ControlAddress]"] { +input[name="newMonitor[ControlAddress]"], +input[name="newMonitor[ONVIF_URL]"], +input[name="newMonitor[ONVIF_Username]"], +input[name="newMonitor[ONVIF_Password]"], +input[name="newMonitor[ONVIF_Options]"] { width: 100%; } input[name="newMonitor[LabelFormat]"]{ From 8e62562afdeb87be0ce2a241b5aa02cadc67bd3c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 26 Jan 2022 21:39:29 -0500 Subject: [PATCH 12/31] Move onclick to the surrounding div instead of the img. Fixes clicking on montage --- web/js/MonitorStream.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index b5f9d0b6a..86b9b97e0 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -38,6 +38,14 @@ function MonitorStream(monitorData) { } return this.element; }; + this.getFrame = function() { + if (this.frame) return this.frame; + this.frame = document.getElementById('imageFeed'+this.id); + if (!this.frame) { + console.error("No frame div for #imageFeed"+this.id); + } + return this.frame; + }; /* if the img element didn't have a src, this would fill it in, causing it to show. */ this.show = function() { @@ -161,7 +169,7 @@ function MonitorStream(monitorData) { this.setup_onclick = function(func) { this.onclick = func; - const el = this.getElement(); + const el = this.getFrame(); if (!el) return; el.addEventListener('click', this.onclick, false); }; From d567d8aafcfd1ead184f7685234768ea88aadc0d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jan 2022 17:06:18 -0500 Subject: [PATCH 13/31] Merge upstream patches to RTSPServer --- dep/RtspServer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dep/RtspServer b/dep/RtspServer index 1b40f1661..333717a3a 160000 --- a/dep/RtspServer +++ b/dep/RtspServer @@ -1 +1 @@ -Subproject commit 1b40f1661f93f50fd5805f239d1e466a3bcf888f +Subproject commit 333717a3ac59b8f98389905c1281909e5543bff6 From a91e7d96f8a1bd37d657fd7148cb068597c5fc67 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 28 Jan 2022 12:42:41 -0500 Subject: [PATCH 14/31] Merge fix for VP8 --- dep/RtspServer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dep/RtspServer b/dep/RtspServer index 333717a3a..eab328514 160000 --- a/dep/RtspServer +++ b/dep/RtspServer @@ -1 +1 @@ -Subproject commit 333717a3ac59b8f98389905c1281909e5543bff6 +Subproject commit eab32851421ffe54fec0229c3efc44c642bc8d46 From e5792c21c923a19a315cefb2127fe27c3bb66516 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 28 Jan 2022 15:38:18 -0500 Subject: [PATCH 15/31] Push locked packets into event packetqueue instead of unlcoked contents. Prevents freeing of data before writing to event, hence crashing. --- src/zm_event.cpp | 8 ++++--- src/zm_event.h | 4 ++-- src/zm_monitor.cpp | 60 +++++++++++++++++++++++----------------------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index e0ffdff46..3ef7ea887 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -301,9 +301,9 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) { } // end if update } // void Event::updateNotes(const StringSetMap &newNoteSetMap) -void Event::AddPacket(const std::shared_ptr&packet) { +void Event::AddPacket(ZMLockedPacket *packetlock) { std::unique_lock lck(packet_queue_mutex); - packet_queue.push(packet); + packet_queue.push(packetlock); packet_queue_condition.notify_one(); } @@ -684,7 +684,9 @@ void Event::Run() { while (true) { if (!packet_queue.empty()) { Debug(1, "adding packet"); - this->AddPacket_(packet_queue.front()); + const ZMLockedPacket * packet_lock = packet_queue.front(); + this->AddPacket_(packet_lock->packet_); + delete packet_lock; packet_queue.pop(); } else { if (terminate_ or zm_terminate) { diff --git a/src/zm_event.h b/src/zm_event.h index 31a23fad4..2029c062a 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -105,7 +105,7 @@ class Event { void createNotes(std::string ¬es); - std::queue> packet_queue; + std::queue packet_queue; std::mutex packet_queue_mutex; std::condition_variable packet_queue_condition; @@ -134,7 +134,7 @@ class Event { SystemTimePoint EndTime() const { return end_time; } TimePoint::duration Duration() const { return end_time - start_time; }; - void AddPacket(const std::shared_ptr &p); + void AddPacket(ZMLockedPacket *); void AddPacket_(const std::shared_ptr &p); bool WritePacket(const std::shared_ptr &p); bool SendFrameImage(const Image *image, bool alarm_frame=false); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 4ae155e23..b55d2f91d 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2270,34 +2270,34 @@ bool Monitor::Analyse() { shared_data->state = state = IDLE; } // end if ( trigger_data->trigger_state != TRIGGER_OFF ) - if (event) event->AddPacket(snap); + if (event) event->AddPacket(packet_lock); + + // In the case where people have pre-alarm frames, the web ui will generate the frame images + // from the mp4. So no one will notice anyways. + if (snap->image and (videowriter == PASSTHROUGH)) { + if (!savejpegs) { + Debug(1, "Deleting image data for %d", snap->image_index); + // Don't need raw images anymore + delete snap->image; + snap->image = nullptr; + } + if (snap->analysis_image and !(savejpegs & 2)) { + Debug(1, "Deleting analysis image data for %d", snap->image_index); + delete snap->analysis_image; + snap->analysis_image = nullptr; + } + } + + packetqueue.clearPackets(snap); + + if (snap->codec_type == AVMEDIA_TYPE_VIDEO) { + // Only do these if it's a video packet. + shared_data->last_read_index = snap->image_index; + analysis_image_count++; + } + packetqueue.increment_it(analysis_it); + if (!event) delete packet_lock; } // end scope for event_lock - - // In the case where people have pre-alarm frames, the web ui will generate the frame images - // from the mp4. So no one will notice anyways. - if (snap->image and (videowriter == PASSTHROUGH)) { - if (!savejpegs) { - Debug(1, "Deleting image data for %d", snap->image_index); - // Don't need raw images anymore - delete snap->image; - snap->image = nullptr; - } - if (snap->analysis_image and !(savejpegs & 2)) { - Debug(1, "Deleting analysis image data for %d", snap->image_index); - delete snap->analysis_image; - snap->analysis_image = nullptr; - } - } - - packetqueue.clearPackets(snap); - - if (snap->codec_type == AVMEDIA_TYPE_VIDEO) { - // Only do these if it's a video packet. - shared_data->last_read_index = snap->image_index; - analysis_image_count++; - } - packetqueue.increment_it(analysis_it); - delete packet_lock; //packetqueue.unlock(packet_lock); shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); @@ -2870,16 +2870,16 @@ Event * Monitor::openEvent( // Write out starting packets, do not modify packetqueue it will garbage collect itself while (starting_packet and ((*start_it) != *analysis_it)) { - event->AddPacket(starting_packet); + event->AddPacket(starting_packet_lock); // Have added the packet, don't want to unlock it until we have locked the next packetqueue.increment_it(start_it); if ((*start_it) == *analysis_it) { - if (starting_packet_lock) delete starting_packet_lock; + //if (starting_packet_lock) delete starting_packet_lock; break; } ZMLockedPacket *lp = packetqueue.get_packet(start_it); - delete starting_packet_lock; + //delete starting_packet_lock; if (!lp) return nullptr; // only on terminate FIXME starting_packet_lock = lp; starting_packet = lp->packet_; From 61028a3ae1f09003403eec6bf462d2791490e838 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jan 2022 10:09:31 -0500 Subject: [PATCH 16/31] Remove travis badge, use Isaac's full name. Fixes #3417 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index da96b3e60..bcb53ba8a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ ZoneMinder ========== -[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) [![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE) @@ -25,7 +24,7 @@ https://github.com/ZoneMinder/zmdockerfiles This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros: -- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor) +- Ubuntu via [Isaac Connor's PPA](https://launchpad.net/~iconnor) - Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder) - RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org) - Fedora via [RPM Fusion](http://rpmfusion.org) From 46ad8e74e600b8efd74cccf83d3157868fbf5935 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jan 2022 11:01:51 -0500 Subject: [PATCH 17/31] Add publish --- .github/workflows/create-packages.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/create-packages.yml b/.github/workflows/create-packages.yml index 6a76da322..29e5d471a 100644 --- a/.github/workflows/create-packages.yml +++ b/.github/workflows/create-packages.yml @@ -29,3 +29,13 @@ jobs: DIST: ${{ matrix.os_dist.dist }} DOCKER_REPO: iconzm/packpack run: utils/packpack/startpackpack.sh + + - name: Publish + uses: nogsantos/scp-deploy@master + with: + src: ./build/*.{deb,dsc,tar.xz,buildinfo,changes} + host: ${{ secrets.ZMREPO_HOST }} + remote: debian/master/mini-dinstall/incoming/ + port: ${{ secrets.ZMREPO_SSH_PORT }} + user: ${{ secrets.ZMREPO_SSH_USER }} + key: ${{ secrets.ZMREPO_SSH_KEY }} From 3a2484b874af8b870dd70d487529c79768d1b513 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jan 2022 11:19:36 -0500 Subject: [PATCH 18/31] Try a different ssh implementation --- .github/workflows/create-packages.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/create-packages.yml b/.github/workflows/create-packages.yml index 29e5d471a..2fdc3577c 100644 --- a/.github/workflows/create-packages.yml +++ b/.github/workflows/create-packages.yml @@ -31,11 +31,11 @@ jobs: run: utils/packpack/startpackpack.sh - name: Publish - uses: nogsantos/scp-deploy@master - with: - src: ./build/*.{deb,dsc,tar.xz,buildinfo,changes} - host: ${{ secrets.ZMREPO_HOST }} - remote: debian/master/mini-dinstall/incoming/ - port: ${{ secrets.ZMREPO_SSH_PORT }} - user: ${{ secrets.ZMREPO_SSH_USER }} - key: ${{ secrets.ZMREPO_SSH_KEY }} + uses: easingthemes/ssh-deploy@main + env: + SSH_PRIVATE_KEY: ${{ secrets.ZMREPO_SSH_KEY }} + ARGS: "-rltgoDzvO" + SOURCE: ./build/*.{deb,dsc,tar.xz,buildinfo,changes} + REMOTE_HOST: ${{ secrets.ZMREPO_HOST }} + REMOTE_USER: ${{ secrets.ZMREPO_SSH_USER }} + TARGET: debian/master/mini-dinstall/incoming/ From b00ca5ce91b4f6343737a2983fbd38f4919e0838 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jan 2022 11:47:42 -0500 Subject: [PATCH 19/31] upload entire build dir as shell expansions don't seem to work --- .github/workflows/create-packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-packages.yml b/.github/workflows/create-packages.yml index 2fdc3577c..87d46246d 100644 --- a/.github/workflows/create-packages.yml +++ b/.github/workflows/create-packages.yml @@ -35,7 +35,7 @@ jobs: env: SSH_PRIVATE_KEY: ${{ secrets.ZMREPO_SSH_KEY }} ARGS: "-rltgoDzvO" - SOURCE: ./build/*.{deb,dsc,tar.xz,buildinfo,changes} + SOURCE: build/ REMOTE_HOST: ${{ secrets.ZMREPO_HOST }} REMOTE_USER: ${{ secrets.ZMREPO_SSH_USER }} TARGET: debian/master/mini-dinstall/incoming/ From 6b8cc14723024bbdcc8c3eca8785b8c39c58bd9a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jan 2022 12:07:09 -0500 Subject: [PATCH 20/31] Don't free image data if we added the packet to an event. The lock moves to the event so it's not safe --- src/zm_monitor.cpp | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index b55d2f91d..2c56ec6fe 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2270,24 +2270,6 @@ bool Monitor::Analyse() { shared_data->state = state = IDLE; } // end if ( trigger_data->trigger_state != TRIGGER_OFF ) - if (event) event->AddPacket(packet_lock); - - // In the case where people have pre-alarm frames, the web ui will generate the frame images - // from the mp4. So no one will notice anyways. - if (snap->image and (videowriter == PASSTHROUGH)) { - if (!savejpegs) { - Debug(1, "Deleting image data for %d", snap->image_index); - // Don't need raw images anymore - delete snap->image; - snap->image = nullptr; - } - if (snap->analysis_image and !(savejpegs & 2)) { - Debug(1, "Deleting analysis image data for %d", snap->image_index); - delete snap->analysis_image; - snap->analysis_image = nullptr; - } - } - packetqueue.clearPackets(snap); if (snap->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -2295,9 +2277,30 @@ bool Monitor::Analyse() { shared_data->last_read_index = snap->image_index; analysis_image_count++; } - packetqueue.increment_it(analysis_it); - if (!event) delete packet_lock; + + if (event) { + event->AddPacket(packet_lock); + } else { + // In the case where people have pre-alarm frames, the web ui will generate the frame images + // from the mp4. So no one will notice anyways. + if (snap->image and (videowriter == PASSTHROUGH)) { + if (!savejpegs) { + Debug(1, "Deleting image data for %d", snap->image_index); + // Don't need raw images anymore + delete snap->image; + snap->image = nullptr; + } + if (snap->analysis_image and !(savejpegs & 2)) { + Debug(1, "Deleting analysis image data for %d", snap->image_index); + delete snap->analysis_image; + snap->analysis_image = nullptr; + } + } + delete packet_lock; + } } // end scope for event_lock + + packetqueue.increment_it(analysis_it); //packetqueue.unlock(packet_lock); shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); From 0eb4d95d0e048c71adacca9eb38a3a123fa0f4a9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 31 Jan 2022 18:32:58 -0500 Subject: [PATCH 21/31] remove geolocation copies from config to javscript land. Now all of config is brought in my skin.js.php --- web/skins/classic/views/js/monitor.js.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index c635af233..7b06905f7 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -1,10 +1,3 @@ -var ZM_OPT_USE_GEOLOCATION = '' == '1' ? true : false; - var optControl = ; var hasOnvif = ; var defaultAspectRatio = ''; From 34d7f192b4d10da46294185c97db73c5371c2c7c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 1 Feb 2022 07:23:12 -0500 Subject: [PATCH 22/31] Remove field that ffmpeg 5.0 doesn't have. --- src/zm_ffmpeg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 3986b38b8..4fe14051f 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -257,8 +257,8 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) Debug(1, "ids [0x%x]", st->id); if (lang) Debug(1, "language (%s)", lang->value); - Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d", - st->codec_info_nb_frames, codec->frame_size, + Debug(1, "frame_size:%d stream timebase: %d/%d", + codec->frame_size, st->time_base.num, st->time_base.den ); From 462ec45a0725bc12d15ffb64f880091935c602cb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 1 Feb 2022 09:46:37 -0500 Subject: [PATCH 23/31] Escape newlines in config values. --- web/skins/classic/js/skin.js.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index 2d443fbe1..52beb07ce 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -109,7 +109,9 @@ stateStrings[STATE_TAPE] = ""; $c) { - if (!$c['Private']) - echo 'const '. $name . ' = \''.$c['Value'].'\''.PHP_EOL; + if (!$c['Private']) { + + echo 'const '. $name . ' = \''.preg_replace('/(\n\r?)/', '\$1', $c['Value']).'\''.PHP_EOL; + } } ?> From bc4884afe5ef2da04e6778827fba16a003f9d678 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 1 Feb 2022 10:07:04 -0500 Subject: [PATCH 24/31] fix replacement on escape. For some reason needs double bacl slash --- web/skins/classic/js/skin.js.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index 52beb07ce..67cf74fd0 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -110,8 +110,7 @@ stateStrings[STATE_TAPE] = ""; global $config; foreach ($config as $name=>$c) { if (!$c['Private']) { - - echo 'const '. $name . ' = \''.preg_replace('/(\n\r?)/', '\$1', $c['Value']).'\''.PHP_EOL; + echo 'const '. $name . ' = \''.preg_replace('/(\n\r?)/', '\\\\$1', $c['Value']).'\';'.PHP_EOL; } } ?> From 80461cb1350b3cba4c2f4bb9190c389e44300d54 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 1 Feb 2022 13:36:11 -0500 Subject: [PATCH 25/31] add libcurl4 as a dependency as it was in build-depends. --- distros/ubuntu2004/control | 1 + 1 file changed, 1 insertion(+) diff --git a/distros/ubuntu2004/control b/distros/ubuntu2004/control index dda0e7852..54ca58af1 100644 --- a/distros/ubuntu2004/control +++ b/distros/ubuntu2004/control @@ -42,6 +42,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libswscale5|libswscale4 ,libswresample3|libswresample2 ,ffmpeg + ,libcurl4 ,libdatetime-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdbd-mysql-perl ,libphp-serialization-perl From 4949159b88fd45663cc483400daaaf69e9233805 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 1 Feb 2022 14:06:12 -0500 Subject: [PATCH 26/31] Rough in pcm alaw support. Untested. --- src/zm_rtsp_server.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/zm_rtsp_server.cpp b/src/zm_rtsp_server.cpp index ea6f4e2b7..3da273e00 100644 --- a/src/zm_rtsp_server.cpp +++ b/src/zm_rtsp_server.cpp @@ -298,6 +298,14 @@ int main(int argc, char *argv[]) { session->GetMediaSessionId(), xop::channel_1, audioFifoPath); audioSource->setFrequency(monitor->GetAudioFrequency()); audioSource->setChannels(monitor->GetAudioChannels()); + } else if (std::string::npos != audioFifoPath.find("pcm_alaw")) { + Debug(1, "Adding G711A source at %dHz %d channels", + monitor->GetAudioFrequency(), monitor->GetAudioChannels()); + session->AddSource(xop::channel_1, xop::G711ASource::CreateNew()); + audioSource = new ADTS_ZoneMinderFifoSource(rtspServer, + session->GetMediaSessionId(), xop::channel_1, audioFifoPath); + audioSource->setFrequency(monitor->GetAudioFrequency()); + audioSource->setChannels(monitor->GetAudioChannels()); } else { Warning("Unknown format in %s", audioFifoPath.c_str()); } From 18532096c59b0244e1984dae5ae48da2c20c63dc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 1 Feb 2022 16:10:46 -0500 Subject: [PATCH 27/31] Need full commits to get # of commits since last version change --- .github/workflows/create-packages.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/create-packages.yml b/.github/workflows/create-packages.yml index 87d46246d..93279cc57 100644 --- a/.github/workflows/create-packages.yml +++ b/.github/workflows/create-packages.yml @@ -21,6 +21,7 @@ jobs: steps: - uses: actions/checkout@v2 with: + fetch-depth: '0' submodules: recursive - name: Run packpack env: From ad0a095886fde17650533848249c0cc9faf2c1d9 Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Wed, 2 Feb 2022 11:35:36 +1100 Subject: [PATCH 28/31] Update MariaDB Bullseye info Specifically note that setting a root password is not required (or recommended; unix socket authentication is better than simple password auth). --- docs/installationguide/debian.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index b92ff267a..cf8bffdc5 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -35,7 +35,7 @@ Run the following commands. sudo apt install mariadb-server sudo apt install zoneminder -When mariadb is installed for the first time, it doesn't add a password to the root user. Therefore, for security, it is recommended to run ``mysql secure installation``. +By default MariaDB uses `unix socket authentication`_, so no root user password is required (root MariaDB user access only avaialble to local root Linux user). If you wish, you can set a root MariaDB password (and apply other security tweaks) by running `mariadb-secure-installation`_. **Step 3:** Setup permissions for zm.conf @@ -337,3 +337,6 @@ Reload Apache to enable your changes and then start ZoneMinder. sudo systemctl start zoneminder You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer. + +.. _unix socket authentication: https://mariadb.com/kb/en/authentication-plugin-unix-socket/ +.. _mariadb-secure-installation: https://mariadb.com/kb/en/mysql_secure_installation/ From 63b3042d3726a32aeea239a77c4308e8a402422d Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Wed, 2 Feb 2022 14:18:26 +1100 Subject: [PATCH 29/31] fix typo --- docs/installationguide/debian.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index cf8bffdc5..a5162d185 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -35,7 +35,7 @@ Run the following commands. sudo apt install mariadb-server sudo apt install zoneminder -By default MariaDB uses `unix socket authentication`_, so no root user password is required (root MariaDB user access only avaialble to local root Linux user). If you wish, you can set a root MariaDB password (and apply other security tweaks) by running `mariadb-secure-installation`_. +By default MariaDB uses `unix socket authentication`_, so no root user password is required (root MariaDB user access only available to local root Linux user). If you wish, you can set a root MariaDB password (and apply other security tweaks) by running `mariadb-secure-installation`_. **Step 3:** Setup permissions for zm.conf From 4874dedc1ff92592a83318569bb7c20f68115c11 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 2 Feb 2022 10:28:18 -0500 Subject: [PATCH 30/31] Bump version of RtspServer used --- utils/packpack/startpackpack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 2ea949078..9737c794a 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -118,7 +118,7 @@ commonprep () { fi fi - RTSPVER="cd7fd49becad6010a1b8466bfebbd93999a39878" + RTSPVER="eab32851421ffe54fec0229c3efc44c642bc8d46" if [ -e "build/RtspServer-${RTSPVER}.tar.gz" ]; then echo "Found existing RtspServer ${RTSPVER} tarball..." else From 31eff49a46a66439890c94e1519cab9a7975a1b8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 2 Feb 2022 10:49:05 -0500 Subject: [PATCH 31/31] Implement filter limits. Which go before pagination/advanced search limits --- web/ajax/events.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/ajax/events.php b/web/ajax/events.php index 4db6b99cc..c95b404c4 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -187,6 +187,9 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $col_str = 'E.*, M.Name AS Monitor'; $sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.($sort?' ORDER BY '.$sort.' '.$order:''); + if ($filter->limit() and !count($filter->pre_sql_conditions()) and !count($filter->post_sql_conditions())) { + $sql .= ' LIMIT '.$filter->limit(); + } $storage_areas = ZM\Storage::find(); $StorageById = array(); @@ -213,6 +216,12 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $unfiltered_rows[] = $row; } # end foreach row + # Filter limits come before pagination limits. + if ($filter->limit() and ($filter->limit() > count($unfiltered_rows))) { + ZM\Debug("Filtering rows due to filter->limit " . count($unfiltered_rows)." limit: ".$filter->limit()); + $unfiltered_rows = array_slice($unfiltered_rows, 0, $filter->limit()); + } + ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.'); $filtered_rows = null; @@ -251,8 +260,10 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim $filtered_rows = $unfiltered_rows; } # end if search_filter->terms() > 1 - if ($limit) + if ($limit) { + ZM\Debug("Filtering rows due to limit " . count($filtered_rows)." offset: $offset limit: $limit"); $filtered_rows = array_slice($filtered_rows, $offset, $limit); + } $returned_rows = array(); foreach ($filtered_rows as $row) {