Commit Graph

6450 Commits

Author SHA1 Message Date
Isaac Connor
8b4c93ea1f fix: bail from MonitorStream::runStream when monitor failed to load
When setStreamStart()->loadMonitor() failed (e.g. monitor id not found,
shm not yet mapped), zms continued into runStream() for non-SINGLE
stream types and dereferenced a null Monitor shared_ptr at
monitor->GetFPS(), crashing in get_capture_fps with fault address 0x618.

Bail early after the STREAM_SINGLE branch with a "Not connected" text
frame, mirroring the SINGLE-type recovery. For STREAM_JPEG, emit the
multipart Content-Type first so sendTextFrame produces a well-formed
response. Placed before openComms()/command_processor spawn so there
is no resource teardown to do on the bail path.
2026-04-30 18:11:55 -04:00
Isaac Connor
b2b50aba5d perf: skip clearPackets early-returns and allow drop-to-iterator-keyframe
Two related changes to PacketQueue::clearPackets, called by the analysis
thread on every video packet:

1. Lock-free call-site gate (should_try_clear) on the analysis path.
   In keep_keyframes mode the existing early-return at the top of
   clearPackets discards most non-keyframe video packets after acquiring
   the queue mutex. Add an inline lock-free check at the call site so
   non-keyframe packets skip the mutex acquire entirely. clear_packets_pending_
   is now std::atomic<bool> so it can be read without the lock; a stale
   read is harmless (at worst we make one extra cheap early-returning call).
   The !keep_keyframes path always returns true from the gate because that
   mode pops one packet at a time on every video packet.

2. Iterator boundary in the scan loop changed from >= to >. Setting
   next_front to a packet that an iterator points at is safe because
   clearPackets deletes strictly before next_front, so the iterator's
   own packet stays in the queue. Previously, an event-start (or other)
   iterator landing exactly on a keyframe blocked the leading GOP from
   being dropped until the iterator advanced; now we can include that
   keyframe as next_front while the iterator continues to point at it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 19:39:04 -04:00
Isaac Connor
257ecafa4d fix: clean up ONVIF subscription before re-Subscribe to stop NotAuthorized loop
When PullMessages failed, Run() looped back into Subscribe() and called
CreatePullPointSubscription on the same soap context without unsubscribing
the previous one. Cameras with per-user pull-point caps (Hikvision,
Reolink, etc.) only release a slot when the originating socket closes, so
each cycle leaked a slot until the camera started rejecting new
subscriptions with ter:NotAuthorized. Only a zmc restart (which closed
sockets and ran the destructor) recovered.

- Subscribe() now calls cleanup_subscription() and tears down the soap
  context up front when has_valid_subscription_ is set, forcing a fresh
  TCP connection.
- WaitForMessage()'s failure path calls cleanup_subscription() before
  going unhealthy so the camera-side slot is released as soon as we know
  we're abandoning it.
- try_usernametoken_auth is reset at the start of each subscription cycle
  so a transient plain-auth fallback doesn't pin for the lifetime of the
  process.
- Auth-error detection in CreatePullPointSubscription now checks
  soap_fault_string in addition to soap_fault_detail; the observed fault
  carries the "...not authorized" wording in fault_string with detail=null,
  so the existing plain-UsernameToken fallback never fired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:36:49 -04:00
Isaac Connor
509d8b4d3f feat: add G.711 μ-law (PCMU) audio support to zm_rtsp_server
Mirror the existing pcm_alaw branch with a pcm_mulaw branch that
creates an xop::G711USource. Cameras emitting PCMU audio can now
be re-streamed via the built-in RTSP server.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 23:06:36 -04:00
Isaac Connor
2526b9b85e feat: install SIGPIPE handler in zms for clean browser-disconnect shutdown
When a browser closes a streaming connection, fwrite to stdout raises
SIGPIPE.  Without a handler the default action terminates the process
immediately, skipping exit_zm() and leaving the DB handle and log
unclosed.

Add zm_pipe_handler that sets zm_terminate so zms falls out of its
streaming loop and exits through the normal shutdown path.  Clarify
the sendFrame comment to match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 09:53:23 -04:00
Isaac Connor
9aa2f2c306 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-04-16 15:37:16 -04:00
Isaac Connor
aa5e493694 perf: default ffmpeg decoder thread_count to 2
libavcodec's avcodec_alloc_context3 leaves thread_count at 1, so
software 1080p H.264 decoding hits ~60ms/frame and saturates a core
per camera.  Default to 2 frame-threads, which roughly halves per-
frame decode time without oversubscribing systems with many cameras.

User can still override via thread_count in monitor Options (0 = let
ffmpeg auto-detect based on CPU count).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:58:44 -04:00
Isaac Connor
3f8bc81760 fix: HLS fragment tracking, live playback, and fallback handling
Fragment tracking:
- Rewrite detection to use avio_flush()+avio_tell() before each video
  keyframe write, giving exact fragment boundaries. Fixes duplicate
  entries and missing first fragment in the m3u8 manifest.
- Remove unused pending_fragment_dts_ and last_flush_pos_ members.

Live event playback:
- Set DefaultVideo to index.m3u8 at event INSERT time (no DB update
  needed after videoStore opens)
- Rename incomplete file to include codec (incomplete.h264.mp4) so
  canPlayCodec() works during recording
- Rewrite final m3u8 after video file rename to reference final name
- Add live retry handler in event.php: retries all error codes with
  backoff (3s then 5s), max 30 retries, resets on successful playback
- Update progress bar duration from video element for live events

Fallback handling:
- view_video.php: when DefaultVideo is m3u8 and no file param given,
  search event dir for actual mp4 (final name then incomplete)
- view_hls.php: reject m3u8 with no EXTINF entries (event just started)
- event.php: require m3u8 file to exist on disk before offering HLS,
  fall back to direct MP4 otherwise

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 20:57:46 -04:00
Isaac Connor
0dff9c12b1 Merge remote-tracking branch 'upstream/master' into hls-byte-range-playback 2026-04-12 15:40:45 -04:00
Isaac Connor
e15d2a6427 feat: add HLS byte-range playback for event video
Write a single continuous fragmented MP4 per event and generate an HLS
m3u8 manifest with byte-range references into that file. This enables
seamless browser playback via video.js's built-in http-streaming (VHS)
without needing separate segment files.

C++ changes:
- VideoStore tracks fragment boundaries (moof+mdat byte offsets and
  durations) as packets are written, by monitoring avio_tell() around
  keyframe writes in write_packet()
- Add writeM3U8() method that generates EXT-X-VERSION:7 byte-range
  manifests with EXT-X-MAP for the init segment
- Event writes a live m3u8 (no EXT-X-ENDLIST) on each new fragment
  for in-progress viewing, and a final VOD manifest at event close
- Change movflags to frag_keyframe+empty_moov+default_base_moof
  (default_base_moof required by HLS fMP4 spec, faststart removed
  as it's meaningless with empty_moov)

PHP/web changes:
- New view_hls.php endpoint serves pre-built m3u8 with auth tokens
- event.php detects index.m3u8 and uses HLS as primary source with
  direct MP4 as fallback
- CSRF exemption for view_hls in index.php

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:40:12 -04:00
Isaac Connor
7bcdfae0d8 fix: prevent empty events in ONDEMAND mode and fix VideoStore fd/codec leaks
The ONDEMAND capture mode rapidly cycled between Pause() and Play()
because Pause() resets the write index, making the GetLastWriteIndex()
guard false, which fell through to Play(). This created ~2 empty events
per second. Remove the write index guard so monitors stay paused when
nobody is watching.

In VideoStore, fix three resource management issues:
- Free the codec context opened in the PASSTHROUGH+new_extradata path
  immediately after extracting stream parameters, preventing flush_codecs
  from crashing on an encoder that never received frames.
- Clean up video_out_ctx, opts dict, and hw_device_ctx when
  setup_hwaccel() fails, preventing fd accumulation.
- Track whether frames were actually sent to the encoder and skip
  flush_codecs when none were, avoiding segfaults in avcodec_send_frame.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:40:26 -04:00
Isaac Connor
83ed47abf4 fix: scan-line polygon fill incorrectly filled non-convex polygon gaps
Image::Fill(Polygon) implements scan-line polygon fill but iterated
through active edges one at a time instead of in pairs. For convex
polygons (always exactly 2 active edges per scan line) this happened
to work, but for non-convex polygons it would fill the gaps between
concave sections.

A banana-shaped zone, for example, would have its inner concave area
incorrectly marked as inside the zone, causing motion detection to
trigger on the area the user explicitly drew the zone to avoid.

Fix by stepping the iterator by 2 to fill between pairs of edges
following the standard parity rule for scan-line polygon fill.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:33:52 -04:00
Isaac Connor
1e4ec3d251 fix: treat ENOTTY like EINVAL when querying V4L2 JPEG compression options
Some V4L2 drivers return ENOTTY (rather than EINVAL) when VIDIOC_G_JPEGCOMP
is unsupported. Treat both as "feature unavailable" and log at debug level
instead of warning, and include the errno string for clarity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 20:48:57 -04:00
Isaac Connor
0e51fb6788 fix: call disconnect() in MonitorLink::connect() before reopening map_fd
MonitorLink::connect() opened a new map_fd on each invocation without
closing any previously-opened one. Token::score() in
zm_monitorlink_token.h calls connect() on every analysis cycle when the
linked monitor is unavailable, causing rapid file descriptor
accumulation and eventual "Too many open files" errors in zmc.

Call disconnect() first to release any prior map_fd, mmap, and shared
state before re-establishing the link.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 20:48:42 -04:00
Isaac Connor
d22f8450e0 fix: free video_out_ctx after failed avcodec_open2 in new_extradata path
When avcodec_open2 failed in the new_extradata code path,
video_out_ctx was left allocated but half-initialized. Later
flush_codecs() would call avcodec_send_frame on this context, causing
a segfault (null deref at offset 0x28, likely ctx->internal). Free
the context on failure and guard the subsequent
avcodec_parameters_from_context call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:30:18 -04:00
Isaac Connor
5ff625a899 fix: fd leak in MonitorLink::disconnect() when connect() fails
disconnect() was guarded by `if (connected)`, but connected is only
set to true as the last step of a successful connect(). Every error
path in connect() called disconnect() before connected was true, so
map_fd was never closed and mmap was never unmapped. Each failed
connect attempt leaked one fd, eventually causing "Too many open
files" errors when opening new events.

Fix by cleaning up based on actual resource state (map_fd >= 0,
mem_ptr != nullptr) instead of the connected flag. Also fix
MAP_FAILED check, null out derived pointers, and fix shm_id being
zeroed before its IPC_RMID call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:58:52 -04:00
Isaac Connor
33a2148c7c Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-04-02 14:17:21 -04:00
Isaac Connor
af33926aa3 fix: reset last_write_index in Pause to restore DECODING_ONDEMAND bootstrap
Pause() did not restore last_write_index to the sentinel value
(image_buffer_count). After a Pause/Play cycle, the DECODING_ONDEMAND
fallback condition (last_write_index == image_buffer_count) was dead,
making decoding depend entirely on hasViewers(). This created a timing
gap where the decoder skipped packets after Play before zms called
setLastViewed, causing the decoder to fall behind capture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:45:08 -04:00
Isaac Connor
1ec9c449b4 fix: null-check StorageId column in Monitor::Load to prevent segfault
dbrow[col] for StorageId can be NULL when the database column contains
a NULL value. Passing NULL to atoi causes a segfault in strtol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:13:50 -04:00
Isaac Connor
cf27243055 fix: add DTS backward jump detection in ffmpeg camera capture
The existing PTS backward jump check in FfmpegCamera::Capture() does not
catch cases where DTS jumps back significantly (e.g. stream restarts,
encoder resets) while PTS may remain unaffected. This causes the
VideoStore to spend minutes forcing DTS monotonicity on every packet via
the write_packet fixup path, flooding logs with warnings.

Add per-stream DTS tracking (mLastVideoDTS/mLastAudioDTS) and a backward
jump check mirroring the existing PTS check: if DTS jumps back more than
10 seconds, increment error_count and after 6 occurrences return -1 to
trigger capture reconnection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:52:00 -04:00
Isaac Connor
0e78b5a6c6 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-03-26 17:20:23 -04:00
Isaac Connor
277eece633 Log the value when event_close_mode is unkown 2026-03-26 17:19:59 -04:00
Isaac Connor
8fe8141eb5 fix: prefer MJPEG over raw YUV in V4L2 AUTO palette selection
Move MJPEG/JPEG ahead of YUYV/UYVY in the preferred format arrays
for all color spaces. MJPEG uses far less USB bandwidth than raw YUV
while the decode cost is minimal (sw MJPEG decoder).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 14:01:14 -04:00
Isaac Connor
9adbed166e fix: add missing CLOSE_DURATION event close mode handling
- Add "duration" string mapping in constructor and DB loader — was
  silently falling back to CLOSE_IDLE
- Add CLOSE_DURATION handler in analysis logic: close event when
  duration >= section_length regardless of alarm state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 14:01:14 -04:00
Isaac Connor
5561829450 fix: include username in auth relay and fix stale auth in stream restart
- Add user= parameter to get_auth_relay() so zms can use the indexed
  Username column instead of iterating all users to validate the hash
- Apply the same fix to Event.php getStreamSrc() and getThumbnailSrc()
- Tighten Monitor.php from isset() to !empty() for consistency
- In MonitorStream.js start(), check if the auth hash in the img src
  matches the current auth_hash before resuming via CMD_PLAY. If stale,
  fall through to rebuild the URL with fresh auth_relay. This prevents
  long-running montage pages from spawning zms with expired credentials.
- Downgrade zms auth failure from Error to Warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 10:01:47 -04:00
Isaac Connor
4356a6dea8 fix: add event-id latch to linked monitor score detection
Linked monitor alarm detection was purely point-in-time: it only
returned a score when the linked monitor's state was exactly ALARM at
the instant of the check. If the linked monitor's entire alarm cycle
(ALARM -> ALERT -> IDLE) occurred between two analysis cycles of the
checking monitor, the alarm was missed entirely.

Add a latch using last_event_id: if the linked monitor's event id has
changed since the last check, a new event started and we return a
score even if the ALARM state has already passed. Also remove dead
code in hasAlarmed() where last_event_id was updated after an early
return.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:40:26 -04:00
Isaac Connor
f017c72498 fix: reduce linked monitor reconnect throttle from 60s to 1s
The 60-second reconnect throttle meant that any brief disconnection
(e.g., linked monitor restart, momentary shared memory issue) created
a full 60-second window where linked monitor alarms were silently
missed. Reducing to 1 second minimizes this blind spot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:44:05 -04:00
Isaac Connor
29d371313a fix: store zone thresholds as percentages and convert to pixels at runtime
Zone threshold fields (MinAlarmPixels, MaxAlarmPixels, MinFilterPixels,
MaxFilterPixels, MinBlobPixels, MaxBlobPixels) were being corrupted on
every save because the PHP conversion used monitor pixel area instead of
zone pixel area. This caused values to inflate progressively, breaking
motion detection.

The fix changes the storage model: thresholds are now stored as
percentages of zone area (DECIMAL(7,2) columns) matching the percentage
coordinate system from zm_update-1.39.2. The C++ Zone::Load() converts
percentages to pixel counts at runtime using polygon.Area(). Legacy
pixel-coordinate zones pass through unchanged.

JS changes:
- submitForm() converts to percentages when in Pixels display mode
- Init skips applyZoneUnits() since DB values are already percentages
- limitRange() only updates field.value when constrained value differs,
  preventing oninput from stripping decimal points mid-keystroke

fixes #4690

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:16:56 -04:00
Isaac Connor
3dfe185260 fix: resolve role-based group permissions not granting stream access
The C++ User class (used by zms for streaming) had no awareness of
roles. It only checked user-direct permissions from Monitors_Permissions
and Groups_Permissions tables, completely ignoring Role_Monitors_Permissions,
Role_Groups_Permissions, and User_Roles base permissions. This caused
users who received camera permissions via Roles to be denied live stream
access, even though the PHP web interface (which has its own role-aware
checks in visibleMonitor()) showed the monitors correctly.

Changes:
- Add role_id to C++ User class, loaded via COALESCE(RoleId, 0) in all
  SQL queries (find, zmLoadTokenUser, zmLoadAuthUser)
- Add loadRoleBasePermissions() to merge role's Stream/Events/Monitors/
  etc. as fallback when user's own permission is PERM_NONE
- Add findByRole() to Group_Permission and Monitor_Permission classes
  to query Role_Groups_Permissions and Role_Monitors_Permissions tables
- Extend User::canAccess() to check role monitor and group permissions
  after user-direct permissions, matching the PHP visibleMonitor() logic
- Fix Monitor::canView() in PHP to also check role permissions when
  called for a user other than the global $user
- Fix off-by-one in zmLoadTokenUser where dbrow[10] read TokenMinExpiry
  out of bounds (was at index 9); adding RoleId shifts it to index 10

Fixes #4692

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:07:14 -04:00
Isaac Connor
4df2bf4f0b feat: add analysis_image field to stream status for client-side verification
Add a boolean analysis_image field to the CMD_QUERY status response that
reports whether zms is actually sending analysis images (with motion zone
overlays) or regular capture images. This lets MonitorStream.js detect
when the stream state is out of sync with what the client requested and
re-send the CMD_ANALYZE_ON/OFF command to correct it.

The field is true only when frame_type is FRAME_ANALYSIS, shared memory
is valid, and the monitor has analysis enabled — matching the same
condition used to select the image in the streaming loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:44:49 -04:00
Isaac Connor
7d78b722d0 fix: auto-detect zone coordinate format instead of trusting Units field
The zone loader now ignores the Units DB field and detects the coordinate
format by checking for decimal points: decimal values are percentages,
integer-only values are legacy pixels. This fixes motion detection being
broken when zones had Units=Pixels but percentage coordinates (or vice
versa), which resulted in a ~99x99 pixel zone on a 2560x1440 monitor.

The PHP zone view now always forces Units=Percent when saving, since it
always works in percentage space. convertPixelPointsToPercent() now
returns bool to indicate whether conversion occurred.

Tests added for: truncation bug via atoi, correct percentage-to-pixel
conversion, auto-detect heuristic, and resolution independence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:26:36 -04:00
Isaac Connor
d2a26aee59 fix: use +1 instead of +last_duration for equal DTS fixup
When two packets have the same DTS (common with B-frames near
keyframes), bumping by last_duration overshoots and causes a cascading
misalignment where every subsequent packet triggers a "non increasing
dts" warning with a growing gap. The comment already said "add 1" but
the code used last_duration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:32:28 -05:00
Isaac Connor
1b689d6e67 fix: include alarm_frame_count in ready_count calculation
ready_count only considered warmup_count and pre_event_count, but
openEvent walks back max(pre_event_count, alarm_frame_count) frames.
When pre_event_count=0 and alarm_frame_count=2, analysis started before
the queue had enough packets, causing spurious "Hit end of packetqueue
before satisfying pre_event_count" warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:32:28 -05:00
Simpler1
88c179bc9d fix: Handle incomplete.mp4 in cpp file. 2026-03-06 12:33:25 -05:00
Isaac Connor
3cbd67eb4b Merge pull request #4679 from ZoneMinder/copilot/fix-build-failure-zm-no-curl
fix: add HAVE_LIBCURL preprocessor guards to fix ZM_NO_CURL=ON build
2026-03-05 08:31:03 -05:00
copilot-swe-agent[bot]
149eba0d10 refactor: move HAVE_LIBCURL guards inside class definitions as stubs
Instead of wrapping entire class declarations and usage sites in
#if HAVE_LIBCURL, each inner class (AmcrestAPI, RTSP2WebManager,
Go2RTCManager, JanusManager) now has the guard inside the class body
with #else stubs. This makes zm_monitor.cpp completely free of
HAVE_LIBCURL guards.

Stub behaviour when curl is absent:
- isHealthy() returns true (avoids recreation loop in Poll)
- isAlarmed() returns false (no spurious triggers)
- check_*() returns 1 (no-op, avoids re-add loop in Poll)
- all other methods are no-ops / return 0

refs #4967

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-03-05 02:41:06 +00:00
copilot-swe-agent[bot]
2dfb79975d fix: prevent segfault when MPEG stream codec fails to open
Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-03-05 02:15:08 +00:00
copilot-swe-agent[bot]
d9c9b4087e fix: add HAVE_LIBCURL preprocessor guards to fix ZM_NO_CURL=ON build
Wraps all libcurl usage in #if HAVE_LIBCURL / #endif guards, matching
the existing pattern used for HAVE_LIBVLC and HAVE_LIBVNC.

Files changed:
- src/zm_monitor.h: guard curl include, 4 inner class declarations,
  4 manager member pointers, check_janus() decl, EventPollerHealthy()
- src/zmc.cpp: guard curl_global_init/cleanup calls
- src/zm_monitor.cpp: guard initializer list, instantiation, destructor,
  Poll(), Analyse(), Poller thread condition, and Close() teardown
- src/zm_monitor_amcrest.cpp: wrap implementation in HAVE_LIBCURL guard
- src/zm_monitor_janus.cpp: wrap implementation in HAVE_LIBCURL guard
- src/zm_monitor_rtsp2web.cpp: wrap implementation in HAVE_LIBCURL guard
- src/zm_monitor_go2rtc.cpp: wrap implementation in HAVE_LIBCURL guard

Verified: builds succeed with both ZM_NO_CURL=ON and ZM_NO_CURL=OFF

refs #4967

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-03-05 02:13:59 +00:00
Isaac Connor
2b33bdcd5e Fix missing _ 2026-03-04 17:50:35 -05:00
Isaac Connor
f61a2e0a6b Merge branch 'master' into copilot/refactor-filepath-handling 2026-03-04 17:34:44 -05:00
Isaac Connor
f9085b7ae0 fix: add missing unistd.h include in zma.cpp for FreeBSD build
getpid() requires explicit inclusion of <unistd.h> on FreeBSD where
transitive includes from other headers are not guaranteed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:23:38 -05:00
Isaac Connor
5e0ea3160e fix: use portable %jd format for chrono duration count
The chrono duration rep type is long long on some platforms but long on
others. Using %ld causes -Wformat warnings on platforms where it is
long long. Use %jd with static_cast<intmax_t>() for portability,
matching the convention used elsewhere in the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:16:23 -05:00
Isaac Connor
bf82278b6e feat: integrate EventStream into montagereview with zms recovery and fixes
- Integrate EventStream.js into montagereview.js for persistent MJPEG
  streaming in replay mode (speeds >= 1x), falling back to per-frame
  mode=single for speeds < 1x where zms lacks slow-motion support
- Add EventStream recovery logic: detect zms death via AJAX failures,
  img.onerror, and error responses; auto-restart with new connkey and
  exponential backoff (max 5 retries)
- Fix zms crash on bulk/interpolated frames: add stat() check in
  sendFrame() for SaveJPEGs & 1 path, fall through to ffmpeg_input
  when JPEG file doesn't exist on disk; send "No frame available"
  text frame instead of terminating when no source available
- Center monitor canvases: text-align in CSS for scale mode,
  horizontal offset calculation in maxfit2() for fit mode
- Reduce console noise: comment out high-frequency debug logs,
  convert error-path logs to console.warn/error
- Fix ESLint sourceType to "script" for traditional non-module JS
  files, resolving false-positive no-unused-vars on global functions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:14:06 -05:00
Isaac Connor
ffe4858106 Merge branch 'master' of github.com:ZoneMinder/zoneminder 2026-02-26 13:16:56 -05:00
Isaac Connor
2b14e9044e fix: improve montage review playback smoothness and fix video seek overshoot
Fix FFmpeg initial seek overshooting to a future keyframe when the
requested timestamp falls between keyframes. After the initial
AVSEEK_FLAG_FRAME seek, detect if the returned frame is past the
target and re-seek backward to get the correct keyframe before it.

On the JS side, preserve fractional seconds throughout the playback
pipeline: remove Math.floor() truncation in mmove() and setSpeed(),
and use parseFloat instead of parseInt for currentTimeSecs
initialization. Reduce initial display interval from 1000ms to 100ms
for ~10fps refresh rate during review playback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:16:07 -05:00
Isaac Connor
139179ce9a fix: link stdc++fs for std::filesystem on GCC 8 and add zma to RPM spec
GCC < 9 (CentOS/RHEL 8) requires explicitly linking stdc++fs for
std::filesystem support. Add a CMake compile check that detects this
and conditionally links the library for the zma target.

Also add the zma binary to the Red Hat spec file's %files section
so it gets packaged in the RPM.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:02:26 -05:00
Isaac Connor
ce5fb82f08 fix: handle AV_NOPTS_VALUE pts/dts in VideoStore
Some cameras produce packets where both pts and dts are AV_NOPTS_VALUE.
Previously, video passthrough skipped timestamp processing for these,
write_packet set dts to -1 and never updated tracking state,
audio_first_dts could be set to AV_NOPTS_VALUE permanently, and the
reorder queue compared AV_NOPTS_VALUE values meaninglessly.

- Video passthrough: synthesize dts as last_dts+1 when both pts/dts
  are undefined, keeping monotonic ordering without colliding with the
  next valid packet's timestamp
- Audio: guard audio_first_dts from being set to AV_NOPTS_VALUE, and
  guard passthrough subtraction to avoid NOPTS arithmetic overflow
- write_packet: synthesize dts as last_dts+1 instead of reusing stale
  last_dts, fall back to 0 when no history exists, and always update
  tracking state (last_dts/next_dts/last_duration) regardless of
  which branch was taken
- Reorder queue: skip reordering when incoming dts is undefined and
  treat queued NOPTS packets as in-order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:19:20 -05:00
Isaac Connor
635439eb5b feat: add zma utility for offline event re-analysis
Add a new zma binary that re-analyses recorded events using current zone
settings. It decodes frames from stored video (FFmpeg) or JPEG files and
runs the full motion detection pipeline (DetectMotion + ref_image blending
+ state machine).

Two modes of operation:
- Default: updates the original event's motion stats (AlarmFrames,
  TotScore, AvgScore, MaxScore) in the database
- --create-events (-c): creates new events from detected motion regions,
  with video files hard linked (copy fallback) to the new event directory

Additional features:
- --save-analysis (-a): writes analysis JPEGs with zone alarm overlays
  to the event directory for visual inspection
- --monitor (-m): override which monitor's zone config to use
- --verbose (-v): increase debug verbosity

Adds Monitor::AnalyseFrame() public methods that encapsulate DetectMotion
+ ref_image initialization and blending, with an optional analysis_image
output parameter for zone overlay rendering. Also guards shared_data
access in DetectMotion to allow offline use without shared memory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:19:20 -05:00
Isaac Connor
4d40ca4edf fix: add www-data to video group and font path fallback refs #4642
Add www-data to video and dialout groups in Debian/Ubuntu postinst
scripts so zmc can access /dev/video* devices on fresh installs.
RedHat packaging already handled this via gpasswd in %post.

Add compile-time ZM_FONTDIR to zm_config_data.h.in and use it as a
fallback in zm_image.cpp when the configured font_file_location is not
found, fixing font loading failures caused by stale DB config values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:19:20 -05:00
Isaac Connor
4c42e736af fix: reset send_twice flag after use in EventStream::runStream
send_twice was set to true by zoom/pan/scale/seek commands when paused
but never reset to false. Once set, every subsequent frame was sent
twice forever, even after unpausing. This doubled bandwidth usage and
increased exposure to the processCommand race condition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:19:20 -05:00