Restructure std_alarmedpixels inner loop so GCC/Clang can auto-vectorize
it at -O2/-O3. The compiler now emits 16-byte SIMD (SSE2/NEON) processing
16 pixels per iteration instead of 1.
Three changes enable this:
- Extract inner loop into static alarmedpixels_row() with __restrict__
on function parameters, giving the compiler a strong no-alias guarantee
- Use branchless bitwise AND instead of short-circuit && to avoid
branches that block vectorization
- Remove per-row Debug(7) call that clobbered memory from the compiler's
perspective, invalidating pointer analysis
The original hand-written SSE2 ASM (removed in 2011, commit 8e9ccfe1e)
had alignment restrictions and didn't use per-row polygon ranges. This
approach is portable, maintainable, and achieves equivalent throughput.
GCC confirms: "loop vectorized using 16 byte vectors"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 13 global include_directories() calls with list(APPEND
ZM_INCLUDE_DIRS ...) in root CMakeLists.txt, then apply them scoped
to the zm static library target via target_include_directories() in
src/CMakeLists.txt.
Previously all library include paths (MySQL, OpenSSL, curl, zlib, JPEG,
pthread, PCRE, VLC, VNC, libunwind, GnuTLS, Mosquitto) leaked into
vendored deps (RtspServer, CxxUrl, bcrypt). Now only the zm target and
its dependents see them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Provide default rtsp://<ip>/ monitor entry for cameras discovered
via ARP that lack a vendor-specific probe function, so they always
have a URL for the Add button
- Only render the Add button and populate ProbeResults when url is
non-empty, preventing the "No url in button" alert
- Fix curl_getinfo() called after curl_close() which broke HTTP
response body parsing in probe functions
- Add missing break in import switch case to prevent fall-through
to default warning
maybe fixes#4613
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace global add_definitions(-DWITH_GSOAP) and add_definitions(-DZM_STRIP_NEON=1)
with target_compile_definitions on the zm static library target. These defines are
only consumed by src/ files but were leaking into all vendored deps (RtspServer,
CxxUrl, bcrypt, etc).
WITH_GSOAP is PUBLIC on zm since the executables linking zm include headers
that check it. ZM_STRIP_NEON is PRIVATE since it's only used in zm_image.cpp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 4 hardcoded `perl` invocations with `${PERL_EXECUTABLE}` from
find_package(Perl) so builds work when perl isn't in PATH or the wrong
perl would be found (NixOS, custom installs, Homebrew vs system).
Also: fix ZMCONFGEN_RESULT variable case mismatch in error message,
remove deprecated IMMEDIATE keyword from configure_file(), bump
cmake_minimum_required from 3.5 to 3.12 (C++17 requires 3.9+), and
remove dead C++11 fallback for CMake < 3.8 in ConfigureBaseTargets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove phpunit/phpunit from require-dev in web/api/composer.json.
The pinned ^3.7 version is vulnerable to unsafe deserialization in
PHPT code coverage handling. Since ZoneMinder does not run CakePHP
unit tests in CI, the dependency is unused.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow specifying a local git directory to clone from without pulling it
first. Accepts -l=DIR / --local-source=DIR flag or a trailing positional
argument that is an existing directory. Skips all git pull operations on
the source, using the directory contents as-is.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three changes to prevent the analysis thread from stalling and the
packet queue from filling up:
1. Replace blind sleep_for/usleep in Event::Run() with
packetqueue->wait_for() condition variable waits. The event thread
now wakes immediately when decoder/analysis completes or new packets
are queued, instead of always sleeping the full 33ms/10ms.
2. Add missing packetqueue.notify_all() calls after setting
packet->analyzed (Monitor::Analyse) and packet->decoded
(DECODING_NONE path in Monitor::Capture) so the event thread's
condition waits actually get signaled.
3. Replace synchronous zmDbDoUpdate() calls in Event::~Event() with
async dbQueue.push(). The two Events UPDATE queries (with Name
fallback logic) are combined into a single query using MySQL IF().
This eliminates blocking DB I/O from the close_event_thread, which
the analysis thread joins on the next closeEvent() call.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When StreamChannel is set to Restream, the JS maps it to the
_ZoneMinderPrimary go2rtc stream. However, that stream is only
registered when RTSPServer is enabled on the monitor. Fall back
to _CameraDirectPrimary when RTSPServer is not enabled.
Pass RTSPServer property through monitorData in all views so the
JS can check it at stream selection time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The monitorStatus HTML was conditionally omitted based on
ZM_WEB_COMPACT_MONTAGE, leaving the status position dropdown
with no elements to operate on. Always render the HTML and
use the dropdown's hidden option to handle compact montage
instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When SaveJPEGs is enabled, sendFrame() reads JPEG files directly from
disk and never touches the FFmpeg input. However, loadEventData()
unconditionally called FFmpeg_Input::Open() on the event video file,
which runs avformat_find_stream_info() — a 2-5 second probe that was
pure overhead for JPEG-based streaming.
Gate the Open() call on !(SaveJPEGs & 1) so the expensive probe is
only performed when frame extraction from the video container is
actually needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace delete/new Image cycle with lazy-alloc + Assign(). When the
buffer already exists (every frame after the first), Assign() detects
matching dimensions and does a plain memcpy into the existing
allocation, eliminating an aligned malloc+free of ~2 MB per zone per
analyzed frame.
With 4 zones at 15 fps this removes 60 alloc/free cycles per second
from the analysis hot path. The HighlightEdges code path (analysis
images) still allocates a new Image and deletes the old diff buffer,
which is correct — the next Assign() will reallocate once to restore
the single-channel format, then resume reuse.
Behaviorally equivalent: Zone dimensions are constant during zone
lifetime, the destructor already handles cleanup via delete image,
and the only external consumer (Monitor::Analyse → AlarmImage →
Overlay) reads the image without storing pointers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
start() reconstructs the zms URL from url_to_zms, which only contains
the monitor id. Parameters like auth, connkey, scale, and mode are
carried over, but maxfps was dropped. This causes streams to start at
the camera's native capture rate even when PHP rendered the initial
<img src> with a maxfps value (e.g. from the montage Rate cookie).
Carry over maxfps from the existing src the same way the other
parameters are preserved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The jump-ahead heuristic used 10*time_base.den*frame->duration which for
a typical stream (time_base=1/90000, 30fps duration=3000) produced a
threshold of ~30000 seconds, effectively never triggering. This caused
large seek jumps to decode every intermediate packet sequentially.
Replace with av_rescale_q-based 5-second threshold that correctly
converts to stream time_base units regardless of the time_base format.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When mSecondFormatContext is set but no audio stream was found during
PrimeCapture, mAudioStream is null. Dereferencing mAudioStream->time_base
causes a segfault (SIGSEGV at fault address 0x20). Add mAudioStream to
the condition check, consistent with the existing null guard on line 172.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace two linear scans in event playback seeking:
- seek(): O(n) walk from frame 0 replaced with std::lower_bound on
timestamp. Falls back to last frame if target is past end.
- processCommand(CMD_SEEK): O(n) estimate-then-walk replaced with
std::upper_bound on offset, then step back one. Handles edge cases
where offset lands before first frame or past last frame.
For a 10-minute event at 30fps (~18,000 frames), worst case drops
from 18,000 comparisons to ~14 (log2).
When zms dies (any cause), the UI sets zmsBroke=true and subsequent
seeks/plays silently fail. Instead, restart the stream by resetting
the img src, which triggers the browser to make a new CGI request to
zms with the same connkey, spawning a fresh process.
Uses img.onload as the readiness signal rather than polling: when the
new zms delivers its first MJPEG frame, onload fires, confirming the
command socket is ready. This eliminates the race window where
CMD_SEEK could be sent before the socket exists, and removes
dependency on the poll interval (streamTimeout) for seek delivery.
Implementation:
- restartZmsStream(onReady): shared helper that clears img.src to
kill the old connection, sets a one-shot onload handler, then sets
the new src. onload clears zmsBroke and invokes the callback.
- streamSeek(): when zmsBroke, calls restartZmsStream with a callback
that re-invokes streamSeek(offset) once zms is alive.
- playClicked(): when zmsBroke, calls restartZmsStream with no
callback since zms starts in play mode by default.
Consolidates duplicate restart logic that was previously inlined in
both playClicked() and streamSeek().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When checkEventLoaded pauses at the end of an event in MODE_SINGLE/
MODE_NONE, the main loop relies on last_frame_sent to decide when to
send keepalive frames. If last_frame_sent is stale, the loop waits up
to 5 seconds (MAX_STREAM_DELAY) before sending anything. During that
gap the HTTP connection can time out, causing SIGPIPE on the next write.
Reset last_frame_sent to epoch so the next iteration sends a keepalive
immediately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WaitForMessage used WSA Action "PullMessageRequest" while Subscribe's
initial PullMessages used "PullPointSubscription/PullMessagesRequest".
The wrong action caused some cameras to return a generic "not authorized"
fault instead of a proper ActionNotSupported error. Also throttle the
auth error to log once as Error then demote to Debug on repeats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When SSL certificate verification fails for Go2RTC, RTSP2Web, or Janus
curl requests, the warning was logged on every single call. Since these
methods are called periodically, this flooded the logs. Now a
ssl_verification_failed flag is set on first failure so subsequent calls
skip verification silently. Also adds SSL verification to Janus which
previously had none.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 'events' command to check if an ONVIF camera supports the events
service. Returns "Events: yes" or "Events: no" for easy parsing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>