Monitor::Pause() reset shared_data->last_write_index to image_buffer_count,
the "nothing written yet" sentinel. Once an OnDemand monitor went to sleep the
last captured image became unreachable, so zms mode=single gave up waiting and
returned "No image available." mode=jpeg worked only because runStream calls
setLastViewed() each iteration, waking capture for a fresh frame.
The zmc OnDemand loop also paused before capturing anything: on a fresh shm
last_viewed is 0, so the first iteration paused a primed camera and no initial
image was ever written for the console thumbnail. The GetLastWriteIndex()
guard that prevented this had been removed because Pause() clobbering the
index made it cycle Pause/Play.
Leave last_write_index alone in Pause() and restore the guard so capture
continues until one image has been written. The index then stays valid, so the
pause is stable and mode=single serves the last captured image.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace numeric ID-based config system with name-based lookup using
std::unordered_map. Config entries now have compiled-in default values,
and only rows where Value != DefaultValue are loaded from the database.
This eliminates the fragile dependency on sequential ID numbers that
required DB regeneration whenever a config entry was added.
The config generator (zmconfgen.pl) now produces three macros:
- ZM_CFG_DECLARE_LIST: declares Config struct members
- ZM_CFG_DEFAULTS_INIT: initializes members to compiled-in defaults
- ZM_CFG_MAP_INIT: registers name-to-member bindings for DB loading
Only daemon-relevant config entries (137 of 245) are included in the
C++ header; web-only settings (WEB_H_*, WEB_M_*, WEB_L_*, skin
defaults, etc.) are excluded.
Also fixes two pre-existing bugs exposed by removing numeric #defines:
- ZM_WATCH_MAX_DELAY was used as Seconds(139) instead of the actual
config value (the 139 was the config table row ID, not seconds)
- ZM_OPT_USE_AUTH evaluated as if(8) (always true) instead of
checking the actual auth setting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 86d4a8e5f0)
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>
avformat_close_input() can block for 75-90s on TCP retransmit timeout
when an RTSP camera becomes unresponsive, and the connect() retry loop
also lacks heartbeat updates. This causes zmwatch to kill zmc with a
stale heartbeat even though the process is actively reconnecting.
Add SetHeartbeatTime() calls before/after Close() and in the connect()
retry loop so zmwatch knows zmc is still alive during reconnection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>