- Wrap all GSOAP-dependent code in a single #ifdef WITH_GSOAP block
- Move GSOAP-specific members and methods inside the conditional
- Provide minimal stubs (constructor, destructor, start) for non-GSOAP builds
- Remove unnecessary stub methods for private members that can't be called
- Code now compiles cleanly with or without GSOAP support
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Monitor::Decode():
- Reorganize into 5 clear phases with descriptive comments
- Phase 1: Receive decoded frame from decoder
- Phase 2: Get and send new packet to decoder
- Phase 3: Convert decoded frame to Image
- Phase 4: Prepare Y-channel for analysis
- Phase 5: Process RGB image (deinterlace, rotate, privacy, timestamp)
- Extract applyOrientation() and applyDeinterlacing() helper functions
- Keep slow send_packet detection timing for diagnostics
PacketQueue locking fixes:
- Move lock acquisition before accessing shared state in queuePacket()
- Keep lock held while iterating in stop()
- Add lock to addStream()
- Remove duplicate packet_counts allocation in clear()
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cameras like Reolink send alarm=true but never send the corresponding
false, causing alarms to stick indefinitely. Use the TerminationTime
from PullMessagesResponse to auto-expire stale per-topic alarms.
- Add AlarmEntry struct with value and termination_time fields
- Extract TerminationTime from each PullMessagesResponse and attach
it to alarm entries; refresh on re-trigger so active alarms persist
- Sweep expired alarms after processing messages and on poll timeout
- Add expire_alarms option (default: true) to disable via onvif_options
- Fix TOCTOU race: remove unsynchronized alarms.empty() check before
acquiring mutex in the timeout sweep path
- Simplify SetNoteSet with C++17 structured bindings
- Add Catch2 tests for alarm expiry logic (mirrored struct to avoid
gSOAP header dependency)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, when plain UsernameToken authentication succeeded after
digest auth failed, the code would skip the subscription setup code
(initial PullMessages, renewal tracking, initial Renew). This was
because the success path only executed inside an else block that
plain auth success didn't reach.
Restructure attempt_subscription() so both success paths (first attempt
and plain auth retry) fall through to common success handling code:
- Initial PullMessages to clear queued messages
- Update renewal tracking times
- Perform initial subscription renewal
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>