mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-10-30 02:08:42 -04:00
A monitor with Capturing=Ondemand and LinkedMonitors never recorded a linked alarm unless someone happened to be watching it. Links are evaluated in Analyse(), per captured packet, and zmc pauses an on-demand camera, stopping its analysis thread, 10 s after the last viewer. While paused nothing looked at the links, so every alarm on the linked monitor was missed. This is the low-bandwidth setup of a substream that analyses and a main stream that records on demand. zmc's on-demand decision moves into Monitor::CheckOndemand(), around a DecideOndemand() table that can be tested: - While paused, the links are checked on every pass. The analysis thread is stopped then, so the capture thread reads them without racing it. An alarm reopens the camera. - An unwatched monitor keeps capturing while it is alarmed or in its post-event frames, instead of pausing in the middle of the event. - A link only reports an alarm while it lasts, and reopening the camera can take longer than that (2.5 s against a local camera). So the wake is remembered for up to 30 s, and the first packet Analyse() scores counts it as a linked alarm, then clears it. Tested live on 1.38 with a paused on-demand passthrough monitor linked to an analysing one. Before: an 8 s forced alarm on the source recorded nothing. After: the monitor woke within 0.1 s, alarmed 2.5 s later and recorded the event. A 1 s alarm, over before the camera reopened, was also recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
3.6 KiB
C++
92 lines
3.6 KiB
C++
/*
|
|
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "zm_catch2.h"
|
|
|
|
#include "zm_config.h"
|
|
#include "zm_monitor.h"
|
|
|
|
#include <chrono>
|
|
|
|
namespace {
|
|
|
|
using Step = Monitor::OndemandStep;
|
|
|
|
void EnsureConfig() {
|
|
if (!config.font_file_location) config.font_file_location = "";
|
|
if (!config.event_close_mode) config.event_close_mode = "idle";
|
|
}
|
|
|
|
class WakeMonitor : public Monitor {
|
|
public:
|
|
void WakeUntil(std::chrono::steady_clock::time_point until) {
|
|
linked_wake_until_ =
|
|
std::chrono::duration_cast<std::chrono::nanoseconds>(until.time_since_epoch()).count();
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// An on-demand monitor with LinkedMonitors only noticed the link while it was
|
|
// capturing, and it stopped capturing 10 s after the last viewer: every later
|
|
// alarm on the linked monitor was missed. It now opens the camera when a link
|
|
// alarms, and keeps capturing through the alarm it causes.
|
|
TEST_CASE("On-demand capture step", "[Monitor]") {
|
|
int link_checks = 0;
|
|
auto link = [&link_checks](bool alarmed) {
|
|
return [&link_checks, alarmed]() { link_checks++; return alarmed; };
|
|
};
|
|
|
|
SECTION("watched: capture, opening the camera if it is closed") {
|
|
REQUIRE(Monitor::DecideOndemand(true, true, true, false, link(false)) == Step::CAPTURE);
|
|
REQUIRE(Monitor::DecideOndemand(true, true, false, false, link(false)) == Step::PLAY);
|
|
}
|
|
SECTION("no image yet: capture one for thumbnails") {
|
|
REQUIRE(Monitor::DecideOndemand(false, false, true, false, link(false)) == Step::CAPTURE);
|
|
REQUIRE(Monitor::DecideOndemand(false, false, false, false, link(false)) == Step::PLAY);
|
|
}
|
|
SECTION("unwatched and idle: pause, then stay paused") {
|
|
REQUIRE(Monitor::DecideOndemand(false, true, true, false, link(false)) == Step::PAUSE);
|
|
REQUIRE(Monitor::DecideOndemand(false, true, false, false, link(false)) == Step::SLEEP);
|
|
}
|
|
SECTION("unwatched but alarmed or woken: keep capturing") {
|
|
REQUIRE(Monitor::DecideOndemand(false, true, true, true, link(false)) == Step::CAPTURE);
|
|
}
|
|
SECTION("paused and a linked monitor alarms: open the camera") {
|
|
REQUIRE(Monitor::DecideOndemand(false, true, false, false, link(true)) == Step::PLAY);
|
|
}
|
|
SECTION("links are read only while the camera is closed") {
|
|
// With the camera open the analysis thread evaluates them.
|
|
Monitor::DecideOndemand(false, true, true, false, link(true));
|
|
Monitor::DecideOndemand(true, true, true, false, link(true));
|
|
Monitor::DecideOndemand(true, true, false, false, link(true));
|
|
REQUIRE(link_checks == 0);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("A link wake lasts until acted on or timed out", "[Monitor]") {
|
|
EnsureConfig();
|
|
WakeMonitor monitor;
|
|
REQUIRE_FALSE(monitor.LinkedWakePending());
|
|
|
|
monitor.WakeUntil(std::chrono::steady_clock::now() + Monitor::kLinkedWakeTimeout);
|
|
REQUIRE(monitor.LinkedWakePending());
|
|
|
|
monitor.WakeUntil(std::chrono::steady_clock::now() - std::chrono::seconds(1));
|
|
REQUIRE_FALSE(monitor.LinkedWakePending());
|
|
}
|