From 02a79f79f07d18904e0e8af39ec8bc3249b8240a Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 03:12:27 -0400 Subject: [PATCH 01/56] Add option to disable auto detection of systemd. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a3c123f2..af9f45f77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ mark_as_advanced( ZM_PATH_ARP_SCAN ZM_CONFIG_DIR ZM_CONFIG_SUBDIR + ZM_DETECT_SYSTEMD ZM_SYSTEMD ZM_MANPAGE_DEST_PREFIX) @@ -188,6 +189,8 @@ set(ZM_PERL_SEARCH_PATH "" CACHE PATH installed outside Perl's default search path.") set(ZM_TARGET_DISTRO "" CACHE STRING "Build ZoneMinder for a specific distribution. Currently, valid names are: fc27, fc26, el7, OS13, FreeBSD") +set(ZM_DETECT_SYSTEMD "ON" CACHE BOOL + "Set to OFF to disable detection of systemd. default: ON") set(ZM_SYSTEMD "OFF" CACHE BOOL "Set to ON to force building ZM with systemd support. default: OFF") set(ZM_MANPAGE_DEST_PREFIX "share/man" CACHE PATH @@ -270,7 +273,7 @@ set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} stdio.h stdlib.h math set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON) # Set the systemd flag if systemd is autodetected or ZM_SYSTEMD has been set -if(ZM_SYSTEMD OR (IS_DIRECTORY /usr/lib/systemd/system) OR (IS_DIRECTORY /lib/systemd/system)) +if(ZM_SYSTEMD OR (ZM_DETECT_SYSTEMD AND ((IS_DIRECTORY /usr/lib/systemd/system) OR (IS_DIRECTORY /lib/systemd/system)))) set(WITH_SYSTEMD 1) endif() From 3b211537570930d0ead8612e9e2227720297693c Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 03:18:18 -0400 Subject: [PATCH 02/56] Fix various printf formats for 32bit, 32bit w/_FILE_OFFSET_BITS=64 and 64-bit --- src/zm_eventstream.cpp | 2 +- src/zm_image.cpp | 2 +- src/zm_monitor.cpp | 12 ++++++------ src/zm_monitor_monitorlink.cpp | 2 +- src/zm_rtsp_server_fifo_source.cpp | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index bec939187..95ae870ac 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -1097,7 +1097,7 @@ bool EventStream::send_file(const std::string &filepath) { Info("File size is zero. Unable to send raw frame %d: %s", curr_frame_id, strerror(errno)); return false; } - if (0 > fprintf(stdout, "Content-Length: %jd\r\n\r\n", filestat.st_size)) { + if (0 > fprintf(stdout, "Content-Length: %jd\r\n\r\n", static_cast(filestat.st_size))) { fclose(fdj); /* Close the file handle */ Info("Unable to send raw frame %d: %s", curr_frame_id, strerror(errno)); return false; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index ab68de90d..a3c40023d 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -914,7 +914,7 @@ bool Image::ReadRaw(const char *filename) { if ( (unsigned int)statbuf.st_size != size ) { fclose(infile); - Error("Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size); + Error("Raw file size mismatch, expected %d bytes, found %jd", size, static_cast(statbuf.st_size)); return false; } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 5a9a96bb9..53434567f 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -802,7 +802,7 @@ bool Monitor::connect() { image_buffer_count, image_size, (image_buffer_count * image_size), - mem_size); + static_cast(mem_size)); #if ZM_MEM_MAPPED mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id); if (purpose != CAPTURE) { @@ -831,25 +831,25 @@ bool Monitor::connect() { if (purpose == CAPTURE) { // Allocate the size if (ftruncate(map_fd, mem_size) < 0) { - Error("Can't extend memory map file %s to %jd bytes: %s", mem_file.c_str(), mem_size, strerror(errno)); + Error("Can't extend memory map file %s to %jd bytes: %s", mem_file.c_str(), static_cast(mem_size), strerror(errno)); close(map_fd); map_fd = -1; return false; } } else if (map_stat.st_size == 0) { - Error("Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size); + Error("Got empty memory map file size %jd, is the zmc process for this monitor running?", static_cast(map_stat.st_size)); close(map_fd); map_fd = -1; return false; } else { - Error("Got unexpected memory map file size %ld, expected %jd", map_stat.st_size, static_cast(mem_size)); + Error("Got unexpected memory map file size %jd, expected %jd", static_cast(map_stat.st_size), static_cast(mem_size)); close(map_fd); map_fd = -1; return false; } } // end if map_stat.st_size != mem_size - Debug(3, "MMap file size is %ld", map_stat.st_size); + Debug(3, "MMap file size is %jd", static_cast(map_stat.st_size)); #ifdef MAP_LOCKED mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0); if (mem_ptr == MAP_FAILED) { @@ -911,7 +911,7 @@ bool Monitor::connect() { ); alarm_image.HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ if (alarm_image.Buffer() + image_size > mem_ptr + mem_size) { - Warning("We will exceed memsize by %ld bytes!", alarm_image.Buffer() + image_size - (mem_ptr + mem_size)); + Warning("We will exceed memsize by %td bytes!", (alarm_image.Buffer() + image_size) - (mem_ptr + mem_size)); } if (purpose == CAPTURE) { diff --git a/src/zm_monitor_monitorlink.cpp b/src/zm_monitor_monitorlink.cpp index ffd48d351..8fb485888 100644 --- a/src/zm_monitor_monitorlink.cpp +++ b/src/zm_monitor_monitorlink.cpp @@ -115,7 +115,7 @@ bool Monitor::MonitorLink::connect() { disconnect(); return false; } else if (map_stat.st_size < mem_size) { - Error("Got unexpected memory map file size %ld, expected %jd", map_stat.st_size, static_cast(mem_size)); + Error("Got unexpected memory map file size %jd, expected %jd", static_cast(map_stat.st_size), static_cast(mem_size)); disconnect(); return false; } diff --git a/src/zm_rtsp_server_fifo_source.cpp b/src/zm_rtsp_server_fifo_source.cpp index d067395ae..fc1ee9caa 100644 --- a/src/zm_rtsp_server_fifo_source.cpp +++ b/src/zm_rtsp_server_fifo_source.cpp @@ -215,7 +215,7 @@ int ZoneMinderFifoSource::getNextFrame() { return 0; } if (header_start != m_buffer) { - Debug(4, "ZM Packet didn't start at beginning of buffer %ld. %c%c", + Debug(4, "ZM Packet didn't start at beginning of buffer %td. %c%c", header_start - m_buffer.head(), m_buffer[0], m_buffer[1]); } From ca1cca2ff4c1fbeface758f34e14e5463db36a92 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 03:20:20 -0400 Subject: [PATCH 03/56] Add _FILE_OFFSET_BITS=64 define to enable large file support on 32-bit. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a3c123f2..9c7f64840 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") set (CMAKE_CXX_STANDARD 17) set (CMAKE_CXX_STANDARD_REQUIRED ON) +add_compile_definitions(_FILE_OFFSET_BITS=64) + include(ConfigureBaseTargets) include(CheckPlatform) From 0db4a18ff627db950372caea185e9c135c5fa50c Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 03:21:25 -0400 Subject: [PATCH 04/56] Switch to unsigned int for loop to restrict size of printf. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC 12 incorrectly detects a format truncation. src/zm_logger.cpp:86:39: warning: ā€˜%d’ directive output may be truncated writing between 1 and 11 bytes into a region of size 2 [-Wformat-truncation=] 86 | snprintf(code, sizeof(code), "DB%d", i); | ^~ src/zm_logger.cpp:86:36: note: directive argument in the range [-2147483647, 9] 86 | snprintf(code, sizeof(code), "DB%d", i); | ^~~~~~ --- src/zm_logger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index f43ea6f30..a4d9e92f8 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -82,8 +82,8 @@ Logger::Logger() : smSyslogPriorities[PANIC] = LOG_ERR; char code[4] = ""; - for (int i = DEBUG1; i <= DEBUG9; i++) { - snprintf(code, sizeof(code), "DB%d", i); + for (unsigned int i = DEBUG1; i <= DEBUG9; i++) { + snprintf(code, sizeof(code), "DB%u", i); smCodes[i] = code; smSyslogPriorities[i] = LOG_DEBUG; } From 3c5e44dfe52b3b10bcdb30b4bf674f58d7dc8fb3 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 10:03:16 +0000 Subject: [PATCH 05/56] Need unistd.h for getpid(). --- src/zmc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zmc.cpp b/src/zmc.cpp index 9e4506447..1942aec35 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -65,6 +65,7 @@ possible, this should run at more or less constant speed. #include #include +#include void Usage() { fprintf(stderr, "zmc -d or -r -H -P -p or -f or -m \n"); From df414fc028aebddd0105c7405dbccfe61dc7458a Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 10:03:54 +0000 Subject: [PATCH 06/56] Need to cast duration to system_clock resolution. --- src/zm_time.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zm_time.cpp b/src/zm_time.cpp index 041962003..828e7b0f3 100644 --- a/src/zm_time.cpp +++ b/src/zm_time.cpp @@ -36,8 +36,9 @@ std::string SystemTimePointToString(SystemTimePoint tp) { } std::string TimePointToString(TimePoint tp) { + const auto tp_dur = std::chrono::duration_cast(tp - std::chrono::steady_clock::now()); time_t tp_sec = std::chrono::system_clock::to_time_t( - std::chrono::system_clock::now() + (tp - std::chrono::steady_clock::now())); + std::chrono::system_clock::now() + tp_dur); Microseconds now_frac = std::chrono::duration_cast( tp.time_since_epoch() - std::chrono::duration_cast(tp.time_since_epoch())); From 5b44ee729d2cc2fafddaedb4b18a115b7a93a921 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 16 Jul 2022 10:05:46 +0000 Subject: [PATCH 07/56] Fix sendfile calling convention taking into account possible null offset parameter. --- src/zm_sendfile.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_sendfile.h b/src/zm_sendfile.h index 26d6011df..a4f703efc 100644 --- a/src/zm_sendfile.h +++ b/src/zm_sendfile.h @@ -22,10 +22,12 @@ ssize_t zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { return err; #elif HAVE_SENDFILE7_SUPPORT - ssize_t err = sendfile(in_fd, out_fd, offset, size, nullptr, &size, 0); + off_t sbytes = 0; + off_t ofs = offset ? *offset : 0; + ssize_t err = sendfile(in_fd, out_fd, ofs, size, nullptr, &sbytes, 0); if (err && errno != EAGAIN) return -errno; - return size; + return sbytes; #else uint8_t buffer[size]; ssize_t err = read(in_fd, buffer, size); From 981769b9db64d7b38ca99e0ff84a177b56106bc3 Mon Sep 17 00:00:00 2001 From: ovargasp <59069596+ovargasp@users.noreply.github.com> Date: Wed, 20 Jul 2022 11:51:13 -0600 Subject: [PATCH 08/56] Allows users to set an specific "Onvif alarm text" for each monitor (#3535) * Allows users to set an specific "Onvif alarm text" for each monitor With this personalization the cameras that use strings different to "MotionAlarm" such as Dahua, will be able to process their Onvif alarms. To make it easier, the change sets a default "Alarm Text" value for it to work out of the box and then allows to modify it according to the particular necessities of each brand. It consists of a new column on table Monitors, changes on Monitors.h, Monitors.cpp and a change on UI to manage the value of Alarm Text per camera. Updated es_la.php language file. Co-authored-by: ovargasp --- db/monitors_dbupdate.sql | 1 + src/zm_monitor.cpp | 13 +++++++++---- src/zm_monitor.h | 1 + web/includes/Monitor.php | 1 + web/lang/es_la.php | 2 +- web/skins/classic/views/monitor.php | 4 ++++ 6 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 db/monitors_dbupdate.sql diff --git a/db/monitors_dbupdate.sql b/db/monitors_dbupdate.sql new file mode 100644 index 000000000..ee76af60e --- /dev/null +++ b/db/monitors_dbupdate.sql @@ -0,0 +1 @@ +ALTER TABLE zm.Monitors ADD Onvif_Alarm_Txt varchar(30) DEFAULT 'MotionAlarm' NULL; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 5a9a96bb9..e70b6a9b0 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -95,10 +95,11 @@ std::string load_monitor_sql = "`ImageBufferCount`, `MaxImageBufferCount`, `WarmupCount`, `PreEventCount`, `PostEventCount`, `StreamReplayBuffer`, `AlarmFrameCount`, " "`SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, " "`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`," -"`RTSPServer`, `RTSPStreamName`," +"`RTSPServer`, `RTSPStreamName`, `ONVIF_Alarm_Txt`," "`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, `use_Amcrest_API`, " "`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1, ZoneCount FROM `Monitors`"; + std::string CameraType_Strings[] = { "Unknown", "Local", @@ -229,6 +230,7 @@ Monitor::Monitor() embed_exif(false), rtsp_server(false), rtsp_streamname(""), + onvif_alarm_txt(""), importance(0), zone_count(0), capture_max_fps(0), @@ -494,6 +496,9 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { /* "`RTSPServer`,`RTSPStreamName`, */ rtsp_server = (*dbrow[col] != '0'); col++; rtsp_streamname = dbrow[col]; col++; +// get alarm text from table. + onvif_alarm_txt = std::string(dbrow[col] ? dbrow[col] : ""); col++; + /* "`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, `use_Amcrest_API`, " */ onvif_url = std::string(dbrow[col] ? dbrow[col] : ""); col++; @@ -1705,7 +1710,7 @@ bool Monitor::Poll() { Debug(1, "Got Good Response! %i", result); for (auto msg : tev__PullMessagesResponse.wsnt__NotificationMessage) { if (msg->Topic->__any.text != NULL && - std::strstr(msg->Topic->__any.text, "MotionAlarm") && + std::strstr(msg->Topic->__any.text, onvif_alarm_txt.c_str()) && msg->Message.__any.elts != NULL && msg->Message.__any.elts->next != NULL && msg->Message.__any.elts->next->elts != NULL && @@ -2065,7 +2070,7 @@ bool Monitor::Analyse() { (event_close_mode == CLOSE_ALARM)); } if ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1)) { - Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s", + Info("%s: %03d - ExtAlm - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s", name.c_str(), snap->image_index, Event::PreAlarmCount(), alarm_frame_count, cause.c_str()); shared_data->state = state = ALARM; @@ -2078,7 +2083,7 @@ bool Monitor::Analyse() { Debug(1, "%s: %03d - Alarmed frame while in alert state. Consecutive alarmed frames left to return to alarm state: %03d", name.c_str(), analysis_image_count, alert_to_alarm_frame_count); if (alert_to_alarm_frame_count == 0) { - Info("%s: %03d - Gone back into alarm state", name.c_str(), analysis_image_count); + Info("%s: %03d - ExtAlm - Gone back into alarm state Cause:ONVIF", name.c_str(), analysis_image_count); shared_data->state = state = ALARM; } } else if (state == TAPE) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 68038936c..ecc1e373f 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -457,6 +457,7 @@ protected: bool embed_exif; // Whether to embed Exif data into each image frame or not bool rtsp_server; // Whether to include this monitor as an rtsp server stream std::string rtsp_streamname; // path in the rtsp url for this monitor + std::string onvif_alarm_txt; // def onvif_alarm_txt int importance; // Importance of this monitor, affects Connection logging errors. unsigned int zone_count; diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index d583c0525..855f91825 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -154,6 +154,7 @@ public static function getStatuses() { 'ONVIF_Username' => '', 'ONVIF_Password' => '', 'ONVIF_Options' => '', + 'Onvif_Alarm_Txt' => '', 'ONVIF_Event_Listener' => '0', 'use_Amcrest_API' => '0', 'Device' => '', diff --git a/web/lang/es_la.php b/web/lang/es_la.php index 63c83cef0..c3bd51c36 100644 --- a/web/lang/es_la.php +++ b/web/lang/es_la.php @@ -907,7 +907,7 @@ $SLANG = array( 'Showing Analysis' => 'Mostrar Analisis', 'ConfirmDeleteTitle' => 'Borrar Seleccionados', 'Continuous' => 'Continuo', - + 'ONVIF_Alarm_Txt' => 'Texto Alarma ONVIF', //added 18/07/2022 ); diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 76ec7bcea..675fdb3c9 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -578,6 +578,10 @@ switch ($name) { + + + + translate('Enabled'), '0'=>translate('Disabled')), $monitor->ONVIF_Event_Listener()); ?> From 447fd3efd0674ee51b08f58069ac5723fb3abaaf Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Fri, 22 Jul 2022 10:20:10 -0400 Subject: [PATCH 09/56] CI/Cirrus: Update to FreeBSD 12.3 --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 3f2182f9c..4b9f1b055 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -2,7 +2,7 @@ task: name: freebsd-build freebsd_instance: matrix: - - image_family: freebsd-12-2 + - image_family: freebsd-12-3 - image_family: freebsd-13-0 prepare_script: From 9c40e2bb86b29333069b8c7e2673b43ac2022725 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Fri, 22 Jul 2022 10:30:36 -0400 Subject: [PATCH 10/56] CI: Remove CI workflow for Debian Stretch Debian Stretch has finished LTS and is too old for current code base. --- .github/workflows/ci-stretch.yml | 43 -------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/ci-stretch.yml diff --git a/.github/workflows/ci-stretch.yml b/.github/workflows/ci-stretch.yml deleted file mode 100644 index cb192074d..000000000 --- a/.github/workflows/ci-stretch.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: CI Debian Stretch - -on: - push: - branches: - - '*' - pull_request: - branches: [ master ] - -permissions: - contents: read - -jobs: - build: - defaults: - run: - shell: bash - runs-on: ubuntu-latest - container: debian:stretch-backports - - steps: - - name: Update packages - run: apt-get -qq update && apt-get -qq upgrade - - name: Install git - run: apt-get -qq install git/stretch-backports git-man/stretch-backports - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Install dependencies - run: > - apt-get -qq install make cmake g++ - default-libmysqlclient-dev - libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libavdevice-dev - libcurl4-gnutls-dev libvlc-dev libvncserver-dev - libdate-manip-perl libdbd-mysql-perl libsys-mmap-perl libwww-perl - libpolkit-gobject-1-dev - libssl-dev - - name: Prepare - run: mkdir build - - name: Configure - run: cd build && cmake --version && cmake .. -DBUILD_MAN=0 -DENABLE_WERROR=1 - - name: Build - run: cd build && make -j3 | grep --line-buffered -Ev '^(cp lib\/|Installing.+\.pm)' && (exit ${PIPESTATUS[0]}) From cfa4cdc9a3c9c95d8a27df7f5ebaeedcaba569a6 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Fri, 22 Jul 2022 11:05:14 -0400 Subject: [PATCH 11/56] Use add_compile_options instead of add_compile_definitions add_compile_definitions is not available until version 3.12. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 052792e4b..97dc6ae9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") set (CMAKE_CXX_STANDARD 17) set (CMAKE_CXX_STANDARD_REQUIRED ON) -add_compile_definitions(_FILE_OFFSET_BITS=64) +add_compile_options(-D_FILE_OFFSET_BITS=64) include(ConfigureBaseTargets) include(CheckPlatform) From 3b69a51ff85653586f42dde147158f7b4acde1a6 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Fri, 22 Jul 2022 12:00:42 -0400 Subject: [PATCH 12/56] Try to fix warning again that printf might exceed buffer. The last fix worked on GCC 11 & 12 but broke GCC 8. --- src/zm_logger.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index a4d9e92f8..4db535a78 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -82,8 +82,9 @@ Logger::Logger() : smSyslogPriorities[PANIC] = LOG_ERR; char code[4] = ""; - for (unsigned int i = DEBUG1; i <= DEBUG9; i++) { - snprintf(code, sizeof(code), "DB%u", i); + // Extra comparison against DEBUG1 to ensure GCC knows we are printing a single byte. + for (int i = DEBUG1; i>=DEBUG1 && i <= DEBUG9; i++) { + snprintf(code, sizeof(code), "DB%d", i); smCodes[i] = code; smSyslogPriorities[i] = LOG_DEBUG; } From c0304165006984cf07289567d76c58b3fda727b8 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Fri, 22 Jul 2022 19:52:09 -0400 Subject: [PATCH 13/56] Fix truncation warning. --- src/zm_sdp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/zm_sdp.cpp b/src/zm_sdp.cpp index bdbc9e1d1..1593b1640 100644 --- a/src/zm_sdp.cpp +++ b/src/zm_sdp.cpp @@ -290,7 +290,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const { #if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100)) formatContext->url = av_strdup(mUrl.c_str()); #else - strncpy(formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename)); + strncpy(formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) - 1); + formatContext->filename[sizeof(formatContext->filename) - 1] = '\0'; #endif /* if ( mName.length() ) From a65e960ce6ef406318d163fac445fb31adb67bb7 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 23 Jul 2022 04:01:56 -0400 Subject: [PATCH 14/56] Docker image of CentOS 7, GCC 8 & ZoneMinder dependencies --- .github/docker/centos7-gcc8-zm/Dockerfile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/docker/centos7-gcc8-zm/Dockerfile diff --git a/.github/docker/centos7-gcc8-zm/Dockerfile b/.github/docker/centos7-gcc8-zm/Dockerfile new file mode 100644 index 000000000..80210ebb9 --- /dev/null +++ b/.github/docker/centos7-gcc8-zm/Dockerfile @@ -0,0 +1,11 @@ +FROM centos:7 + +LABEL name="centos7-gcc8-zm" \ + version="1" + +RUN yum -y install https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && \ + yum -y install https://repo.ius.io/ius-release-el7.rpm && yum -y install git236 && \ + yum -y update && yum -y install make cmake3 gcc-c++ mariadb-devel ffmpeg-devel libcurl-devel vlc-devel libvncserver-devel libjpeg-turbo-devel "perl(Date::Manip)" "perl(DBD::mysql)" "perl(ExtUtils::MakeMaker)" "perl(Sys::Mmap)" "perl(Sys::Syslog)" "perl(LWP::UserAgent)" polkit-devel libjwt-devel && \ + yum -y install centos-release-scl-rh && \ + INSTALL_PKGS="devtoolset-8-gcc devtoolset-8-gcc-c++" && \ + yum -y install --setopt=tsflags=nodocs $INSTALL_PKGS From 78962c461bde428f8847102cb533432481c59b59 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Fri, 22 Jul 2022 19:57:55 -0400 Subject: [PATCH 15/56] Use docker image for centos 7 --- .github/workflows/ci-centos-7.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-centos-7.yml b/.github/workflows/ci-centos-7.yml index 1c1dad8e1..39973d05a 100644 --- a/.github/workflows/ci-centos-7.yml +++ b/.github/workflows/ci-centos-7.yml @@ -9,6 +9,7 @@ on: permissions: contents: read + packages: read jobs: build: @@ -22,21 +23,15 @@ jobs: - crypto_backend: gnutls jwt_backend: libjwt runs-on: ubuntu-latest - container: centos:7 + container: ghcr.io/dougnazar/centos7-gcc8-zm:latest steps: - - name: Enable RPMFusion and EPEL - run: yum -y install https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm - - name: Install git - run: yum -y install https://repo.ius.io/ius-release-el7.rpm && yum -y install git224 - uses: actions/checkout@v3 with: submodules: recursive - - name: Install dependencies - run: yum -y update && yum -y install make cmake3 gcc-c++ mariadb-devel ffmpeg-devel libcurl-devel vlc-devel libvncserver-devel libjpeg-turbo-devel "perl(Date::Manip)" "perl(DBD::mysql)" "perl(ExtUtils::MakeMaker)" "perl(Sys::Mmap)" "perl(Sys::Syslog)" "perl(LWP::UserAgent)" polkit-devel libjwt-devel - name: Prepare run: mkdir build - name: Configure - run: cd build && cmake3 --version && cmake3 .. -DBUILD_MAN=0 -DENABLE_WERROR=1 -DZM_CRYPTO_BACKEND=${{ matrix.crypto_backend }} -DZM_JWT_BACKEND=${{ matrix.jwt_backend }} + run: source /usr/bin/scl_source enable devtoolset-8 && cd build && cmake3 --version && cmake3 .. -DBUILD_MAN=0 -DENABLE_WERROR=1 -DZM_CRYPTO_BACKEND=${{ matrix.crypto_backend }} -DZM_JWT_BACKEND=${{ matrix.jwt_backend }} - name: Build - run: cd build && make -j3 | grep --line-buffered -Ev '^(cp lib\/|Installing.+\.pm)' && (exit ${PIPESTATUS[0]}) + run: source /usr/bin/scl_source enable devtoolset-8 && cd build && make -j3 | grep --line-buffered -Ev '^(cp lib\/|Installing.+\.pm)' && (exit ${PIPESTATUS[0]}) From d2d5b6e48920b0af698fc7a2847bb4d0a55d2335 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:07:13 -0400 Subject: [PATCH 16/56] Add ONVIF_Alarm_text to zm_create.sql --- db/zm_create.sql.in | 1 + 1 file changed, 1 insertion(+) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 30bf6db9f..da9544989 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -474,6 +474,7 @@ CREATE TABLE `Monitors` ( `ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '', `ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '', `ONVIF_Event_Listener` BOOLEAN NOT NULL DEFAULT FALSE, + `ONVIF_Alarm_Text` VARCHAR(30) DEFAULT 'MotionAlarm', `use_Amcrest_API` BOOLEAN NOT NULL DEFAULT FALSE, `Device` tinytext NOT NULL default '', `Channel` tinyint(3) unsigned NOT NULL default '0', From a3395df49f75b4d8624eab4b50b781e13308dafe Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:07:45 -0400 Subject: [PATCH 17/56] Rename ONVIF_Alarm_Txt to ONVIF_Alarm_Text --- db/zm_update-1.37.19.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 db/zm_update-1.37.19.sql diff --git a/db/zm_update-1.37.19.sql b/db/zm_update-1.37.19.sql new file mode 100644 index 000000000..9edee9766 --- /dev/null +++ b/db/zm_update-1.37.19.sql @@ -0,0 +1,18 @@ +-- +-- Update Monitors table to have ONVIF_Alarm_Text +-- + +SELECT 'Checking for ONVIF_Alarm_Text in Monitors'; +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'ONVIF_Alarm_Text' + ) > 0, +"SELECT 'Column ONVIF_Alarm_Text already exists in Monitors'", +"ALTER TABLE zm.Monitors ADD Onvif_Alarm_Text varchar(30) DEFAULT 'MotionAlarm' AFTER `ONVIF_Event_Listener`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; From bfeee1ed0977aff731f360bd3d7acfb98d1cebbc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:08:27 -0400 Subject: [PATCH 18/56] rename Onvif_Alarm_Txt to ONVIF_Alarm_Text --- web/includes/Monitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 855f91825..2deac9be5 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -154,7 +154,7 @@ public static function getStatuses() { 'ONVIF_Username' => '', 'ONVIF_Password' => '', 'ONVIF_Options' => '', - 'Onvif_Alarm_Txt' => '', + 'ONVIF_Alarm_Text' => '', 'ONVIF_Event_Listener' => '0', 'use_Amcrest_API' => '0', 'Device' => '', From 917c3bd7ba7a5c92d61886e3c90eac8593c4169c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:08:42 -0400 Subject: [PATCH 19/56] rename Onvif_Alarm_Txt to ONVIF_Alarm_Text --- web/lang/es_la.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/lang/es_la.php b/web/lang/es_la.php index c3bd51c36..3ce632091 100644 --- a/web/lang/es_la.php +++ b/web/lang/es_la.php @@ -907,7 +907,7 @@ $SLANG = array( 'Showing Analysis' => 'Mostrar Analisis', 'ConfirmDeleteTitle' => 'Borrar Seleccionados', 'Continuous' => 'Continuo', - 'ONVIF_Alarm_Txt' => 'Texto Alarma ONVIF', //added 18/07/2022 + 'ONVIF_Alarm_Text' => 'Texto Alarma ONVIF', //added 18/07/2022 ); From 8a8a5fd5c00218b188d29848bfc364b05e308e1c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:09:01 -0400 Subject: [PATCH 20/56] rename Onvif_Alarm_Txt to ONVIF_Alarm_Text --- src/zm_monitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 1e06fe38a..743e07c2c 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -95,7 +95,7 @@ std::string load_monitor_sql = "`ImageBufferCount`, `MaxImageBufferCount`, `WarmupCount`, `PreEventCount`, `PostEventCount`, `StreamReplayBuffer`, `AlarmFrameCount`, " "`SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, " "`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`," -"`RTSPServer`, `RTSPStreamName`, `ONVIF_Alarm_Txt`," +"`RTSPServer`, `RTSPStreamName`, `ONVIF_Alarm_Text`," "`ONVIF_URL`, `ONVIF_Username`, `ONVIF_Password`, `ONVIF_Options`, `ONVIF_Event_Listener`, `use_Amcrest_API`, " "`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1, ZoneCount FROM `Monitors`"; From 00c160da0ca23be7aaffc60f88073470ec0805e4 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:09:26 -0400 Subject: [PATCH 21/56] rename Onvif_Alarm_Txt to ONVIF_Alarm_Text --- web/skins/classic/views/monitor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 675fdb3c9..b137d254b 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -579,8 +579,8 @@ switch ($name) { - - + + From 5fde6129dd48d7eb6349ac7a049d23f5f6c04def Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 23 Jul 2022 16:10:38 -0400 Subject: [PATCH 22/56] Bump version to 1.37.19 for ONVIF_Alarm_Text changes --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 98b9bb581..b7a0ef948 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -37,7 +37,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.37.18 +Version: 1.37.19 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 05f79ff79..b49b97a4b 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.37.18 +1.37.19 From 262c08780038582a1f3481536d293c27bc4c9c77 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sun, 24 Jul 2022 04:28:37 -0400 Subject: [PATCH 23/56] Use old event unpack() on 32bit systems. PHP unpack() 64bit formats aren't available on 32bit. --- web/ajax/stream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ajax/stream.php b/web/ajax/stream.php index fe2bd52fc..492d8d8bd 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -158,7 +158,7 @@ if ($have_semaphore !== false) { ajaxResponse(array('status'=>$data)); break; case MSG_DATA_EVENT : - if ( version_compare( phpversion(), '5.6.0', '<') ) { + if ( PHP_INT_SIZE===4 || version_compare( phpversion(), '5.6.0', '<') ) { ZM\Debug('Using old unpack methods to handle 64bit event id'); $data = unpack('ltype/ieventlow/ieventhigh/dduration/dprogress/irate/izoom/Cpaused', $msg); $data['event'] = $data['eventhigh'] << 32 | $data['eventlow']; From 8788b372fe342b055dbad26288361998b758cd65 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 10 Jul 2022 09:52:29 -0400 Subject: [PATCH 24/56] Don't reload img steam on auth hash change. Just update the global variable. --- web/skins/classic/views/js/event.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index fca346b2a..e50ea65f3 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -28,7 +28,7 @@ var zmsBroke = false; //Use alternate navigation if zms has crashed var wasHidden = false; function streamReq(data) { - if ( auth_hash ) data.auth = auth_hash; + if (auth_hash) data.auth = auth_hash; data.connkey = connKey; data.view = 'request'; data.request = 'stream'; @@ -312,11 +312,7 @@ function getCmdResponse(respObj, respText) { updateProgressBar(); if (streamStatus.auth) { - // Try to reload the image stream. - var streamImg = document.getElementById('evtStream'); - if (streamImg) { - streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth); - } + auth_hash = streamStatus.auth; } // end if haev a new auth hash streamCmdTimer = setTimeout(streamQuery, streamTimeout); //Timeout is refresh rate for progressBox and time display From 6e1901894f5b3fdd2e7944329e9b87da40610d3b Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sun, 24 Jul 2022 07:57:01 -0400 Subject: [PATCH 25/56] CI: Only attempt to create packages on push. --- .github/workflows/create-packages.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/create-packages.yml b/.github/workflows/create-packages.yml index f0d6083f3..5e7754e2d 100644 --- a/.github/workflows/create-packages.yml +++ b/.github/workflows/create-packages.yml @@ -3,8 +3,6 @@ name: Create packages on: push: branches: [ master ] - pull_request: - branches: [ master ] jobs: package: From 7cc8cb17fab1424f367fcc4f93b40e0f3d3f6c76 Mon Sep 17 00:00:00 2001 From: Jonathan Mathews Date: Tue, 26 Jul 2022 09:30:46 -0500 Subject: [PATCH 26/56] Enable building on ubuntu jammy --- 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 9737c794a..8cee2fe20 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -369,7 +369,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia setdebpkgname movecrud - if [ "${DIST}" == "bionic" ] || [ "${DIST}" == "focal" ] || [ "${DIST}" == "hirsute" ] || [ "${DIST}" == "impish" ] || [ "${DIST}" == "buster" ] || [ "${DIST}" == "bullseye" ]; then + if [ "${DIST}" == "bionic" ] || [ "${DIST}" == "focal" ] || [ "${DIST}" == "hirsute" ] || [ "${DIST}" == "impish" ] || [ "${DIST}" == "jammy" ] || [ "${DIST}" == "buster" ] || [ "${DIST}" == "bullseye" ]; then ln -sfT distros/ubuntu2004 debian elif [ "${DIST}" == "beowulf" ]; then ln -sfT distros/beowulf debian From bd304ecbecfcfc9313079700524ab8d6db92fcfe Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 26 Jul 2022 19:09:09 -0400 Subject: [PATCH 27/56] include ffmpeg.h. Fixes #3539 --- src/zm_packet.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_packet.h b/src/zm_packet.h index 23a593b22..07e7e7a45 100644 --- a/src/zm_packet.h +++ b/src/zm_packet.h @@ -20,6 +20,7 @@ #ifndef ZM_PACKET_H #define ZM_PACKET_H +#include "zm_ffmpeg.h" #include "zm_logger.h" #include "zm_time.h" #include "zm_zone.h" From 590697bd807ab9a74d605122ef0be4a094db9605 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Wed, 27 Jul 2022 12:19:16 -0400 Subject: [PATCH 28/56] Add padding to shared_data to re-align fields on 32bit --- src/zm_monitor.h | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/zm_monitor.h b/src/zm_monitor.h index ecc1e373f..e36729b51 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -192,37 +192,40 @@ protected: uint8_t recording; /* +55 */ uint8_t signal; /* +56 */ uint8_t format; /* +57 */ - uint32_t imagesize; /* +58 */ - uint32_t last_frame_score; /* +62 */ - uint32_t audio_frequency; /* +66 */ - uint32_t audio_channels; /* +70 */ + uint8_t reserved1; /* +58 */ + uint8_t reserved2; /* +59 */ + uint32_t imagesize; /* +60 */ + uint32_t last_frame_score; /* +64 */ + uint32_t audio_frequency; /* +68 */ + uint32_t audio_channels; /* +72 */ + uint32_t reserved3; /* +76 */ /* ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. ** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple ** of 8. Add or delete epadding's to achieve this. */ - union { /* +72 */ + union { /* +80 */ time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */ uint64_t extrapad1; }; - union { /* +80 */ + union { /* +88 */ time_t zmc_heartbeat_time; /* Constantly updated by zmc. Used to determine if the process is alive or hung or dead */ uint64_t extrapad2; }; - union { /* +88 */ + union { /* +96 */ time_t last_write_time; uint64_t extrapad3; }; - union { /* +96 */ + union { /* +104 */ time_t last_read_time; uint64_t extrapad4; }; - union { /* +104 */ + union { /* +112 */ time_t last_viewed_time; uint64_t extrapad5; }; - uint8_t control_state[256]; /* +112 */ + uint8_t control_state[256]; /* +120 */ char alarm_cause[256]; char video_fifo_path[64]; From a34db8bc380b8fea0730e1786c6d3240152fa939 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Wed, 27 Jul 2022 12:21:44 -0400 Subject: [PATCH 29/56] Don't hardcode db in upgrade script. --- db/zm_update-1.37.19.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/zm_update-1.37.19.sql b/db/zm_update-1.37.19.sql index 9edee9766..ec8c61a8c 100644 --- a/db/zm_update-1.37.19.sql +++ b/db/zm_update-1.37.19.sql @@ -11,7 +11,7 @@ SET @s = (SELECT IF( AND column_name = 'ONVIF_Alarm_Text' ) > 0, "SELECT 'Column ONVIF_Alarm_Text already exists in Monitors'", -"ALTER TABLE zm.Monitors ADD Onvif_Alarm_Text varchar(30) DEFAULT 'MotionAlarm' AFTER `ONVIF_Event_Listener`" +"ALTER TABLE Monitors ADD Onvif_Alarm_Text varchar(30) DEFAULT 'MotionAlarm' AFTER `ONVIF_Event_Listener`" )); PREPARE stmt FROM @s; From 5cb5132bf715fc2e00fb00a2b90cad758152b6c8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 28 Jul 2022 15:26:32 -0400 Subject: [PATCH 30/56] Fix crash due to double delete of Janus_Manager --- src/zm_monitor.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 743e07c2c..b7828426b 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -3177,13 +3177,13 @@ int Monitor::Close() { } if (analysis_thread) { analysis_thread->Stop(); - Debug(1, "Analysi stopped"); + Debug(1, "Analysis stopped"); } //ONVIF Teardown if (Poller) { Poller->Stop(); - Debug(1, "Polleri stopped"); + Debug(1, "Poller stopped"); } #ifdef WITH_GSOAP if (onvif_event_listener && (soap != nullptr)) { @@ -3198,8 +3198,9 @@ int Monitor::Close() { } //End ONVIF #endif //Janus Teardown - if (janus_enabled && (purpose == CAPTURE)) { + if (janus_enabled and (purpose == CAPTURE) and Janus_Manager) { delete Janus_Manager; + Janus_Manager = nullptr; } if (audio_fifo) { From f463246e43705cceb81393365f62add84221cdf0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 28 Jul 2022 15:46:01 -0400 Subject: [PATCH 31/56] Log reorder_queue_size --- src/zm_videostore.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 2599c903b..afedcd1e0 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1321,8 +1321,8 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { pkt->dts = last_dts[stream->index]; } else { if ((last_dts[stream->index] != AV_NOPTS_VALUE) and (pkt->dts < last_dts[stream->index])) { - Warning("non increasing dts, fixing. our dts %" PRId64 " stream %d last_dts %" PRId64, - pkt->dts, stream->index, last_dts[stream->index]); + Warning("non increasing dts, fixing. our dts %" PRId64 " stream %d last_dts %" PRId64 ". reorder_queue_size=%zu", + pkt->dts, stream->index, last_dts[stream->index], reorder_queue_size); pkt->dts = last_dts[stream->index]; } next_dts[stream->index] = pkt->dts + pkt->duration; From 33943acbc405f72305afbb8905fae864c34c2f57 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 28 Jul 2022 16:27:28 -0400 Subject: [PATCH 32/56] Code style, spacing. Move check for trailing slash into the if specified janus_path part --- src/zm_monitor_janus.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/zm_monitor_janus.cpp b/src/zm_monitor_janus.cpp index d46ce4ce1..0994b014f 100644 --- a/src/zm_monitor_janus.cpp +++ b/src/zm_monitor_janus.cpp @@ -27,15 +27,20 @@ Monitor::JanusManager::JanusManager(Monitor *parent_) : //constructor takes care of init and calls add_to parent = parent_; if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) { - janus_endpoint = config.janus_path; //TODO: strip trailing / + janus_endpoint = config.janus_path; + //remove the trailing slash if present + if (janus_endpoint.back() == '/') janus_endpoint.pop_back(); } else { janus_endpoint = "127.0.0.1:8088/janus"; } - if (janus_endpoint.back() == '/') janus_endpoint.pop_back(); //remove the trailing slash if present std::size_t at_pos = parent->path.find("@", 7); - if (at_pos != std::string::npos) { //If we find an @ symbol, we have a username/password. Otherwise, passwordless login. + if (at_pos != std::string::npos) { + //If we find an @ symbol, we have a username/password. Otherwise, passwordless login. std::size_t colon_pos = parent->path.find(":", 7); //Search for the colon, but only after the rtsp:// text. - if (colon_pos == std::string::npos) throw std::runtime_error("Cannot Parse URL for Janus."); //Looks like an invalid url + if (colon_pos == std::string::npos) { + //Looks like an invalid url + throw std::runtime_error("Cannot Parse URL for Janus."); + } rtsp_username = parent->path.substr(7, colon_pos-7); rtsp_password = parent->path.substr(colon_pos+1, at_pos - colon_pos - 1); rtsp_path = "rtsp://"; @@ -161,12 +166,12 @@ int Monitor::JanusManager::add_to_janus() { CURLcode res; std::string response; - curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str()); res = curl_easy_perform(curl); - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); if (res != CURLE_OK) { Error("Failed to curl_easy_perform adding rtsp stream"); @@ -192,8 +197,8 @@ int Monitor::JanusManager::add_to_janus() { size_t Monitor::JanusManager::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { - ((std::string*)userp)->append((char*)contents, size * nmemb); - return size * nmemb; + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; } /* @@ -216,11 +221,10 @@ void Monitor::JanusManager::generateKey() } */ - int Monitor::JanusManager::get_janus_session() { janus_session = ""; curl = curl_easy_init(); - if(!curl) return -1; + if (!curl) return -1; std::string endpoint = janus_endpoint; std::string response; @@ -249,15 +253,14 @@ int Monitor::JanusManager::get_janus_session() { int Monitor::JanusManager::get_janus_handle() { curl = curl_easy_init(); - if(!curl) return -1; - + if (!curl) return -1; CURLcode res; std::string response = ""; std::string endpoint = janus_endpoint+"/"+janus_session; std::string postData = "{\"janus\" : \"attach\", \"plugin\" : \"janus.plugin.streaming\", \"transaction\" : \"randomString\"}"; - curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str()); + curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str()); From 7f192c4e47268da89b36745cb5b6f97d17f9e1b2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 28 Jul 2022 14:41:34 -0400 Subject: [PATCH 33/56] Merge pull request #3551 from dougnazar/fix_crash Fix zmc Segfault on 1.36.20 --- src/zm_videostore.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index afedcd1e0..082de8e25 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -964,6 +964,11 @@ int VideoStore::writePacket(const std::shared_ptr &zm_pkt) { if (zm_pkt->codec_type == AVMEDIA_TYPE_VIDEO) { stream_index = video_out_stream->index; } else if (zm_pkt->codec_type == AVMEDIA_TYPE_AUDIO) { + if (!audio_out_stream) { + Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); + return 0; + // FIXME -ve return codes do not free packet in ffmpeg_camera at the moment + } stream_index = audio_out_stream->index; } else { Error("Unknown stream type in packet (%d)", zm_pkt->codec_type); @@ -1192,10 +1197,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_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; - opkt->size = ipkt->size; - opkt->flags = ipkt->flags; - opkt->duration = ipkt->duration; av_packet_ref(opkt, ipkt); if (ipkt->dts != AV_NOPTS_VALUE) { @@ -1223,12 +1224,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet) { - if (!audio_out_stream) { - Debug(1, "Called writeAudioFramePacket when no audio_out_stream"); - return 0; - // FIXME -ve return codes do not free packet in ffmpeg_camera at the moment - } - AVPacket *ipkt = &zm_packet->packet; ZM_DUMP_STREAM_PACKET(audio_in_stream, (*ipkt), "input packet"); From d8dea37c88a385093ba4071820cb9d178ad9e3f7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 28 Jul 2022 16:33:38 -0400 Subject: [PATCH 34/56] When selecting AlarmedZone, must check for SCore > 0. --- web/includes/FilterTerm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/FilterTerm.php b/web/includes/FilterTerm.php index 2e90d969e..bd09b5647 100644 --- a/web/includes/FilterTerm.php +++ b/web/includes/FilterTerm.php @@ -77,7 +77,7 @@ class FilterTerm { switch ( $this->attr ) { case 'AlarmedZoneId': - $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.')'; + $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND ZoneId='.$value.' AND Score > 0)'; break; case 'ExistsInFileSystem': $value = ''; From d0cbb7b478bbe91844156af6ee2e2b06d447b3ba Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Jul 2022 10:27:18 -0400 Subject: [PATCH 35/56] Sync up with c++ shm alignment to fix same size of 32bit --- scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 70960d36e..a461a0e4f 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -165,6 +165,8 @@ our %mem_data = ( recording => { type=>'uint8', seq=>$mem_seq++ }, signal => { type=>'uint8', seq=>$mem_seq++ }, format => { type=>'uint8', seq=>$mem_seq++ }, + reserved1 => { type=>'uint8', seq=>$mem_seq++ }, + reserved2 => { type=>'uint8', seq=>$mem_seq++ }, imagesize => { type=>'uint32', seq=>$mem_seq++ }, last_frame_score => { type=>'uint32', seq=>$mem_seq++ }, audio_frequency => { type=>'uint32', seq=>$mem_seq++ }, From 12c1011ff9f560fd38140e752ff72d2b3d88fc6e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Jul 2022 10:44:28 -0400 Subject: [PATCH 36/56] Use parseInt to turn '0' into 0 --- web/skins/classic/views/js/monitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index 7a326f35c..b3ff064ff 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -315,7 +315,7 @@ function initPage() { } }); - if ( ZM_OPT_USE_GEOLOCATION ) { + if ( parseInt(ZM_OPT_USE_GEOLOCATION) ) { if ( window.L ) { const form = document.getElementById('contentForm'); const latitude = form.elements['newMonitor[Latitude]'].value; From 4d3108970711d29b7fe59ccea46ca0fb7600ff81 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Jul 2022 12:42:13 -0400 Subject: [PATCH 37/56] Only decremember warned_count if > 0. Fixes too much logging. Add warning about keyframe interval being > max image buffer setting. --- src/zm_packetqueue.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 21ab89349..082376995 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -130,7 +130,7 @@ bool PacketQueue::queuePacket(std::shared_ptr add_packet) { ZMLockedPacket lp(zm_packet); if (!lp.trylock()) { - if (warned_count <2) { + if (warned_count < 2) { warned_count++; // Can't delete a locked packet, but can delete one after it. Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up."); @@ -166,7 +166,7 @@ bool PacketQueue::queuePacket(std::shared_ptr add_packet) { if (zm_packet->packet.stream_index == video_stream_id) break; } // end while - } else { + } else if (warned_count > 0) { warned_count--; } // end if not able catch up } // end lock scope @@ -262,6 +262,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { return; } + int keyframe_interval = 1; ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); if (!lp->trylock()) { Debug(4, "Failed getting lock on first packet"); @@ -269,7 +270,6 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { return; } // end if first packet not locked - int keyframe_interval = 1; int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking ++it; delete lp; @@ -313,9 +313,13 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { ++it; } // end while - Debug(1, "Resulting it pointing at latest packet? %d, next front points to begin? %d", + if (keyframe_interval > pre_event_video_packet_count) { + Warning("Max Image Buffer setting is too low! Needs to be great than keyframe interval. Keyframe interval is at least %d", keyframe_interval); + } + Debug(1, "Resulting it pointing at latest packet? %d, next front points to begin? %d, Keyframe interval %d", ( *it == add_packet ), - ( next_front == pktQueue.begin() ) + ( next_front == pktQueue.begin() ), + keyframe_interval ); if (next_front != pktQueue.begin()) { while (pktQueue.begin() != next_front) { From c937169a6b864f0c59003c128d40db8f83663b09 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Jul 2022 13:50:40 -0400 Subject: [PATCH 38/56] Fix logic inversion on test_pre_sql_conditions --- web/includes/Filter.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 2351d04d9..417f5f232 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -315,14 +315,12 @@ class Filter extends ZM_Object { return true; } # end if pre_sql_conditions - $failed = false; foreach ( $this->pre_sql_conditions() as $term ) { if ( !$term->test() ) { - $failed = true; - break; + return false; } } - return $failed; + return true; } public function test_post_sql_conditions($event) { From e1581b6be64239d851d857eaa567842ab0bc11c6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Jul 2022 13:59:01 -0400 Subject: [PATCH 39/56] db limits should not be affected by pre-conditions, so include the limit regardless of pre-sql-conditions. --- web/ajax/events.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/ajax/events.php b/web/ajax/events.php index 3ee83eb1f..1bb4581e7 100644 --- a/web/ajax/events.php +++ b/web/ajax/events.php @@ -154,8 +154,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim 'updated' => $dateTimeFormatter->format(time()) ); - $failed = !$filter->test_pre_sql_conditions(); - if ($failed) { + if (!$filter->test_pre_sql_conditions()) { ZM\Debug('Pre conditions failed, not doing sql'); return $data; } @@ -193,7 +192,7 @@ 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())) { + if ($filter->limit() and !count($filter->post_sql_conditions())) { $sql .= ' LIMIT '.$filter->limit(); } From 5776a766e1da14e6f56ee9b6d123711a0d7797e7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 Jul 2022 13:59:21 -0400 Subject: [PATCH 40/56] fixup logic inversion on test_post_sql_conditions. condense code. --- web/includes/Filter.php | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 417f5f232..d282c90fa 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -311,31 +311,21 @@ class Filter extends ZM_Object { } public function test_pre_sql_conditions() { - if ( !count($this->pre_sql_conditions()) ) { - return true; - } # end if pre_sql_conditions - - foreach ( $this->pre_sql_conditions() as $term ) { - if ( !$term->test() ) { - return false; + if (count($this->pre_sql_conditions())) { + foreach ($this->pre_sql_conditions() as $term) { + if (!$term->test()) return false; } - } + } # end if pre_sql_conditions return true; } public function test_post_sql_conditions($event) { - if ( !count($this->post_sql_conditions()) ) { - return true; - } # end if pre_sql_conditions - - $failed = true; - foreach ( $this->post_sql_conditions() as $term ) { - if ( !$term->test($event) ) { - $failed = false; - break; + if (count($this->post_sql_conditions())) { + foreach ($this->post_sql_conditions() as $term) { + if (!$term->test($event)) return false; } - } - return $failed; + } # end if pre_sql_conditions + return true; } function tree() { From 110cc436a304637bd06a0236500b708ef716c541 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Wed, 27 Jul 2022 08:35:35 -0400 Subject: [PATCH 41/56] Switch from av_init_packet() to av_packet_alloc() Remove all uses of deprecated av_init_packet() and switch any stack based AVPackets to unique_ptrs allocated with av_packet_alloc(). Ensure that all code paths call av_packet_unref() after use to reset before next usage. --- src/zm_curl_camera.cpp | 4 +- src/zm_ffmpeg.h | 46 ++++++++------ src/zm_ffmpeg_camera.cpp | 27 +++++---- src/zm_ffmpeg_camera.h | 2 +- src/zm_ffmpeg_input.cpp | 26 ++++---- src/zm_ffmpeg_output.cpp | 33 +++++----- src/zm_fifo.cpp | 10 +-- src/zm_libvlc_camera.cpp | 2 +- src/zm_libvnc_camera.cpp | 2 +- src/zm_local_camera.cpp | 2 +- src/zm_monitor.cpp | 12 ++-- src/zm_mpeg.cpp | 18 ++---- src/zm_mpeg.h | 3 +- src/zm_packet.cpp | 22 +++---- src/zm_packet.h | 4 +- src/zm_packetqueue.cpp | 46 +++++++------- src/zm_remote_camera_http.cpp | 2 +- src/zm_remote_camera_rtsp.cpp | 2 +- src/zm_rtsp_server_device_source.cpp | 2 +- src/zm_videostore.cpp | 91 ++++++++++++---------------- src/zm_videostore.h | 2 +- 21 files changed, 174 insertions(+), 184 deletions(-) diff --git a/src/zm_curl_camera.cpp b/src/zm_curl_camera.cpp index 27a213ed1..ddd7e52cb 100644 --- a/src/zm_curl_camera.cpp +++ b/src/zm_curl_camera.cpp @@ -323,7 +323,7 @@ int cURLCamera::Capture(std::shared_ptr &zm_packet) { } zm_packet->keyframe = 1; zm_packet->codec_type = AVMEDIA_TYPE_VIDEO; - zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->packet->stream_index = mVideoStreamId; zm_packet->stream = mVideoStream; zm_packet->image->DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); frameComplete = true; @@ -351,7 +351,7 @@ int cURLCamera::Capture(std::shared_ptr &zm_packet) { } zm_packet->keyframe = 1; zm_packet->codec_type = AVMEDIA_TYPE_VIDEO; - zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->packet->stream_index = mVideoStreamId; zm_packet->stream = mVideoStream; zm_packet->image->DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); single_offsets.pop_front(); diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 18090098f..94e0c895e 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -23,6 +23,8 @@ #include "zm_config.h" #include "zm_define.h" +#include + extern "C" { #include @@ -177,34 +179,34 @@ void zm_dump_codecpar(const AVCodecParameters *par); Debug(2, "%s: pts: %" PRId64 ", dts: %" PRId64 \ ", size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64 ", duration: %" AV_PACKET_DURATION_FMT, \ text,\ - pkt.pts,\ - pkt.dts,\ - pkt.size,\ - pkt.stream_index,\ - pkt.flags,\ - pkt.flags & AV_PKT_FLAG_KEY,\ - pkt.pos,\ - pkt.duration) + pkt->pts,\ + pkt->dts,\ + pkt->size,\ + pkt->stream_index,\ + pkt->flags,\ + pkt->flags & AV_PKT_FLAG_KEY,\ + pkt->pos,\ + pkt->duration) # define ZM_DUMP_STREAM_PACKET(stream, pkt, text) \ if (logDebugging()) { \ - double pts_time = static_cast(av_rescale_q(pkt.pts, stream->time_base, AV_TIME_BASE_Q)) / AV_TIME_BASE; \ + 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 " * %u/%u=%f, dts: %" PRId64 \ ", size: %d, stream_index: %d, %s flags: %04x, keyframe(%d) pos: %" PRId64", duration: %" AV_PACKET_DURATION_FMT, \ text, \ - pkt.pts, \ + pkt->pts, \ stream->time_base.num, \ stream->time_base.den, \ pts_time, \ - pkt.dts, \ - pkt.size, \ - pkt.stream_index, \ + pkt->dts, \ + pkt->size, \ + pkt->stream_index, \ av_get_media_type_string(CODEC_TYPE(stream)), \ - pkt.flags, \ - pkt.flags & AV_PKT_FLAG_KEY, \ - pkt.pos, \ - pkt.duration); \ + pkt->flags, \ + pkt->flags & AV_PKT_FLAG_KEY, \ + pkt->pos, \ + pkt->duration); \ } #else @@ -238,4 +240,14 @@ int zm_resample_get_delay(SwrContext *resample_ctx, int time_base); int zm_add_samples_to_fifo(AVAudioFifo *fifo, AVFrame *frame); int zm_get_samples_from_fifo(AVAudioFifo *fifo, AVFrame *frame); +struct zm_free_av_packet +{ + void operator()(AVPacket *pkt) const + { + av_packet_free(&pkt); + } +}; + +using av_packet_ptr = std::unique_ptr; + #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 693fa13a4..6e28e1449 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -152,6 +152,7 @@ FfmpegCamera::FfmpegCamera( Panic("Unexpected colours: %d", colours); } + packet = av_packet_ptr{av_packet_alloc()}; } // FfmpegCamera::FfmpegCamera FfmpegCamera::~FfmpegCamera() { @@ -204,7 +205,7 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { ); } - if ((ret = av_read_frame(formatContextPtr, &packet)) < 0) { + if ((ret = av_read_frame(formatContextPtr, packet.get())) < 0) { if ( // Check if EOF. (ret == AVERROR_EOF || (formatContextPtr->pb && formatContextPtr->pb->eof_reached)) || @@ -212,37 +213,37 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { (ret == -110) ) { Info("Unable to read packet from stream %d: error %d \"%s\".", - packet.stream_index, ret, av_make_error_string(ret).c_str()); + packet->stream_index, ret, av_make_error_string(ret).c_str()); } else { Error("Unable to read packet from stream %d: error %d \"%s\".", - packet.stream_index, ret, av_make_error_string(ret).c_str()); + packet->stream_index, ret, av_make_error_string(ret).c_str()); } return -1; } - AVStream *stream = formatContextPtr->streams[packet.stream_index]; + AVStream *stream = formatContextPtr->streams[packet->stream_index]; ZM_DUMP_STREAM_PACKET(stream, packet, "ffmpeg_camera in"); zm_packet->codec_type = stream->codecpar->codec_type; - bytes += packet.size; - zm_packet->set_packet(&packet); + bytes += packet->size; + zm_packet->set_packet(packet.get()); zm_packet->stream = stream; - zm_packet->pts = av_rescale_q(packet.pts, stream->time_base, AV_TIME_BASE_Q); - if (packet.pts != AV_NOPTS_VALUE) { + zm_packet->pts = av_rescale_q(packet->pts, stream->time_base, AV_TIME_BASE_Q); + if (packet->pts != AV_NOPTS_VALUE) { if (stream == mVideoStream) { if (mFirstVideoPTS == AV_NOPTS_VALUE) - mFirstVideoPTS = packet.pts; + mFirstVideoPTS = packet->pts; - mLastVideoPTS = packet.pts - mFirstVideoPTS; + mLastVideoPTS = packet->pts - mFirstVideoPTS; } else { if (mFirstAudioPTS == AV_NOPTS_VALUE) - mFirstAudioPTS = packet.pts; + mFirstAudioPTS = packet->pts; - mLastAudioPTS = packet.pts - mFirstAudioPTS; + mLastAudioPTS = packet->pts - mFirstAudioPTS; } } - zm_av_packet_unref(&packet); + zm_av_packet_unref(packet.get()); return 1; } // FfmpegCamera::Capture diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index 42b716b03..289695b36 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -58,7 +58,7 @@ class FfmpegCamera : public Camera { // Used to store the incoming packet, it will get copied when queued. // We only ever need one at a time, so instead of constantly allocating // and freeing this structure, we will just make it a member of the object. - AVPacket packet; + av_packet_ptr packet; int OpenFfmpeg(); int Close() override; diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 16da59f03..437fe605f 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -137,11 +137,10 @@ int FFmpeg_Input::Close( ) { AVFrame *FFmpeg_Input::get_frame(int stream_id) { int frameComplete = false; - AVPacket packet; - av_init_packet(&packet); + av_packet_ptr packet{av_packet_alloc()}; while ( !frameComplete ) { - int ret = av_read_frame(input_format_context, &packet); + int ret = av_read_frame(input_format_context, packet.get()); if ( ret < 0 ) { if ( // Check if EOF. @@ -153,17 +152,18 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { return nullptr; } Error("Unable to read packet from stream %d: error %d \"%s\".", - packet.stream_index, ret, av_make_error_string(ret).c_str()); + packet->stream_index, ret, av_make_error_string(ret).c_str()); return nullptr; } - ZM_DUMP_STREAM_PACKET(input_format_context->streams[packet.stream_index], packet, "Received packet"); + ZM_DUMP_STREAM_PACKET(input_format_context->streams[packet->stream_index], packet, "Received packet"); - if ( (stream_id >= 0) && (packet.stream_index != stream_id) ) { - Debug(1,"Packet is not for our stream (%d)", packet.stream_index ); + if ( (stream_id >= 0) && (packet->stream_index != stream_id) ) { + Debug(1,"Packet is not for our stream (%d)", packet->stream_index ); + zm_av_packet_unref(packet.get()); continue; } - AVCodecContext *context = streams[packet.stream_index].context; + AVCodecContext *context = streams[packet->stream_index].context; if ( frame ) { av_frame_free(&frame); @@ -171,15 +171,15 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { } else { frame = zm_av_frame_alloc(); } - ret = zm_send_packet_receive_frame(context, frame, packet); + ret = zm_send_packet_receive_frame(context, frame, *packet); if ( ret < 0 ) { Error("Unable to decode frame at frame %d: %d %s, continuing", - streams[packet.stream_index].frame_count, ret, av_make_error_string(ret).c_str()); - zm_av_packet_unref(&packet); + streams[packet->stream_index].frame_count, ret, av_make_error_string(ret).c_str()); + zm_av_packet_unref(packet.get()); av_frame_free(&frame); continue; } else { - if ( is_video_stream(input_format_context->streams[packet.stream_index]) ) { + if ( is_video_stream(input_format_context->streams[packet->stream_index]) ) { zm_dump_video_frame(frame, "resulting video frame"); } else { zm_dump_frame(frame, "resulting frame"); @@ -194,7 +194,7 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { input_format_context->streams[stream_id]->time_base ); - zm_av_packet_unref(&packet); + zm_av_packet_unref(packet.get()); } // end while !frameComplete return frame; } // end AVFrame *FFmpeg_Input::get_frame diff --git a/src/zm_ffmpeg_output.cpp b/src/zm_ffmpeg_output.cpp index ec677f265..548822add 100644 --- a/src/zm_ffmpeg_output.cpp +++ b/src/zm_ffmpeg_output.cpp @@ -84,13 +84,12 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { Debug(1, "Getting frame from stream %d", stream_id ); int frameComplete = false; - AVPacket packet; - av_init_packet( &packet ); + av_packet_ptr packet{av_packet_alloc()}; AVFrame *frame = zm_av_frame_alloc(); char errbuf[AV_ERROR_MAX_STRING_SIZE]; while ( !frameComplete ) { - int ret = av_read_frame( input_format_context, &packet ); + int ret = av_read_frame( input_format_context, packet.get() ); if ( ret < 0 ) { av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); if ( @@ -102,20 +101,20 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { Info( "av_read_frame returned %s.", errbuf ); return NULL; } - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf ); + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet->stream_index, ret, errbuf ); return NULL; } - if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { - Debug(1,"Packet is for our stream (%d)", packet.stream_index ); + if ( (stream_id < 0 ) || ( packet->stream_index == stream_id ) ) { + Debug(1,"Packet is for our stream (%d)", packet->stream_index ); - AVCodecContext *context = streams[packet.stream_index].context; + AVCodecContext *context = streams[packet->stream_index].context; - ret = avcodec_send_packet( context, &packet ); + ret = avcodec_send_packet( context, packet.get() ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); + Error( "Unable to send packet at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); + zm_av_packet_unref( packet.get() ); continue; } else { Debug(1, "Success getting a packet"); @@ -126,15 +125,15 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { ret = avcodec_receive_frame( context, hwFrame ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); + Error( "Unable to receive frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); + zm_av_packet_unref( packet.get() ); continue; } ret = av_hwframe_transfer_data(frame, hwFrame, 0); if (ret < 0) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); + Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); + zm_av_packet_unref( packet.get() ); continue; } } else { @@ -143,8 +142,8 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { ret = avcodec_receive_frame( context, frame ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); - Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); - zm_av_packet_unref( &packet ); + Error( "Unable to send packet at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); + zm_av_packet_unref( packet.get() ); continue; } @@ -155,7 +154,7 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { frameComplete = 1; } // end if it's the right stream - zm_av_packet_unref( &packet ); + zm_av_packet_unref( packet.get() ); } // end while ! frameComplete return frame; diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index c7a4a608f..acdcbad10 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -99,9 +99,9 @@ bool Fifo::close() { bool Fifo::writePacket(const ZMPacket &packet) { if (!(outfile or open())) return false; - Debug(2, "Writing header ZM %u %" PRId64, packet.packet.size, packet.pts); + Debug(2, "Writing header ZM %u %" PRId64, packet.packet->size, packet.pts); // Going to write a brief header - if (fprintf(outfile, "ZM %u %" PRId64 "\n", packet.packet.size, packet.pts) < 0) { + if (fprintf(outfile, "ZM %u %" PRId64 "\n", packet.packet->size, packet.pts) < 0) { if (errno != EAGAIN) { Error("Problem during writing: %s", strerror(errno)); } else { @@ -110,7 +110,7 @@ bool Fifo::writePacket(const ZMPacket &packet) { return false; } - if (fwrite(packet.packet.data, packet.packet.size, 1, outfile) != 1) { + if (fwrite(packet.packet->data, packet.packet->size, 1, outfile) != 1) { Debug(1, "Unable to write to '%s': %s", path.c_str(), strerror(errno)); return false; } @@ -129,8 +129,8 @@ bool Fifo::writePacket(const std::string &filename, const ZMPacket &packet) { return false; } - Debug(4, "Writing packet of size %d pts %" PRId64, packet.packet.size, packet.pts); - if (fwrite(packet.packet.data, packet.packet.size, 1, outfile) != 1) { + Debug(4, "Writing packet of size %d pts %" PRId64, packet.packet->size, packet.pts); + if (fwrite(packet.packet->data, packet.packet->size, 1, outfile) != 1) { Debug(1, "Unable to write to '%s': %s", filename.c_str(), strerror(errno)); fclose(outfile); return false; diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index 95284074f..c1278df12 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -288,7 +288,7 @@ int LibvlcCamera::Capture(std::shared_ptr &zm_packet) { mLibvlcData.mutex.lock(); zm_packet->image->Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); - zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->packet->stream_index = mVideoStreamId; zm_packet->stream = mVideoStream; mLibvlcData.mutex.unlock(); diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp index 3ff7804b6..9e63183bc 100644 --- a/src/zm_libvnc_camera.cpp +++ b/src/zm_libvnc_camera.cpp @@ -214,7 +214,7 @@ int VncCamera::Capture(std::shared_ptr &zm_packet) { } zm_packet->keyframe = 1; zm_packet->codec_type = AVMEDIA_TYPE_VIDEO; - zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->packet->stream_index = mVideoStreamId; zm_packet->stream = mVideoStream; uint8_t *directbuffer = zm_packet->image->WriteBuffer(width, height, colours, subpixelorder); diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index 2bcfac905..1b7f6f806 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -1376,7 +1376,7 @@ int LocalCamera::Capture(std::shared_ptr &zm_packet) { zm_packet->image->Assign(width, height, colours, subpixelorder, buffer, imagesize); } // end if doing conversion or not - zm_packet->packet.stream_index = mVideoStreamId; + zm_packet->packet->stream_index = mVideoStreamId; zm_packet->stream = mVideoStream; zm_packet->codec_type = AVMEDIA_TYPE_VIDEO; zm_packet->keyframe = 1; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index b7828426b..39188e841 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2469,10 +2469,10 @@ int Monitor::Capture() { shared_data->last_write_time = std::chrono::system_clock::to_time_t(packet->timestamp); } Debug(2, "Have packet stream_index:%d ?= videostream_id: %d q.vpktcount %d event? %d image_count %d", - packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ), image_count); + packet->packet->stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ), image_count); if (packet->codec_type == AVMEDIA_TYPE_VIDEO) { - packet->packet.stream_index = video_stream_id; // Convert to packetQueue's index + packet->packet->stream_index = video_stream_id; // Convert to packetQueue's index if (video_fifo) { if (packet->keyframe) { // avcodec strips out important nals that describe the stream and @@ -2493,7 +2493,7 @@ int Monitor::Capture() { if (record_audio and (packetqueue.packet_count(video_stream_id) or event)) { packet->image_index=-1; Debug(2, "Queueing audio packet"); - packet->packet.stream_index = audio_stream_id; // Convert to packetQueue's index + packet->packet->stream_index = audio_stream_id; // Convert to packetQueue's index packetqueue.queuePacket(packet); } else { Debug(4, "Not Queueing audio packet"); @@ -2601,7 +2601,7 @@ bool Monitor::Decode() { return true; // Don't need decode } - if ((!packet->image) and packet->packet.size and !packet->in_frame) { + if ((!packet->image) and packet->packet->size and !packet->in_frame) { if ((decoding == DECODING_ALWAYS) or ((decoding == DECODING_ONDEMAND) and this->hasViewers() ) @@ -2632,7 +2632,7 @@ bool Monitor::Decode() { } // end if have convert_context } // end if need transfer to image } else { - Debug(1, "No packet.size(%d) or packet->in_frame(%p). Not decoding", packet->packet.size, packet->in_frame); + Debug(1, "No packet.size(%d) or packet->in_frame(%p). Not decoding", packet->packet->size, packet->in_frame); } } else { Debug(1, "Not Decoding ? %s", Decoding_Strings[decoding].c_str()); @@ -3268,7 +3268,7 @@ void Monitor::get_ref_image() { std::shared_ptr snap = snap_lock->packet_; Debug(1, "get_ref_image: packet.stream %d ?= video_stream %d, packet image id %d packet image %p", - snap->packet.stream_index, video_stream_id, snap->image_index, snap->image ); + snap->packet->stream_index, video_stream_id, snap->image_index, snap->image ); // Might not have been decoded yet FIXME if (snap->image) { ref_image.Assign(width, height, camera->Colours(), diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index b962883a4..9ca8a6040 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -338,10 +338,8 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi SetParameters( ); // Allocate buffered packets. - packet_buffers = new AVPacket*[2]; - packet_buffers[0] = new AVPacket(); - packet_buffers[1] = new AVPacket(); - packet_index = 0; + for (auto &pkt : packet_buffers) + pkt = av_packet_ptr{av_packet_alloc()}; // Initialize mutex used by streaming thread. if ( pthread_mutex_init( buffer_copy_lock, nullptr ) != 0 ) { @@ -375,12 +373,6 @@ VideoStream::~VideoStream( ) { delete buffer_copy_lock; } - if (packet_buffers) { - delete packet_buffers[0]; - delete packet_buffers[1]; - delete[] packet_buffers; - } - /* close each codec */ if ( ost ) { avcodec_close( codec_context ); @@ -474,8 +466,8 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, } AVFrame *opicture_ptr = opicture; - AVPacket *pkt = packet_buffers[packet_index]; - av_init_packet( pkt ); + AVPacket *pkt = packet_buffers[packet_index].get(); + if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) { pkt->flags |= AV_PKT_FLAG_KEY; @@ -550,7 +542,7 @@ void *VideoStream::StreamingThreadCallback(void *ctx) { // Since this lag is not constant the client may skip frames. // Get the last rendered packet. - AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index]; + AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index].get(); if (packet->size) { videoStream->SendPacket(packet); } diff --git a/src/zm_mpeg.h b/src/zm_mpeg.h index 231821f2d..8d9169c19 100644 --- a/src/zm_mpeg.h +++ b/src/zm_mpeg.h @@ -22,6 +22,7 @@ #include "zm_ffmpeg.h" #include +#include class VideoStream { protected: @@ -59,7 +60,7 @@ protected: pthread_mutex_t *buffer_copy_lock; int buffer_copy_size; int buffer_copy_used; - AVPacket** packet_buffers; + std::array packet_buffers; int packet_index; int SendPacket(AVPacket *packet); static void* StreamingThreadCallback(void *ctx); diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 0ffe262ca..9e10afbaf 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -40,8 +40,7 @@ ZMPacket::ZMPacket() : pts(0), decoded(false) { - av_init_packet(&packet); - packet.size = 0; // So we can detect whether it has been filled. + packet = av_packet_ptr{av_packet_alloc()}; } ZMPacket::ZMPacket(Image *i, SystemTimePoint tv) : @@ -60,8 +59,7 @@ ZMPacket::ZMPacket(Image *i, SystemTimePoint tv) : pts(0), decoded(false) { - av_init_packet(&packet); - packet.size = 0; // So we can detect whether it has been filled. + packet = av_packet_ptr{av_packet_alloc()}; } ZMPacket::ZMPacket(ZMPacket &p) : @@ -80,16 +78,14 @@ ZMPacket::ZMPacket(ZMPacket &p) : pts(0), decoded(false) { - av_init_packet(&packet); - packet.size = 0; - packet.data = nullptr; - if (zm_av_packet_ref(&packet, &p.packet) < 0) { + packet = av_packet_ptr{av_packet_alloc()}; + + if (zm_av_packet_ref(packet.get(), p.packet.get()) < 0) { Error("error refing packet"); } } ZMPacket::~ZMPacket() { - zm_av_packet_unref(&packet); if (in_frame) av_frame_free(&in_frame); if (out_frame) av_frame_free(&out_frame); if (buffer) av_freep(&buffer); @@ -98,7 +94,7 @@ ZMPacket::~ZMPacket() { } ssize_t ZMPacket::ram() { - return packet.size + + return packet->size + (in_frame ? in_frame->linesize[0] * in_frame->height : 0) + (out_frame ? out_frame->linesize[0] * out_frame->height : 0) + (image ? image->Size() : 0) + @@ -122,7 +118,7 @@ int ZMPacket::decode(AVCodecContext *ctx) { // packets are always stored in AV_TIME_BASE_Q so need to convert to codec time base //av_packet_rescale_ts(&packet, AV_TIME_BASE_Q, ctx->time_base); - int ret = zm_send_packet_receive_frame(ctx, in_frame, packet); + int ret = zm_send_packet_receive_frame(ctx, in_frame, *packet); if (ret < 0) { if (AVERROR(EAGAIN) != ret) { Warning("Unable to receive frame : code %d %s.", @@ -249,13 +245,13 @@ Image *ZMPacket::set_image(Image *i) { } AVPacket *ZMPacket::set_packet(AVPacket *p) { - if (zm_av_packet_ref(&packet, p) < 0) { + if (zm_av_packet_ref(packet.get(), p) < 0) { Error("error refing packet"); } timestamp = std::chrono::system_clock::now(); keyframe = p->flags & AV_PKT_FLAG_KEY; - return &packet; + return packet.get(); } AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { diff --git a/src/zm_packet.h b/src/zm_packet.h index 07e7e7a45..b3703ceee 100644 --- a/src/zm_packet.h +++ b/src/zm_packet.h @@ -44,7 +44,7 @@ class ZMPacket { int keyframe; AVStream *stream; // Input stream - AVPacket packet; // Input packet, undecoded + av_packet_ptr packet; // Input packet, undecoded AVFrame *in_frame; // Input image, decoded Theoretically only filled if needed. AVFrame *out_frame; // output image, Only filled if needed. SystemTimePoint timestamp; @@ -61,7 +61,7 @@ class ZMPacket { std::string alarm_cause; public: - AVPacket *av_packet() { return &packet; } + AVPacket *av_packet() { return packet.get(); } AVPacket *set_packet(AVPacket *p) ; AVFrame *av_frame() { return out_frame; } Image *get_image(Image *i=nullptr); diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 082376995..5ea598793 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -99,13 +99,13 @@ bool PacketQueue::queuePacket(std::shared_ptr add_packet) { } } // end foreach iterator - packet_counts[add_packet->packet.stream_index] += 1; + packet_counts[add_packet->packet->stream_index] += 1; Debug(2, "packet counts for %d is %d", - add_packet->packet.stream_index, - packet_counts[add_packet->packet.stream_index]); + add_packet->packet->stream_index, + packet_counts[add_packet->packet->stream_index]); if ( - (add_packet->packet.stream_index == video_stream_id) + (add_packet->packet->stream_index == video_stream_id) and (max_video_packet_count > 0) and @@ -153,17 +153,17 @@ bool PacketQueue::queuePacket(std::shared_ptr add_packet) { } // end foreach iterator it = pktQueue.erase(it); - packet_counts[zm_packet->packet.stream_index] -= 1; + packet_counts[zm_packet->packet->stream_index] -= 1; Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%zu", - zm_packet->packet.stream_index, + zm_packet->packet->stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count, pktQueue.size()); - if (zm_packet->packet.stream_index == video_stream_id) + if (zm_packet->packet->stream_index == video_stream_id) break; } // end while } else if (warned_count > 0) { @@ -192,7 +192,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { if (deleting) return; if (keep_keyframes and ! ( - add_packet->packet.stream_index == video_stream_id + add_packet->packet->stream_index == video_stream_id and add_packet->keyframe and @@ -202,7 +202,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { ) ) { Debug(3, "stream index %d ?= video_stream_id %d, keyframe %d, keep_keyframes %d, counts %d > pre_event_count %d at begin %d", - add_packet->packet.stream_index, video_stream_id, add_packet->keyframe, keep_keyframes, packet_counts[video_stream_id], pre_event_video_packet_count, + add_packet->packet->stream_index, video_stream_id, add_packet->keyframe, keep_keyframes, packet_counts[video_stream_id], pre_event_video_packet_count, ( *(pktQueue.begin()) != add_packet ) ); return; @@ -215,7 +215,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { packetqueue_iterator it = pktQueue.end(); --it; while (*it != add_packet) { - if ((*it)->packet.stream_index == video_stream_id) + if ((*it)->packet->stream_index == video_stream_id) ++tail_count; --it; } @@ -239,10 +239,10 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { } pktQueue.pop_front(); - packet_counts[zm_packet->packet.stream_index] -= 1; + packet_counts[zm_packet->packet->stream_index] -= 1; Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%zu", - zm_packet->packet.stream_index, + zm_packet->packet->stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], @@ -295,7 +295,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { } #endif - if (zm_packet->packet.stream_index == video_stream_id) { + if (zm_packet->packet->stream_index == video_stream_id) { if (zm_packet->keyframe) { Debug(4, "Have a video keyframe so setting next front to it. Keyframe interval so far is %d", keyframe_interval); keyframe_interval = 1; @@ -331,14 +331,14 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%zu", - zm_packet->packet.stream_index, + zm_packet->packet->stream_index, zm_packet->image_index, zm_packet->keyframe, packet_counts[video_stream_id], pre_event_video_packet_count, pktQueue.size()); pktQueue.pop_front(); - packet_counts[zm_packet->packet.stream_index] -= 1; + packet_counts[zm_packet->packet->stream_index] -= 1; } } // end if have at least max_video_packet_count video packets remaining @@ -365,13 +365,13 @@ void PacketQueue::clear() { lp->lock(); Debug(1, "Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%zu", - packet->packet.stream_index, + packet->packet->stream_index, packet->image_index, packet->keyframe, packet_counts[video_stream_id], pre_event_video_packet_count, pktQueue.size()); - packet_counts[packet->packet.stream_index] -= 1; + packet_counts[packet->packet->stream_index] -= 1; pktQueue.pop_front(); delete lp; } @@ -529,7 +529,7 @@ bool PacketQueue::increment_it(packetqueue_iterator *it, int stream_id) { std::unique_lock lck(mutex); do { ++(*it); - } while ( (*it != pktQueue.end()) and ( (*(*it))->packet.stream_index != stream_id) ); + } while ( (*it != pktQueue.end()) and ( (*(*it))->packet->stream_index != stream_id) ); if ( *it != pktQueue.end() ) { Debug(2, "Incrementing %p, still not at end, so incrementing", it); @@ -558,10 +558,10 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( packet = *(*it); /* Debug(1, "Previous packet pre_event_count %d stream_index %d keyframe %d score %d", - pre_event_count, packet->packet.stream_index, packet->keyframe, packet->score); + pre_event_count, packet->packet->stream_index, packet->keyframe, packet->score); ZM_DUMP_PACKET(packet->packet, ""); */ - if (packet->packet.stream_index == video_stream_id) { + if (packet->packet->stream_index == video_stream_id) { pre_event_count --; if (!pre_event_count) break; @@ -589,7 +589,7 @@ packetqueue_iterator *PacketQueue::get_event_start_packet_it( while ((*it) != pktQueue.begin()) { packet = *(*it); //ZM_DUMP_PACKET(packet->packet, "No keyframe"); - if ((packet->packet.stream_index == video_stream_id) and packet->keyframe) + if ((packet->packet->stream_index == video_stream_id) and packet->keyframe) return it; // Success --(*it); } @@ -638,8 +638,8 @@ packetqueue_iterator * PacketQueue::get_video_it(bool wait) { return nullptr; } Debug(1, "Packet keyframe %d for stream %d, so returning the it to it", - zm_packet->keyframe, zm_packet->packet.stream_index); - if (zm_packet->keyframe and ( zm_packet->packet.stream_index == video_stream_id )) { + zm_packet->keyframe, zm_packet->packet->stream_index); + if (zm_packet->keyframe and ( zm_packet->packet->stream_index == video_stream_id )) { Debug(1, "Found a keyframe for stream %d, so returning the it to it", video_stream_id); return it; } diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 598afcd61..0d561073b 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -1099,7 +1099,7 @@ int RemoteCameraHttp::Capture(std::shared_ptr &packet) { Image *image = packet->image; packet->keyframe = 1; packet->codec_type = AVMEDIA_TYPE_VIDEO; - packet->packet.stream_index = mVideoStreamId; + packet->packet->stream_index = mVideoStreamId; packet->stream = mVideoStream; switch (format) { diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index b8a533fae..b6dec6dd4 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -216,7 +216,7 @@ int RemoteCameraRtsp::PreCapture() { int RemoteCameraRtsp::Capture(std::shared_ptr &zm_packet) { int frameComplete = false; - AVPacket *packet = &zm_packet->packet; + AVPacket *packet = zm_packet->packet.get(); while (!frameComplete) { buffer.clear(); diff --git a/src/zm_rtsp_server_device_source.cpp b/src/zm_rtsp_server_device_source.cpp index ba648b2f4..59df44d31 100644 --- a/src/zm_rtsp_server_device_source.cpp +++ b/src/zm_rtsp_server_device_source.cpp @@ -141,7 +141,7 @@ int ZoneMinderDeviceSource::getNextFrame() { m_packetqueue_it = m_packetqueue->get_video_it(true); } ZMPacket *zm_packet = m_packetqueue->get_packet(m_packetqueue_it); - while ( zm_packet and (zm_packet->packet.stream_index != m_stream->index) ) { + while ( zm_packet and (zm_packet->packet->stream_index != m_stream->index) ) { zm_packet->unlock(); // We want our stream to start at the same it as the video // but if this is an audio stream we need to increment past that first packet diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 082de8e25..c86400391 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -110,7 +110,7 @@ VideoStore::VideoStore( { FFMPEGInit(); swscale.init(); - opkt = new AVPacket; + opkt = av_packet_ptr{av_packet_alloc()}; } // VideoStore::VideoStore bool VideoStore::open() { @@ -575,21 +575,17 @@ bool VideoStore::open() { void VideoStore::flush_codecs() { // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. - AVPacket pkt; - // Without these we seg fault becuse av_init_packet doesn't init them - pkt.data = nullptr; - pkt.size = 0; - av_init_packet(&pkt); + av_packet_ptr pkt{av_packet_alloc()}; // I got crashes if the codec didn't do DELAY, so let's test for it. if (video_out_ctx && video_out_ctx->codec && (video_out_ctx->codec->capabilities & AV_CODEC_CAP_DELAY)) { // Put encoder into flushing mode - while ((zm_send_frame_receive_packet(video_out_ctx, nullptr, pkt)) > 0) { - av_packet_rescale_ts(&pkt, + while ((zm_send_frame_receive_packet(video_out_ctx, nullptr, *pkt)) > 0) { + av_packet_rescale_ts(pkt.get(), video_out_ctx->time_base, video_out_stream->time_base); - write_packet(&pkt, video_out_stream); - zm_av_packet_unref(&pkt); + write_packet(pkt.get(), video_out_stream); + zm_av_packet_unref(pkt.get()); } // while have buffered frames Debug(1, "Done writing buffered video."); } // end if have delay capability @@ -608,12 +604,12 @@ void VideoStore::flush_codecs() { if (zm_add_samples_to_fifo(fifo, out_frame)) { // Should probably set the frame size to what is reported FIXME if (zm_get_samples_from_fifo(fifo, out_frame)) { - if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt) > 0) { - av_packet_rescale_ts(&pkt, + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *pkt) > 0) { + av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); - write_packet(&pkt, audio_out_stream); - zm_av_packet_unref(&pkt); + write_packet(pkt.get(), audio_out_stream); + zm_av_packet_unref(pkt.get()); } } // end if data returned from fifo } @@ -629,14 +625,14 @@ void VideoStore::flush_codecs() { // SHould probably set the frame size to what is reported FIXME if (av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size)) { - if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, pkt)) { - pkt.stream_index = audio_out_stream->index; + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *pkt)) { + pkt->stream_index = audio_out_stream->index; - av_packet_rescale_ts(&pkt, + av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); - write_packet(&pkt, audio_out_stream); - zm_av_packet_unref(&pkt); + write_packet(pkt.get(), audio_out_stream); + zm_av_packet_unref(pkt.get()); } } // end if data returned from fifo } // end while still data in the fifo @@ -645,16 +641,16 @@ void VideoStore::flush_codecs() { avcodec_send_frame(audio_out_ctx, nullptr); while (true) { - if (0 >= zm_receive_packet(audio_out_ctx, pkt)) { + if (0 >= zm_receive_packet(audio_out_ctx, *pkt)) { Debug(1, "No more packets"); break; } ZM_DUMP_PACKET(pkt, "raw from encoder"); - av_packet_rescale_ts(&pkt, audio_out_ctx->time_base, audio_out_stream->time_base); + av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); ZM_DUMP_STREAM_PACKET(audio_out_stream, pkt, "writing flushed packet"); - write_packet(&pkt, audio_out_stream); - zm_av_packet_unref(&pkt); + write_packet(pkt.get(), audio_out_stream); + zm_av_packet_unref(pkt.get()); } // while have buffered frames } // end if audio_out_codec } // end flush_codecs @@ -977,13 +973,13 @@ int VideoStore::writePacket(const std::shared_ptr &zm_pkt) { auto &queue = reorder_queues[stream_index]; Debug(1, "Queue size for %d is %zu", stream_index, queue.size()); - AVPacket *av_pkt = &zm_pkt->packet; + AVPacket *av_pkt = zm_pkt->packet.get(); // queue the packet bool have_out_of_order = false; auto rit = queue.rbegin(); // Find the previous packet for the stream, and check dts while (rit != queue.rend()) { - AVPacket *p = &((*rit)->packet); + AVPacket *p = ((*rit)->packet).get(); if (p->dts <= av_pkt->dts) { Debug(1, "Found in order packet"); // packets are in order, everything is fine @@ -1052,7 +1048,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet ); } else if (!zm_packet->in_frame) { Debug(4, "Have no in_frame"); - if (zm_packet->packet.size and !zm_packet->decoded) { + if (zm_packet->packet->size and !zm_packet->decoded) { Debug(4, "Decoding"); if (!zm_packet->decode(video_in_ctx)) { Debug(2, "unable to decode yet."); @@ -1131,10 +1127,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet video_out_ctx->time_base.den); } - av_init_packet(opkt); - opkt->data = nullptr; - opkt->size = 0; - int ret = zm_send_frame_receive_packet(video_out_ctx, frame, *opkt); if (ret <= 0) { if (ret < 0) { @@ -1142,7 +1134,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } return ret; } - ZM_DUMP_PACKET((*opkt), "packet returned by codec"); + ZM_DUMP_PACKET(opkt, "packet returned by codec"); // Need to adjust pts/dts values from codec time to stream time if (opkt->pts != AV_NOPTS_VALUE) @@ -1193,11 +1185,10 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end if in_frmae opkt->duration = duration; } else { // Passthrough - AVPacket *ipkt = &zm_packet->packet; - ZM_DUMP_STREAM_PACKET(video_in_stream, (*ipkt), "Doing passthrough, just copy packet"); + AVPacket *ipkt = zm_packet->packet.get(); + 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); - av_packet_ref(opkt, ipkt); + av_packet_ref(opkt.get(), ipkt); if (ipkt->dts != AV_NOPTS_VALUE) { if (video_first_dts == AV_NOPTS_VALUE) { @@ -1213,19 +1204,19 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet opkt->pts = ipkt->pts - video_first_dts; } - av_packet_rescale_ts(opkt, video_in_stream->time_base, video_out_stream->time_base); + av_packet_rescale_ts(opkt.get(), video_in_stream->time_base, video_out_stream->time_base); } // end if codec matches - write_packet(opkt, video_out_stream); - zm_av_packet_unref(opkt); + write_packet(opkt.get(), video_out_stream); + zm_av_packet_unref(opkt.get()); if (hw_frame) av_frame_free(&hw_frame); return 1; } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet) { - AVPacket *ipkt = &zm_packet->packet; - ZM_DUMP_STREAM_PACKET(audio_in_stream, (*ipkt), "input packet"); + AVPacket *ipkt = zm_packet->packet.get(); + ZM_DUMP_STREAM_PACKET(audio_in_stream, ipkt, "input packet"); if (audio_first_dts == AV_NOPTS_VALUE) { audio_first_dts = ipkt->dts; @@ -1262,17 +1253,16 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet zm_dump_frame(out_frame, "Out frame after resample"); - av_init_packet(opkt); if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *opkt) <= 0) break; // Scale the PTS of the outgoing packet to be the correct time base - av_packet_rescale_ts(opkt, + av_packet_rescale_ts(opkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); - write_packet(opkt, audio_out_stream); - zm_av_packet_unref(opkt); + write_packet(opkt.get(), audio_out_stream); + zm_av_packet_unref(opkt.get()); if (zm_resample_get_delay(resample_ctx, out_frame->sample_rate) < out_frame->nb_samples) break; @@ -1280,7 +1270,6 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet input_frame = nullptr; } // end while there is data in the resampler } else { - av_init_packet(opkt); opkt->data = ipkt->data; opkt->size = ipkt->size; opkt->flags = ipkt->flags; @@ -1293,12 +1282,12 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet opkt->dts = ipkt->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); + ZM_DUMP_STREAM_PACKET(audio_in_stream, ipkt, "after pts adjustment"); + av_packet_rescale_ts(opkt.get(), 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.get(), audio_out_stream); - zm_av_packet_unref(opkt); + zm_av_packet_unref(opkt.get()); } // end if encoding or copying return 0; @@ -1337,7 +1326,7 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { pkt->pts = pkt->dts; } - ZM_DUMP_STREAM_PACKET(stream, (*pkt), "finished pkt"); + ZM_DUMP_STREAM_PACKET(stream, pkt, "finished pkt"); Debug(3, "next_dts for stream %d has become %" PRId64 " last_dts %" PRId64, stream->index, next_dts[stream->index], last_dts[stream->index]); diff --git a/src/zm_videostore.h b/src/zm_videostore.h index a5b0f20df..d66c82f76 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -57,7 +57,7 @@ class VideoStore { const AVCodec *audio_out_codec; AVCodecContext *audio_out_ctx; // Move this into the object so that we aren't constantly allocating/deallocating it on the stack - AVPacket *opkt; + av_packet_ptr opkt; AVFrame *video_in_frame; AVFrame *in_frame; From acbdf1854dd4db3732cdb04852c00bfc4beeaef6 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Thu, 28 Jul 2022 00:37:02 -0400 Subject: [PATCH 42/56] Add guard to handle resetting AVPacket after use. Also handle allocation failures. --- src/zm_ffmpeg.h | 34 ++++++++++++++++++++++++++++++++++ src/zm_ffmpeg_camera.cpp | 3 ++- src/zm_ffmpeg_input.cpp | 10 +++++++--- src/zm_ffmpeg_output.cpp | 13 +++++++------ src/zm_videostore.cpp | 18 +++++++++++++----- 5 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 94e0c895e..71c28c28b 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -250,4 +250,38 @@ struct zm_free_av_packet using av_packet_ptr = std::unique_ptr; +struct av_packet_guard +{ + av_packet_guard() : packet{nullptr} + { + } + explicit av_packet_guard(const av_packet_ptr& p) : packet{p.get()} + { + } + explicit av_packet_guard(AVPacket *p) : packet{p} + { + } + ~av_packet_guard() + { + if (packet) + av_packet_unref(packet); + } + + void acquire(const av_packet_ptr& p) + { + packet = p.get(); + } + void acquire(AVPacket *p) + { + packet = p; + } + void release() + { + packet = nullptr; + } + +private: + AVPacket *packet; +}; + #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 6e28e1449..b8e284951 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -221,6 +221,8 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { return -1; } + av_packet_guard pkt_guard{packet}; + AVStream *stream = formatContextPtr->streams[packet->stream_index]; ZM_DUMP_STREAM_PACKET(stream, packet, "ffmpeg_camera in"); @@ -243,7 +245,6 @@ int FfmpegCamera::Capture(std::shared_ptr &zm_packet) { mLastAudioPTS = packet->pts - mFirstAudioPTS; } } - zm_av_packet_unref(packet.get()); return 1; } // FfmpegCamera::Capture diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 437fe605f..ebde2fe21 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -139,6 +139,11 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { int frameComplete = false; av_packet_ptr packet{av_packet_alloc()}; + if (!packet) { + Error("Unable to allocate packet."); + return nullptr; + } + while ( !frameComplete ) { int ret = av_read_frame(input_format_context, packet.get()); if ( ret < 0 ) { @@ -157,9 +162,10 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { } ZM_DUMP_STREAM_PACKET(input_format_context->streams[packet->stream_index], packet, "Received packet"); + av_packet_guard pkt_guard{packet}; + if ( (stream_id >= 0) && (packet->stream_index != stream_id) ) { Debug(1,"Packet is not for our stream (%d)", packet->stream_index ); - zm_av_packet_unref(packet.get()); continue; } @@ -175,7 +181,6 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { if ( ret < 0 ) { Error("Unable to decode frame at frame %d: %d %s, continuing", streams[packet->stream_index].frame_count, ret, av_make_error_string(ret).c_str()); - zm_av_packet_unref(packet.get()); av_frame_free(&frame); continue; } else { @@ -194,7 +199,6 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { input_format_context->streams[stream_id]->time_base ); - zm_av_packet_unref(packet.get()); } // end while !frameComplete return frame; } // end AVFrame *FFmpeg_Input::get_frame diff --git a/src/zm_ffmpeg_output.cpp b/src/zm_ffmpeg_output.cpp index 548822add..dc9b31242 100644 --- a/src/zm_ffmpeg_output.cpp +++ b/src/zm_ffmpeg_output.cpp @@ -88,6 +88,11 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { AVFrame *frame = zm_av_frame_alloc(); char errbuf[AV_ERROR_MAX_STRING_SIZE]; + if (!packet) { + Error("Unable to allocate packet."); + return nullptr; + } + while ( !frameComplete ) { int ret = av_read_frame( input_format_context, packet.get() ); if ( ret < 0 ) { @@ -105,6 +110,8 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { return NULL; } + av_packet_guard pkt_guard{packet}; + if ( (stream_id < 0 ) || ( packet->stream_index == stream_id ) ) { Debug(1,"Packet is for our stream (%d)", packet->stream_index ); @@ -114,7 +121,6 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); Error( "Unable to send packet at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); - zm_av_packet_unref( packet.get() ); continue; } else { Debug(1, "Success getting a packet"); @@ -126,14 +132,12 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); Error( "Unable to receive frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); - zm_av_packet_unref( packet.get() ); continue; } ret = av_hwframe_transfer_data(frame, hwFrame, 0); if (ret < 0) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); - zm_av_packet_unref( packet.get() ); continue; } } else { @@ -143,7 +147,6 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); Error( "Unable to send packet at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); - zm_av_packet_unref( packet.get() ); continue; } @@ -154,8 +157,6 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { frameComplete = 1; } // end if it's the right stream - zm_av_packet_unref( packet.get() ); - } // end while ! frameComplete return frame; diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index c86400391..05731cdf3 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -577,15 +577,20 @@ void VideoStore::flush_codecs() { // whatever we get. Failures are not fatal. av_packet_ptr pkt{av_packet_alloc()}; + if (!pkt) { + Error("Unable to allocate packet."); + return; + } + // I got crashes if the codec didn't do DELAY, so let's test for it. if (video_out_ctx && video_out_ctx->codec && (video_out_ctx->codec->capabilities & AV_CODEC_CAP_DELAY)) { // Put encoder into flushing mode while ((zm_send_frame_receive_packet(video_out_ctx, nullptr, *pkt)) > 0) { + av_packet_guard pkt_guard{pkt}; av_packet_rescale_ts(pkt.get(), video_out_ctx->time_base, video_out_stream->time_base); write_packet(pkt.get(), video_out_stream); - zm_av_packet_unref(pkt.get()); } // while have buffered frames Debug(1, "Done writing buffered video."); } // end if have delay capability @@ -605,11 +610,11 @@ void VideoStore::flush_codecs() { // Should probably set the frame size to what is reported FIXME if (zm_get_samples_from_fifo(fifo, out_frame)) { if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *pkt) > 0) { + av_packet_guard pkt_guard{pkt}; av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); write_packet(pkt.get(), audio_out_stream); - zm_av_packet_unref(pkt.get()); } } // end if data returned from fifo } @@ -626,13 +631,13 @@ void VideoStore::flush_codecs() { // SHould probably set the frame size to what is reported FIXME if (av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size)) { if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *pkt)) { + av_packet_guard pkt_guard{pkt}; pkt->stream_index = audio_out_stream->index; av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); write_packet(pkt.get(), audio_out_stream); - zm_av_packet_unref(pkt.get()); } } // end if data returned from fifo } // end while still data in the fifo @@ -645,12 +650,12 @@ void VideoStore::flush_codecs() { Debug(1, "No more packets"); break; } + av_packet_guard pkt_guard{pkt}; ZM_DUMP_PACKET(pkt, "raw from encoder"); av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, audio_out_stream->time_base); ZM_DUMP_STREAM_PACKET(audio_out_stream, pkt, "writing flushed packet"); write_packet(pkt.get(), audio_out_stream); - zm_av_packet_unref(pkt.get()); } // while have buffered frames } // end if audio_out_codec } // end flush_codecs @@ -1015,6 +1020,8 @@ int VideoStore::writePacket(const std::shared_ptr &zm_pkt) { } int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet) { + av_packet_guard pkt_guard; + frame_count += 1; // if we have to transcode @@ -1134,6 +1141,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } return ret; } + pkt_guard.acquire(opkt); ZM_DUMP_PACKET(opkt, "packet returned by codec"); // Need to adjust pts/dts values from codec time to stream time @@ -1189,6 +1197,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet ZM_DUMP_STREAM_PACKET(video_in_stream, ipkt, "Doing passthrough, just copy packet"); // Just copy it because the codec is the same av_packet_ref(opkt.get(), ipkt); + pkt_guard.acquire(opkt); if (ipkt->dts != AV_NOPTS_VALUE) { if (video_first_dts == AV_NOPTS_VALUE) { @@ -1208,7 +1217,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end if codec matches write_packet(opkt.get(), video_out_stream); - zm_av_packet_unref(opkt.get()); if (hw_frame) av_frame_free(&hw_frame); return 1; From f4dd897ef4500917c34a6c851b7aacbd31fe9154 Mon Sep 17 00:00:00 2001 From: Doug Nazar Date: Sat, 30 Jul 2022 13:21:11 -0400 Subject: [PATCH 43/56] Convert AVFrame* to custom unique_ptr (av_frame_ptr). Switch all owning AVFrame* variables to av_frame_ptr for automatic cleanup. Use AVBufferRef to store frame data in AVFrame where appropriate so that it can be freed automatically with it's AVFrame. Handle allocation errors. --- src/zm_eventstream.cpp | 3 +- src/zm_ffmpeg.h | 10 ++++++ src/zm_ffmpeg_input.cpp | 30 +++++++---------- src/zm_ffmpeg_input.h | 3 +- src/zm_ffmpeg_output.cpp | 13 +++++--- src/zm_ffmpeg_output.h | 1 + src/zm_image.cpp | 9 +++-- src/zm_local_camera.cpp | 13 +++----- src/zm_local_camera.h | 4 +-- src/zm_monitor.cpp | 18 ++++------ src/zm_monitor.h | 2 +- src/zm_mpeg.cpp | 39 ++++++++-------------- src/zm_mpeg.h | 4 +-- src/zm_packet.cpp | 53 ++++++++++++----------------- src/zm_packet.h | 7 ++-- src/zm_swscale.cpp | 12 ++----- src/zm_swscale.h | 4 +-- src/zm_videostore.cpp | 72 +++++++++++++++------------------------- src/zm_videostore.h | 6 ++-- 19 files changed, 130 insertions(+), 173 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 95ae870ac..9dafae3cd 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -734,9 +734,8 @@ bool EventStream::sendFrame(Microseconds delta_us) { FrameData *frame_data = &event_data->frames[curr_frame_id-1]; AVFrame *frame = ffmpeg_input->get_frame(ffmpeg_input->get_video_stream_id(), FPSeconds(frame_data->offset).count()); - if ( frame ) { + if (frame) { image = new Image(frame); - //av_frame_free(&frame); } else { Error("Failed getting a frame."); return false; diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 71c28c28b..cbb661e59 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -284,4 +284,14 @@ private: AVPacket *packet; }; +struct zm_free_av_frame +{ + void operator()(AVFrame *frame) const + { + av_frame_free(&frame); + } +}; + +using av_frame_ptr = std::unique_ptr; + #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index ebde2fe21..ab2429adb 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -9,7 +9,6 @@ FFmpeg_Input::FFmpeg_Input() { audio_stream_id = -1; FFMPEGInit(); streams = nullptr; - frame = nullptr; last_seek_request = -1; } @@ -17,10 +16,6 @@ FFmpeg_Input::~FFmpeg_Input() { if ( input_format_context ) { Close(); } - if ( frame ) { - av_frame_free(&frame); - frame = nullptr; - } } // end ~FFmpeg_Input() /* Takes streams provided from elsewhere. They might not come from the same source @@ -171,23 +166,22 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { AVCodecContext *context = streams[packet->stream_index].context; - if ( frame ) { - av_frame_free(&frame); - frame = zm_av_frame_alloc(); - } else { - frame = zm_av_frame_alloc(); + frame = av_frame_ptr{zm_av_frame_alloc()}; + if (!frame) { + Error("Unable to allocate frame."); + return nullptr; } - ret = zm_send_packet_receive_frame(context, frame, *packet); + ret = zm_send_packet_receive_frame(context, frame.get(), *packet); if ( ret < 0 ) { Error("Unable to decode frame at frame %d: %d %s, continuing", streams[packet->stream_index].frame_count, ret, av_make_error_string(ret).c_str()); - av_frame_free(&frame); + frame = nullptr; continue; } else { if ( is_video_stream(input_format_context->streams[packet->stream_index]) ) { - zm_dump_video_frame(frame, "resulting video frame"); + zm_dump_video_frame(frame.get(), "resulting video frame"); } else { - zm_dump_frame(frame, "resulting frame"); + zm_dump_frame(frame.get(), "resulting frame"); } } @@ -200,7 +194,7 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { ); } // end while !frameComplete - return frame; + return frame.get(); } // end AVFrame *FFmpeg_Input::get_frame AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { @@ -253,7 +247,7 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { } } else if ( last_seek_request == seek_target ) { // paused case, sending keepalives - return frame; + return frame.get(); } // end if frame->pts > seek_target last_seek_request = seek_target; @@ -279,11 +273,11 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { while ( frame && (frame->pts < seek_target) ) { if ( !get_frame(stream_id) ) { Warning("Got no frame. returning nothing"); - return frame; + return frame.get(); } } zm_dump_frame(frame, "frame->pts <= seek_target, got"); - return frame; + return frame.get(); } return get_frame(stream_id); diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 3f2e5b7e9..d2143ee27 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -2,6 +2,7 @@ #define ZM_FFMPEG_INPUT_H #include "zm_define.h" +#include "zm_ffmpeg.h" extern "C" { #include @@ -49,7 +50,7 @@ class FFmpeg_Input { int video_stream_id; int audio_stream_id; AVFormatContext *input_format_context; - AVFrame *frame; + av_frame_ptr frame; int64_t last_seek_request; }; diff --git a/src/zm_ffmpeg_output.cpp b/src/zm_ffmpeg_output.cpp index dc9b31242..2d7a7edc9 100644 --- a/src/zm_ffmpeg_output.cpp +++ b/src/zm_ffmpeg_output.cpp @@ -85,11 +85,16 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { int frameComplete = false; av_packet_ptr packet{av_packet_alloc()}; - AVFrame *frame = zm_av_frame_alloc(); char errbuf[AV_ERROR_MAX_STRING_SIZE]; if (!packet) { - Error("Unable to allocate packet."); + Error("Unable to allocate packet.", ); + return nullptr; + } + + frame = av_frame_ptr{zm_av_frame_alloc()}; + if (!frame) { + Error("Unable to allocate frame."); return nullptr; } @@ -143,7 +148,7 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { } else { #endif Debug(1,"Getting a frame?"); - ret = avcodec_receive_frame( context, frame ); + ret = avcodec_receive_frame( context, frame.get() ); if ( ret < 0 ) { av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); Error( "Unable to send packet at frame %d: %s, continuing", streams[packet->stream_index].frame_count, errbuf ); @@ -158,6 +163,6 @@ AVFrame *FFmpeg_Output::get_frame( int stream_id ) { } // end if it's the right stream } // end while ! frameComplete - return frame; + return frame.get(); } // end AVFrame *FFmpeg_Output::get_frame diff --git a/src/zm_ffmpeg_output.h b/src/zm_ffmpeg_output.h index 9ab4ea403..abeffa942 100644 --- a/src/zm_ffmpeg_output.h +++ b/src/zm_ffmpeg_output.h @@ -35,6 +35,7 @@ class FFmpeg_Output { int video_stream_id; int audio_stream_id; AVFormatContext *input_format_context; + av_frame_ptr frame; }; #endif diff --git a/src/zm_image.cpp b/src/zm_image.cpp index a3c40023d..65aec46f8 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -289,7 +289,11 @@ bool Image::Assign(const AVFrame *frame) { // Desired format AVPixelFormat format = (AVPixelFormat)AVPixFormat(); - AVFrame *dest_frame = zm_av_frame_alloc(); + av_frame_ptr dest_frame{zm_av_frame_alloc()}; + if (!dest_frame) { + Error("Unable to allocate destination frame"); + return false; + } sws_convert_context = sws_getCachedContext( sws_convert_context, frame->width, frame->height, (AVPixelFormat)frame->format, @@ -301,8 +305,7 @@ bool Image::Assign(const AVFrame *frame) { Error("Unable to create conversion context"); return false; } - bool result = Assign(frame, sws_convert_context, dest_frame); - av_frame_free(&dest_frame); + bool result = Assign(frame, sws_convert_context, dest_frame.get()); update_function_pointers(); return result; } // end Image::Assign(const AVFrame *frame) diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index 1b7f6f806..5b31a89fc 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -219,7 +219,7 @@ int LocalCamera::vid_fd = -1; int LocalCamera::v4l_version = 0; LocalCamera::V4L2Data LocalCamera::v4l2_data; -AVFrame **LocalCamera::capturePictures = nullptr; +av_frame_ptr *LocalCamera::capturePictures; LocalCamera *LocalCamera::last_camera = nullptr; @@ -436,7 +436,7 @@ LocalCamera::LocalCamera( /* Initialize swscale stuff */ if (capture and (conversion_type == 1)) { - tmpPicture = av_frame_alloc(); + tmpPicture = av_frame_ptr{zm_av_frame_alloc()}; if (!tmpPicture) Fatal("Could not allocate temporary picture"); @@ -456,7 +456,6 @@ LocalCamera::LocalCamera( Fatal("Unable to initialise image scaling context"); } } else { - tmpPicture = nullptr; imgConversionContext = nullptr; } // end if capture and conversion_tye == swscale if (capture and device_prime) @@ -471,8 +470,6 @@ LocalCamera::~LocalCamera() { if (capture && (conversion_type == 1)) { sws_freeContext(imgConversionContext); imgConversionContext = nullptr; - - av_frame_free(&tmpPicture); } } // end LocalCamera::~LocalCamera @@ -658,7 +655,7 @@ void LocalCamera::Initialise() { channel_count, v4l_multi_buffer, v4l2_data.reqbufs.count); v4l2_data.buffers = new V4L2MappedBuffer[v4l2_data.reqbufs.count]; - capturePictures = new AVFrame *[v4l2_data.reqbufs.count]; + capturePictures = new av_frame_ptr[v4l2_data.reqbufs.count]; for (unsigned int i = 0; i < v4l2_data.reqbufs.count; i++) { struct v4l2_buffer vid_buf; @@ -681,7 +678,7 @@ void LocalCamera::Initialise() { Fatal("Can't map video buffer %u (%u bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno); - capturePictures[i] = av_frame_alloc(); + capturePictures[i] = av_frame_ptr{zm_av_frame_alloc()}; if (!capturePictures[i]) Fatal("Could not allocate picture"); @@ -738,7 +735,7 @@ void LocalCamera::Terminate() { Debug(3, "Unmapping video buffers"); for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { - av_frame_free(&capturePictures[i]); + capturePictures[i] = nullptr; if ( munmap(v4l2_data.buffers[i].start, v4l2_data.buffers[i].length) < 0 ) Error("Failed to munmap buffer %d: %s", i, strerror(errno)); diff --git a/src/zm_local_camera.h b/src/zm_local_camera.h index f702b816f..16ebe8b4b 100644 --- a/src/zm_local_camera.h +++ b/src/zm_local_camera.h @@ -73,11 +73,11 @@ protected: static V4L2Data v4l2_data; - static AVFrame **capturePictures; + static av_frame_ptr *capturePictures; _AVPIXELFORMAT imagePixFormat; _AVPIXELFORMAT capturePixFormat; struct SwsContext *imgConversionContext; - AVFrame *tmpPicture; + av_frame_ptr tmpPicture; static LocalCamera *last_camera; diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 39188e841..88968af0b 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -275,7 +275,6 @@ Monitor::Monitor() analysis_thread(nullptr), decoder_it(nullptr), decoder(nullptr), - dest_frame(nullptr), convert_context(nullptr), //zones(nullptr), privacy_bitmask(nullptr), @@ -1108,8 +1107,6 @@ Monitor::~Monitor() { if (video_fifo) delete video_fifo; if (audio_fifo) delete audio_fifo; Debug(1, "Don fifo"); - if (dest_frame) av_frame_free(&dest_frame); - Debug(1, "Don fifo"); if (convert_context) { Debug(1, "Don fifo"); sws_freeContext(convert_context); @@ -2023,7 +2020,7 @@ bool Monitor::Analyse() { ref_image.Blend(y_image, ( state==ALARM ? alarm_ref_blend_perc : ref_blend_perc )); } else if (snap->image) { Debug(1, "Blending full colour image because analysis_image = %d, in_frame=%p and format %d != %d, %d", - analysis_image, snap->in_frame, + analysis_image, snap->in_frame.get(), (snap->in_frame ? snap->in_frame->format : -1), AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P @@ -2255,8 +2252,7 @@ bool Monitor::Analyse() { } } // Free up the decoded frame as well, we won't be using it for anything at this time. - if (snap->out_frame) av_frame_free(&snap->out_frame); - if (snap->buffer) av_freep(&snap->buffer); + snap->out_frame = nullptr; delete packet_lock; } @@ -2620,19 +2616,19 @@ bool Monitor::Decode() { if (packet->in_frame and !packet->image) { packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder()); - if (convert_context || this->setupConvertContext(packet->in_frame, packet->image)) { - if (!packet->image->Assign(packet->in_frame, convert_context, dest_frame)) { + if (convert_context || this->setupConvertContext(packet->in_frame.get(), packet->image)) { + if (!packet->image->Assign(packet->in_frame.get(), convert_context, dest_frame.get())) { delete packet->image; packet->image = nullptr; } - av_frame_unref(dest_frame); + av_frame_unref(dest_frame.get()); } else { delete packet->image; packet->image = nullptr; } // end if have convert_context } // end if need transfer to image } else { - Debug(1, "No packet.size(%d) or packet->in_frame(%p). Not decoding", packet->packet->size, packet->in_frame); + Debug(1, "No packet.size(%d) or packet->in_frame(%p). Not decoding", packet->packet->size, packet->in_frame.get()); } } else { Debug(1, "Not Decoding ? %s", Decoding_Strings[decoding].c_str()); @@ -3134,7 +3130,7 @@ int Monitor::PrimeCapture() { } if (decoding != DECODING_NONE) { - if (!dest_frame) dest_frame = zm_av_frame_alloc(); + if (!dest_frame) dest_frame = av_frame_ptr{zm_av_frame_alloc()}; if (!decoder_it) decoder_it = packetqueue.get_video_it(false); if (!decoder) { Debug(1, "Creating decoder thread"); diff --git a/src/zm_monitor.h b/src/zm_monitor.h index e36729b51..d2acb7d60 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -523,7 +523,7 @@ protected: std::unique_ptr analysis_thread; packetqueue_iterator *decoder_it; std::unique_ptr decoder; - AVFrame *dest_frame; // Used by decoding thread doing colorspace conversions + av_frame_ptr dest_frame; // Used by decoding thread doing colorspace conversions SwsContext *convert_context; std::thread close_event_thread; diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index 9ca8a6040..0d8fbad7d 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -213,8 +213,8 @@ bool VideoStream::OpenStream( ) { Debug( 1, "Opened codec" ); /* allocate the encoded raw picture */ - opicture = zm_av_frame_alloc( ); - if ( !opicture ) { + opicture = av_frame_ptr{zm_av_frame_alloc()}; + if (!opicture) { Error("Could not allocate opicture"); return false; } @@ -224,36 +224,33 @@ bool VideoStream::OpenStream( ) { int size = av_image_get_buffer_size(codec_context->pix_fmt, codec_context->width, codec_context->height, 1); - uint8_t *opicture_buf = (uint8_t *)av_malloc(size); - if ( !opicture_buf ) { - av_frame_free( &opicture ); - Error( "Could not allocate opicture_buf" ); + opicture->buf[0] = av_buffer_alloc(size); + if (!opicture->buf[0]) { + Error( "Could not allocate opicture buffer" ); return false; } av_image_fill_arrays(opicture->data, opicture->linesize, - opicture_buf, codec_context->pix_fmt, codec_context->width, codec_context->height, 1); + opicture->buf[0]->data, codec_context->pix_fmt, codec_context->width, codec_context->height, 1); /* if the output format is not identical to the input format, then a temporary picture is needed too. It is then converted to the required output format */ - tmp_opicture = nullptr; if ( codec_context->pix_fmt != pf ) { - tmp_opicture = av_frame_alloc(); + tmp_opicture = av_frame_ptr{av_frame_alloc()}; - if ( !tmp_opicture ) { - Error( "Could not allocate tmp_opicture" ); + if (!tmp_opicture) { + Error("Could not allocate tmp_opicture"); return false; } size = av_image_get_buffer_size(pf, codec_context->width, codec_context->height, 1); - uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); - if ( !tmp_opicture_buf ) { - av_frame_free( &tmp_opicture ); - Error( "Could not allocate tmp_opicture_buf" ); + tmp_opicture->buf[0] = av_buffer_alloc(size); + if (!tmp_opicture->buf[0]) { + Error( "Could not allocate tmp_opicture buffer" ); return false; } av_image_fill_arrays(tmp_opicture->data, - tmp_opicture->linesize, tmp_opicture_buf, pf, + tmp_opicture->linesize, tmp_opicture->buf[0]->data, pf, codec_context->width, codec_context->height, 1); } } // end if ost @@ -300,8 +297,6 @@ bool VideoStream::OpenStream( ) { VideoStream::VideoStream( const char *in_filename, const char *in_format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ) : filename(in_filename), format(in_format), - opicture(nullptr), - tmp_opicture(nullptr), video_outbuf(nullptr), video_outbuf_size(0), last_pts( -1 ), @@ -376,12 +371,6 @@ VideoStream::~VideoStream( ) { /* close each codec */ if ( ost ) { avcodec_close( codec_context ); - av_free( opicture->data[0] ); - av_frame_free( &opicture ); - if ( tmp_opicture ) { - av_free( tmp_opicture->data[0] ); - av_frame_free( &tmp_opicture ); - } av_free( video_outbuf ); } @@ -464,8 +453,8 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, } else { memcpy( opicture->data[0], buffer, buffer_size ); } - AVFrame *opicture_ptr = opicture; + AVFrame *opicture_ptr = opicture.get(); AVPacket *pkt = packet_buffers[packet_index].get(); if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && diff --git a/src/zm_mpeg.h b/src/zm_mpeg.h index 8d9169c19..20195b2df 100644 --- a/src/zm_mpeg.h +++ b/src/zm_mpeg.h @@ -46,8 +46,8 @@ protected: AVStream *ost; AVCodecContext *codec_context; const AVCodec *codec; - AVFrame *opicture; - AVFrame *tmp_opicture; + av_frame_ptr opicture; + av_frame_ptr tmp_opicture; uint8_t *video_outbuf; int video_outbuf_size; double last_pts; diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 9e10afbaf..f584b5b0c 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -28,9 +28,6 @@ AVPixelFormat target_format = AV_PIX_FMT_NONE; ZMPacket::ZMPacket() : keyframe(0), stream(nullptr), - in_frame(nullptr), - out_frame(nullptr), - buffer(nullptr), image(nullptr), analysis_image(nullptr), score(-1), @@ -46,10 +43,7 @@ ZMPacket::ZMPacket() : ZMPacket::ZMPacket(Image *i, SystemTimePoint tv) : keyframe(0), stream(nullptr), - in_frame(nullptr), - out_frame(nullptr), timestamp(tv), - buffer(nullptr), image(i), analysis_image(nullptr), score(-1), @@ -65,10 +59,7 @@ ZMPacket::ZMPacket(Image *i, SystemTimePoint tv) : ZMPacket::ZMPacket(ZMPacket &p) : keyframe(0), stream(nullptr), - in_frame(nullptr), - out_frame(nullptr), timestamp(p.timestamp), - buffer(nullptr), image(nullptr), analysis_image(nullptr), score(-1), @@ -86,9 +77,6 @@ ZMPacket::ZMPacket(ZMPacket &p) : } ZMPacket::~ZMPacket() { - if (in_frame) av_frame_free(&in_frame); - if (out_frame) av_frame_free(&out_frame); - if (buffer) av_freep(&buffer); delete analysis_image; delete image; } @@ -112,24 +100,24 @@ int ZMPacket::decode(AVCodecContext *ctx) { if (in_frame) { Error("Already have a frame?"); } else { - in_frame = zm_av_frame_alloc(); + in_frame = av_frame_ptr{zm_av_frame_alloc()}; } // packets are always stored in AV_TIME_BASE_Q so need to convert to codec time base //av_packet_rescale_ts(&packet, AV_TIME_BASE_Q, ctx->time_base); - int ret = zm_send_packet_receive_frame(ctx, in_frame, *packet); + int ret = zm_send_packet_receive_frame(ctx, in_frame.get(), *packet); if (ret < 0) { if (AVERROR(EAGAIN) != ret) { Warning("Unable to receive frame : code %d %s.", ret, av_make_error_string(ret).c_str()); } - av_frame_free(&in_frame); + in_frame = nullptr; return 0; } int bytes_consumed = ret; if (ret > 0) { - zm_dump_video_frame(in_frame, "got frame"); + zm_dump_video_frame(in_frame.get(), "got frame"); #if HAVE_LIBAVUTIL_HWCONTEXT_H #if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0) @@ -168,7 +156,7 @@ int ZMPacket::decode(AVCodecContext *ctx) { } // end if target_format not set #endif - AVFrame *new_frame = zm_av_frame_alloc(); + av_frame_ptr new_frame{zm_av_frame_alloc()}; #if 0 if ( target_format == AV_PIX_FMT_RGB0 ) { if ( image ) { @@ -183,30 +171,28 @@ int ZMPacket::decode(AVCodecContext *ctx) { } #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); + zm_dump_video_frame(in_frame.get(), "Before hwtransfer"); + ret = av_hwframe_transfer_data(new_frame.get(), in_frame.get(), 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); + in_frame = nullptr; return 0; } - ret = av_frame_copy_props(new_frame, in_frame); + ret = av_frame_copy_props(new_frame.get(), in_frame.get()); if (ret < 0) { Error("Unable to copy props: %s, continuing", av_make_error_string(ret).c_str()); } - zm_dump_video_frame(new_frame, "After hwtransfer"); + zm_dump_video_frame(new_frame.get(), "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"); } #endif - av_frame_free(&in_frame); - in_frame = new_frame; + in_frame = std::move(new_frame); } else #endif #endif @@ -235,7 +221,7 @@ Image *ZMPacket::get_image(Image *i) { } image = i; } - image->Assign(in_frame); + image->Assign(in_frame.get()); return image; } @@ -256,7 +242,7 @@ AVPacket *ZMPacket::set_packet(AVPacket *p) { AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { if (!out_frame) { - out_frame = zm_av_frame_alloc(); + out_frame = av_frame_ptr{zm_av_frame_alloc()}; if (!out_frame) { Error("Unable to allocate a frame"); return nullptr; @@ -268,18 +254,23 @@ AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { codec_imgsize = av_image_get_buffer_size( format, width, height, alignment); Debug(1, "buffer size %u from %s %dx%d", codec_imgsize, av_get_pix_fmt_name(format), width, height); - buffer = (uint8_t *)av_malloc(codec_imgsize); + out_frame->buf[0] = av_buffer_alloc(codec_imgsize); + if (!out_frame->buf[0]) { + Error("Unable to allocate a frame buffer"); + out_frame = nullptr; + return nullptr; + } int ret; if ((ret=av_image_fill_arrays( out_frame->data, out_frame->linesize, - buffer, + out_frame->buf[0]->data, format, width, height, alignment))<0) { Error("Failed to fill_arrays %s", av_make_error_string(ret).c_str()); - av_frame_free(&out_frame); + out_frame = nullptr; return nullptr; } @@ -287,5 +278,5 @@ AVFrame *ZMPacket::get_out_frame(int width, int height, AVPixelFormat format) { out_frame->height = height; out_frame->format = format; } - return out_frame; + return out_frame.get(); } // end AVFrame *ZMPacket::get_out_frame( AVCodecContext *ctx ); diff --git a/src/zm_packet.h b/src/zm_packet.h index b3703ceee..255d8e995 100644 --- a/src/zm_packet.h +++ b/src/zm_packet.h @@ -45,10 +45,9 @@ class ZMPacket { int keyframe; AVStream *stream; // Input stream av_packet_ptr packet; // Input packet, undecoded - AVFrame *in_frame; // Input image, decoded Theoretically only filled if needed. - AVFrame *out_frame; // output image, Only filled if needed. + av_frame_ptr in_frame; // Input image, decoded Theoretically only filled if needed. + av_frame_ptr out_frame; // output image, Only filled if needed. SystemTimePoint timestamp; - uint8_t *buffer; // buffer used in image Image *image; Image *analysis_image; int score; @@ -63,7 +62,7 @@ class ZMPacket { public: AVPacket *av_packet() { return packet.get(); } AVPacket *set_packet(AVPacket *p) ; - AVFrame *av_frame() { return out_frame; } + AVFrame *av_frame() { return out_frame.get(); } Image *get_image(Image *i=nullptr); Image *set_image(Image *); ssize_t ram(); diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index 68e7af4ef..a5cf67fda 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -25,8 +25,6 @@ SWScale::SWScale() : gotdefaults(false), swscale_ctx(nullptr), - input_avframe(nullptr), - output_avframe(nullptr), default_width(0), default_height(0) { @@ -34,13 +32,13 @@ SWScale::SWScale() : } bool SWScale::init() { - input_avframe = av_frame_alloc(); + input_avframe = av_frame_ptr{zm_av_frame_alloc()}; if (!input_avframe) { Error("Failed allocating AVFrame for the input"); return false; } - output_avframe = av_frame_alloc(); + output_avframe = av_frame_ptr{zm_av_frame_alloc()}; if (!output_avframe) { Error("Failed allocating AVFrame for the output"); return false; @@ -51,12 +49,6 @@ bool SWScale::init() { SWScale::~SWScale() { /* Free up everything */ - if ( input_avframe ) - av_frame_free(&input_avframe); - - if ( output_avframe ) - av_frame_free(&output_avframe); - if ( swscale_ctx ) { sws_freeContext(swscale_ctx); swscale_ctx = nullptr; diff --git a/src/zm_swscale.h b/src/zm_swscale.h index c0d1e7120..32ce70217 100644 --- a/src/zm_swscale.h +++ b/src/zm_swscale.h @@ -24,8 +24,8 @@ class SWScale { protected: bool gotdefaults; struct SwsContext* swscale_ctx; - AVFrame* input_avframe; - AVFrame* output_avframe; + av_frame_ptr input_avframe; + av_frame_ptr output_avframe; enum _AVPIXELFORMAT default_input_pf; enum _AVPIXELFORMAT default_output_pf; unsigned int default_width; diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 05731cdf3..fe771160c 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -85,10 +85,6 @@ VideoStore::VideoStore( audio_in_ctx(p_audio_in_ctx), audio_out_codec(nullptr), audio_out_ctx(nullptr), - video_in_frame(nullptr), - in_frame(nullptr), - out_frame(nullptr), - hw_frame(nullptr), packets_written(0), frame_count(0), hw_device_ctx(nullptr), @@ -604,12 +600,12 @@ void VideoStore::flush_codecs() { * At the end of the file, we pass the remaining samples to * the encoder. */ while (zm_resample_get_delay(resample_ctx, audio_out_ctx->sample_rate)) { - zm_resample_audio(resample_ctx, nullptr, out_frame); + zm_resample_audio(resample_ctx, nullptr, out_frame.get()); - if (zm_add_samples_to_fifo(fifo, out_frame)) { + if (zm_add_samples_to_fifo(fifo, out_frame.get())) { // Should probably set the frame size to what is reported FIXME - if (zm_get_samples_from_fifo(fifo, out_frame)) { - if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *pkt) > 0) { + if (zm_get_samples_from_fifo(fifo, out_frame.get())) { + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame.get(), *pkt) > 0) { av_packet_guard pkt_guard{pkt}; av_packet_rescale_ts(pkt.get(), audio_out_ctx->time_base, @@ -630,7 +626,7 @@ void VideoStore::flush_codecs() { // SHould probably set the frame size to what is reported FIXME if (av_audio_fifo_read(fifo, (void **)out_frame->data, frame_size)) { - if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *pkt)) { + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame.get(), *pkt)) { av_packet_guard pkt_guard{pkt}; pkt->stream_index = audio_out_stream->index; @@ -740,14 +736,6 @@ VideoStore::~VideoStore() { } swr_free(&resample_ctx); } - if (in_frame) { - av_frame_free(&in_frame); - in_frame = nullptr; - } - if (out_frame) { - av_frame_free(&out_frame); - out_frame = nullptr; - } if (converted_in_samples) { av_free(converted_in_samples); converted_in_samples = nullptr; @@ -883,16 +871,15 @@ bool VideoStore::setup_resampler() { /** Create a new frame to store the audio samples. */ if (!in_frame) { - if (!(in_frame = zm_av_frame_alloc())) { + if (!(in_frame = av_frame_ptr{zm_av_frame_alloc()})) { Error("Could not allocate in frame"); return false; } } /** Create a new frame to store the audio samples. */ - if (!(out_frame = zm_av_frame_alloc())) { + if (!(out_frame = av_frame_ptr{zm_av_frame_alloc()})) { Error("Could not allocate out frame"); - av_frame_free(&in_frame); return false; } out_frame->sample_rate = audio_out_ctx->sample_rate; @@ -913,14 +900,10 @@ bool VideoStore::setup_resampler() { 0, nullptr); if (!resample_ctx) { Error("Could not allocate resample context"); - av_frame_free(&in_frame); - av_frame_free(&out_frame); return false; } if ((ret = swr_init(resample_ctx)) < 0) { Error("Could not open resampler %d", ret); - av_frame_free(&in_frame); - av_frame_free(&out_frame); swr_free(&resample_ctx); return false; } @@ -949,7 +932,7 @@ bool VideoStore::setup_resampler() { // Setup the data pointers in the AVFrame if (avcodec_fill_audio_frame( - out_frame, audio_out_ctx->channels, + out_frame.get(), audio_out_ctx->channels, audio_out_ctx->sample_fmt, (const uint8_t *)converted_in_samples, audioSampleBuffer_size, 0) < 0) { @@ -1021,6 +1004,9 @@ int VideoStore::writePacket(const std::shared_ptr &zm_pkt) { int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet) { av_packet_guard pkt_guard; +#if HAVE_LIBAVUTIL_HWCONTEXT_H + av_frame_ptr hw_frame; +#endif frame_count += 1; @@ -1046,7 +1032,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet //Go straight to out frame swscale.Convert( zm_packet->image, - zm_packet->buffer, + zm_packet->out_frame->buf[0]->data, zm_packet->codec_imgsize, zm_packet->image->AVPixFormat(), chosen_codec_data->sw_pix_fmt, @@ -1062,7 +1048,7 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet return 0; } // Go straight to out frame - swscale.Convert(zm_packet->in_frame, out_frame); + swscale.Convert(zm_packet->in_frame.get(), out_frame); } else { Error("Have neither in_frame or image in packet %d!", zm_packet->image_index); @@ -1070,36 +1056,33 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end if has packet or image } else { // Have in_frame.... may need to convert it to out_frame - swscale.Convert(zm_packet->in_frame, zm_packet->out_frame); + swscale.Convert(zm_packet->in_frame.get(), zm_packet->out_frame.get()); } // end if no in_frame } // end if no out_frame - AVFrame *frame = zm_packet->out_frame; + AVFrame *frame = zm_packet->out_frame.get(); #if HAVE_LIBAVUTIL_HWCONTEXT_H if (video_out_ctx->hw_frames_ctx) { int ret; - if (!(hw_frame = av_frame_alloc())) { - ret = AVERROR(ENOMEM); - return ret; + hw_frame = av_frame_ptr{zm_av_frame_alloc()}; + if (!hw_frame) { + return AVERROR(ENOMEM); } - if ((ret = av_hwframe_get_buffer(video_out_ctx->hw_frames_ctx, hw_frame, 0)) < 0) { + if ((ret = av_hwframe_get_buffer(video_out_ctx->hw_frames_ctx, hw_frame.get(), 0)) < 0) { Error("Error code: %s", av_err2str(ret)); - av_frame_free(&hw_frame); return ret; } if (!hw_frame->hw_frames_ctx) { Error("Outof ram!"); - av_frame_free(&hw_frame); return 0; } - if ((ret = av_hwframe_transfer_data(hw_frame, zm_packet->out_frame, 0)) < 0) { + if ((ret = av_hwframe_transfer_data(hw_frame.get(), zm_packet->out_frame.get(), 0)) < 0) { Error("Error while transferring frame data to surface: %s.", av_err2str(ret)); - av_frame_free(&hw_frame); return ret; } - frame = hw_frame; + frame = hw_frame.get(); } // end if hwaccel #endif @@ -1217,7 +1200,6 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr &zm_packet } // end if codec matches write_packet(opkt.get(), video_out_stream); - if (hw_frame) av_frame_free(&hw_frame); return 1; } // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt ) @@ -1236,24 +1218,24 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet if (audio_out_codec) { // I wonder if we can get multiple frames per packet? Probably - int ret = zm_send_packet_receive_frame(audio_in_ctx, in_frame, *ipkt); + int ret = zm_send_packet_receive_frame(audio_in_ctx, in_frame.get(), *ipkt); if (ret < 0) { Debug(3, "failed to receive frame code: %d", ret); return 0; } zm_dump_frame(in_frame, "In frame from decode"); - AVFrame *input_frame = in_frame; + AVFrame *input_frame = in_frame.get(); - while (zm_resample_audio(resample_ctx, input_frame, out_frame)) { + while (zm_resample_audio(resample_ctx, input_frame, out_frame.get())) { //out_frame->pkt_duration = in_frame->pkt_duration; // resampling doesn't alter duration - if (zm_add_samples_to_fifo(fifo, out_frame) <= 0) + if (zm_add_samples_to_fifo(fifo, out_frame.get()) <= 0) break; // We put the samples into the fifo so we are basically resetting the frame out_frame->nb_samples = audio_out_ctx->frame_size; - if (zm_get_samples_from_fifo(fifo, out_frame) <= 0) + if (zm_get_samples_from_fifo(fifo, out_frame.get()) <= 0) break; out_frame->pts = audio_next_pts; @@ -1261,7 +1243,7 @@ int VideoStore::writeAudioFramePacket(const std::shared_ptr &zm_packet zm_dump_frame(out_frame, "Out frame after resample"); - if (zm_send_frame_receive_packet(audio_out_ctx, out_frame, *opkt) <= 0) + if (zm_send_frame_receive_packet(audio_out_ctx, out_frame.get(), *opkt) <= 0) break; // Scale the PTS of the outgoing packet to be the correct time base diff --git a/src/zm_videostore.h b/src/zm_videostore.h index d66c82f76..b04550225 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -59,10 +59,8 @@ class VideoStore { // Move this into the object so that we aren't constantly allocating/deallocating it on the stack av_packet_ptr opkt; - AVFrame *video_in_frame; - AVFrame *in_frame; - AVFrame *out_frame; - AVFrame *hw_frame; + av_frame_ptr in_frame; + av_frame_ptr out_frame; SWScale swscale; unsigned int packets_written; From a9cc417a8b6ea42d94eddb13d00f6404b77c6cee Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 13:02:12 -0400 Subject: [PATCH 44/56] Fix logic around warning about keyframe interval being larger than max image buffer count. --- src/zm_packetqueue.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 5ea598793..ada763557 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -313,8 +313,10 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { ++it; } // end while - if (keyframe_interval > pre_event_video_packet_count) { - Warning("Max Image Buffer setting is too low! Needs to be great than keyframe interval. Keyframe interval is at least %d", keyframe_interval); + if ((keyframe_interval == 1) and max_video_packet_count) { + Warning("Did not find a second keyframe in the packet queue. It may be that" + " the Max Image Buffer setting is lower than the keyframe interval. We" + " need it to be greater than the keyframe interval."); } Debug(1, "Resulting it pointing at latest packet? %d, next front points to begin? %d, Keyframe interval %d", ( *it == add_packet ), From 30271283ba11b1f1e9762235c19610fdc860dce1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 14:19:57 -0400 Subject: [PATCH 45/56] Make CMD_LIST available to specific monitors as well as all monitors. --- src/zmu.cpp | 69 +++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/zmu.cpp b/src/zmu.cpp index 99af22e37..b081673a1 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -750,33 +750,35 @@ int main(int argc, char *argv[]) { exit_zmu(-1); #endif // ZM_HAS_V4L2 } + } // end if monitor id or not - if ( function & ZMU_LIST ) { - std::string sql = "SELECT `Id`, `Capturing`+0, `Analysing`+0, `Recording`+0 FROM `Monitors`"; - if (!verbose) { - sql += " WHERE `Capturing` != 'None'"; - } - sql += " ORDER BY Id ASC"; + if (function & ZMU_LIST) { + std::string sql = "SELECT `Id`, `Capturing`+0, `Analysing`+0, `Recording`+0 FROM `Monitors`"; + if (!verbose) { + sql += " WHERE `Capturing` != 'None'"; + } + sql += " ORDER BY Id ASC"; - MYSQL_RES *result = zmDbFetch(sql); - if (!result) { - exit_zmu(-1); - } - Debug(1, "Got %" PRIu64 " monitors", static_cast(mysql_num_rows(result))); + MYSQL_RES *result = zmDbFetch(sql); + if (!result) { + exit_zmu(-1); + } + Debug(1, "Got %" PRIu64 " monitors", static_cast(mysql_num_rows(result))); - printf("%4s %9s %9s %9s %5s %8s %13s %5s %5s %9s %9s\n", - "Id", "Capturing", "Analysing", "Recording", "State", "TrgState", - "LastImageTime", "RdIdx", "WrIdx", "LastEvent", "FrameRate"); - for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { - int monitor_id = atoi(dbrow[0]); + printf("%4s %9s %9s %9s %5s %8s %13s %5s %5s %9s %9s\n", + "Id", "Capturing", "Analysing", "Recording", "State", "TrgState", + "LastImageTime", "RdIdx", "WrIdx", "LastEvent", "FrameRate"); + for (int i=0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) { + int monitor_id = atoi(dbrow[0]); + if (mon_id and (monitor_id != mon_id)) continue; + if (!user || user->canAccess(monitor_id)) { int monitor_capturing = atoi(dbrow[1]); - if ( !user || user->canAccess(monitor_id) ) { - if (monitor_capturing > Monitor::CAPTURING_NONE) { - std::shared_ptr monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); - if ( monitor && monitor->connect() ) { - SystemTimePoint timestamp = monitor->GetTimestamp(); + if (monitor_capturing > Monitor::CAPTURING_NONE) { + std::shared_ptr monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); + if (monitor && monitor->connect()) { + SystemTimePoint timestamp = monitor->GetTimestamp(); - printf("%4d %9d %9d %9d %5d %8d %13.2f %5d %5d %9" PRIu64 "%10.2f\n", + printf("%4d %9d %9d %9d %5d %8d %13.2f %5d %5d %9" PRIu64 "%10.2f\n", monitor->Id(), monitor->Capturing(), monitor->Analysing(), @@ -788,11 +790,11 @@ int main(int argc, char *argv[]) { monitor->GetLastWriteIndex(), monitor->GetLastEventId(), monitor->GetFPS() - ); - } - } else { - printf("%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", - mon_id, + ); + } + } else { + printf("%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", + monitor_id, function, 0, 0, @@ -801,13 +803,12 @@ int main(int argc, char *argv[]) { 0, 0, 0.0 - ); - } // end if function filter - } // endif !user || canAccess(mon_id) - } // end foreach row - mysql_free_result(result); - } // end if function && ZMU_LIST - } // end if monitor id or not + ); + } // end if function filter + } // endif !user || canAccess(mon_id) + } // end foreach row + mysql_free_result(result); + } // end if function && ZMU_LIST delete user; exit_zmu(0); From a03189046ec053ce19e7c598a70416e1b4dd6743 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 17:54:27 -0400 Subject: [PATCH 46/56] Rename zmc_heartbeat_time to heartbeat_time. Set it during waiting for prime and capturing. --- src/zm_monitor.cpp | 2 +- src/zm_monitor.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 34e9a3975..36532cee7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -2416,7 +2416,7 @@ int Monitor::Capture() { std::shared_ptr packet = std::make_shared(); packet->image_index = image_count; packet->timestamp = std::chrono::system_clock::now(); - shared_data->zmc_heartbeat_time = std::chrono::system_clock::to_time_t(packet->timestamp); + shared_data->heartbeat_time = std::chrono::system_clock::to_time_t(packet->timestamp); int captureResult = camera->Capture(packet); Debug(4, "Back from capture result=%d image count %d", captureResult, image_count); diff --git a/src/zm_monitor.h b/src/zm_monitor.h index fcd48212c..7c3a43cc1 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -202,7 +202,7 @@ protected: uint64_t extrapad1; }; union { /* +80 */ - time_t zmc_heartbeat_time; /* Constantly updated by zmc. Used to determine if the process is alive or hung or dead */ + time_t heartbeat_time; /* Constantly updated by zmc. Used to determine if the process is alive or hung or dead */ uint64_t extrapad2; }; union { /* +88 */ @@ -579,11 +579,11 @@ public: gettimeofday(&now, nullptr); Debug(3, "Shared data is valid, checking heartbeat %" PRIi64 " - %" PRIi64 " = %" PRIi64" < %f", static_cast(now.tv_sec), - static_cast(shared_data->zmc_heartbeat_time), - static_cast(now.tv_sec - shared_data->zmc_heartbeat_time), + static_cast(shared_data->heartbeat_time), + static_cast(now.tv_sec - shared_data->heartbeat_time), config.watch_max_delay); - if ((now.tv_sec - shared_data->zmc_heartbeat_time) < config.watch_max_delay) + if ((now.tv_sec - shared_data->heartbeat_time) < config.watch_max_delay) return true; } return false; @@ -742,7 +742,7 @@ public: SystemTimePoint GetStartupTime() const { return std::chrono::system_clock::from_time_t(shared_data->startup_time); } void SetStartupTime(SystemTimePoint time) { shared_data->startup_time = std::chrono::system_clock::to_time_t(time); } void SetHeartbeatTime(SystemTimePoint time) { - shared_data->zmc_heartbeat_time = std::chrono::system_clock::to_time_t(time); + shared_data->heartbeat_time = std::chrono::system_clock::to_time_t(time); } void get_ref_image(); From baf65819cb15f7f2ab655db99bc42d2e17db9c72 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:43:20 -0400 Subject: [PATCH 47/56] add notify_all functions --- src/zm_packet.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/zm_packet.h b/src/zm_packet.h index 23a593b22..9601499af 100644 --- a/src/zm_packet.h +++ b/src/zm_packet.h @@ -77,6 +77,9 @@ class ZMPacket { //AVFrame *get_out_frame(const AVCodecContext *ctx); AVFrame *get_out_frame(int width, int height, AVPixelFormat format); int get_codec_imgsize() { return codec_imgsize; }; + void notify_all() { + this->condition_.notify_all(); + } }; class ZMLockedPacket { @@ -119,6 +122,10 @@ class ZMLockedPacket { Debug(4, "packet %d waiting", packet_->image_index); packet_->condition_.wait(lck_); } + void notify_all() { + packet_->notify_all(); + } + }; #endif /* ZM_PACKET_H */ From 42bb0c41a08fec4c421cae6ef248ece3b9645eff Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:43:49 -0400 Subject: [PATCH 48/56] Rename zmc_heartbeat_time to just heartbeat_time --- scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 70960d36e..196b9fd8f 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -170,7 +170,7 @@ our %mem_data = ( audio_frequency => { type=>'uint32', seq=>$mem_seq++ }, audio_channels => { type=>'uint32', seq=>$mem_seq++ }, startup_time => { type=>'time_t64', seq=>$mem_seq++ }, - zmc_heartbeat_time => { type=>'time_t64', seq=>$mem_seq++ }, + heartbeat_time => { type=>'time_t64', seq=>$mem_seq++ }, last_write_time => { type=>'time_t64', seq=>$mem_seq++ }, last_read_time => { type=>'time_t64', seq=>$mem_seq++ }, last_viewed_time => { type=>'time_t64', seq=>$mem_seq++ }, From 1d40279fcf7457c55671b53567468859f0168adb Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:44:10 -0400 Subject: [PATCH 49/56] Check heartbeat time for monitors that don't do decoding --- scripts/zmwatch.pl.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index f585dd65e..bf5120e41 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -101,6 +101,17 @@ while (!$zm_terminate) { $monitor->control('restart'); next; } + + my $heartbeat_time = zmMemRead($monitor, 'shared_data:heartbeat_time'); + my $heartbeat_elapsed = $now-$heartbeat_time; + if ($heartbeat_elapsed > $Config{ZM_WATCH_MAX_DELAY}) { + Info("Restarting capture daemon for $monitor->{Id} $monitor->{Name}, $now - heartbeat time $heartbeat_time $heartbeat_elapsed > $Config{ZM_WATCH_MAX_DELAY}"); + $monitor->control('restart'); + next; + } else { + Debug("Monitor $monitor->{Id} $monitor->{Name}, heartbeat time $now - $heartbeat_time $heartbeat_elapsed < $Config{ZM_WATCH_MAX_DELAY}"); + } + next if $monitor->{Capturing} eq 'Ondemand'; next if $monitor->{Decoding} eq 'None' or $monitor->{Decoding} eq 'Ondemand'; From 6f020960989af30382d69d01f54dfd0e8edb666e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:44:45 -0400 Subject: [PATCH 50/56] Notify all packets when doing packetqueue:stop so prevent deadlock when waiting for decode --- src/zm_packetqueue.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index bccab60b9..f03f921e2 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -342,6 +342,7 @@ void PacketQueue::clearPackets(const std::shared_ptr &add_packet) { void PacketQueue::stop() { deleting = true; condition.notify_all(); + for (const auto p : pktQueue) p->notify_all(); } void PacketQueue::clear() { From c40cedc15cf3b4a994aaaeeb7080b644ec7fed48 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:45:45 -0400 Subject: [PATCH 51/56] Simply code about zm_terminate, replace fixed 60 second max sleep time with ZM_WATCH_MAX_DELAY --- src/zmc.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/zmc.cpp b/src/zmc.cpp index 315682e86..8466d1bdf 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -251,36 +251,31 @@ int main(int argc, char *argv[]) { monitor->Id()); zmDbDo(sql); - if (monitor->Capturing() == Monitor::CAPTURING_ONDEMAND) { while (!zm_terminate and !monitor->hasViewers()) { Debug(1, "ONDEMAND and no Viewers. Sleeping"); std::this_thread::sleep_for(Seconds(1)); + monitor->SetHeartbeatTime(std::chrono::system_clock::now()); } } Seconds sleep_time = Seconds(0); - while (monitor->PrimeCapture() <= 0) { + while ((monitor->PrimeCapture() <= 0) and !zm_terminate) { if (prime_capture_log_count % 60) { - logPrintf(Logger::ERROR + monitor->Importance(), - "Failed to prime capture of initial monitor"); + logPrintf(Logger::ERROR + monitor->Importance(), "Failed to prime capture of initial monitor"); } else { Debug(1, "Failed to prime capture of initial monitor"); } prime_capture_log_count++; - if (zm_terminate) { - break; - } - if (sleep_time < Seconds(60)) { + if (sleep_time < Seconds(ZM_WATCH_MAX_DELAY)) { sleep_time++; } std::this_thread::sleep_for(sleep_time); + monitor->SetHeartbeatTime(std::chrono::system_clock::now()); } - if (zm_terminate) { - break; - } + if (zm_terminate) break; sql = stringtf( "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'", @@ -288,9 +283,7 @@ int main(int argc, char *argv[]) { zmDbDo(sql); } // end foreach monitor - if (zm_terminate) { - break; - } + if (zm_terminate) break; std::vector last_capture_times = std::vector(monitors.size()); Microseconds sleep_time = Microseconds(0); @@ -325,11 +318,13 @@ int main(int argc, char *argv[]) { } monitors[i]->UpdateFPS(); + SystemTimePoint now = std::chrono::system_clock::now(); + monitors[i]->SetHeartbeatTime(now); + // capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate. Microseconds delay = (monitors[i]->GetState() == Monitor::ALARM) ? monitors[i]->GetAlarmCaptureDelay() : monitors[i]->GetCaptureDelay(); if (delay != Seconds(0)) { - SystemTimePoint now = std::chrono::system_clock::now(); if (last_capture_times[i].time_since_epoch() != Seconds(0)) { Microseconds delta_time = std::chrono::duration_cast(now - last_capture_times[i]); From 79f2d1d20f47c4476fc42361046fa2207201a3ec Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:46:33 -0400 Subject: [PATCH 52/56] If nothing selected, present a message about it. This shouldn't happen, but does --- web/skins/classic/views/js/events.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/events.js b/web/skins/classic/views/js/events.js index af5c67863..f1b76ff14 100644 --- a/web/skins/classic/views/js/events.js +++ b/web/skins/classic/views/js/events.js @@ -123,7 +123,11 @@ function manageDelConfirmModalBtns() { evt.preventDefault(); const selections = getIdSelections(); - deleteEvents(selections); + if (!selections.length) { + alert('Please select events to delete.'); + } else { + deleteEvents(selections); + } }); // Manage the CANCEL modal button From 89631a85319aeecb8fbf81613fb686bfc7af37a7 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:47:07 -0400 Subject: [PATCH 53/56] Fix spelling --- src/zm_monitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 36532cee7..720d76a64 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -3155,7 +3155,7 @@ int Monitor::Close() { } if (analysis_thread) { analysis_thread->Stop(); - Debug(1, "Analysi stopped"); + Debug(1, "Analysis stopped"); } //ONVIF Teardown From 2fa90cff4d02e688e9c29e4666b65ba06139c568 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Aug 2022 20:47:47 -0400 Subject: [PATCH 54/56] Use const or let instead of var --- web/skins/classic/views/js/event.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index c29419472..15519043d 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -924,7 +924,7 @@ function initPage() { vid.on('volumechange', function() { setCookie('volume', vid.volume(), 3600); }); - var cookie = getCookie('volume'); + const cookie = getCookie('volume'); if (cookie) vid.volume(cookie); vid.on('timeupdate', function() { @@ -932,7 +932,6 @@ function initPage() { }); vid.on('ratechange', function() { rate = vid.playbackRate() * 100; - console.log("rate change " + rate); $j('select[name="rate"]').val(rate); setCookie('zmEventRate', rate, 3600); }); @@ -949,7 +948,7 @@ function initPage() { if (!$j('#videoFeed')) { console.log('No element with id tag videoFeed found.'); } else { - var streamImg = $j('#videoFeed img'); + let streamImg = $j('#videoFeed img'); if (!streamImg) { streamImg = $j('#videoFeed object'); } From cf3c194e21d4ee6e7b2422e448912243aa39e52a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Aug 2022 11:27:50 -0400 Subject: [PATCH 55/56] Handle when there are no servers and hence no defined ServerId in newMonitor --- web/includes/actions/monitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index edc9ab1bd..b27ff4443 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -109,7 +109,7 @@ if ($action == 'save') { } } # end foreach type - if ($newMonitor['ServerId'] == 'auto') { + if (isset($newMonitor['ServerId']) and ($newMonitor['ServerId'] == 'auto')) { $newMonitor['ServerId'] = dbFetchOne( 'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id'); ZM\Debug('Auto selecting server: Got ' . $newMonitor['ServerId']); From 6e5b9b56ed0015a435b80e945821ed62f4682bf2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Aug 2022 11:55:35 -0400 Subject: [PATCH 56/56] Only load linked monitors if doing capture --- src/zm_monitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 830369a74..420551071 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -775,7 +775,6 @@ std::shared_ptr Monitor::Load(unsigned int p_id, bool load_zones, Purpo } bool Monitor::connect() { - ReloadLinkedMonitors(); ReloadZones(); if (zones.size() != zone_count) { Warning("Monitor %d has incorrect zone_count %d != %zu", id, zone_count, zones.size()); @@ -960,6 +959,7 @@ bool Monitor::connect() { usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal shared_data->valid = true; + ReloadLinkedMonitors(); //ONVIF and Amcrest Setup //For now, only support one event type per camera, so share some state.