feat(zmnext): drive and ingest the per-monitor zm-next worker#9
Merged
Conversation
Wire zm-api up to orchestrate zm-next (zm-core) over the existing per-monitor stream-socket protocol. Off by default ([zmnext].enabled), reversible per camera; zero behaviour change until enabled. - protocol: add EVENT (0x06), StreamId::Monitor, MonitorEvent + parse_event (skip-on-unknown). Reader routes EVENT to SocketEvent::MonitorEvent and skips media/HELLO on the monitor stream; media path unchanged. - router: MonitorEventEnvelope + set_event_sink; reader forwards EVENTs to a bounded ingest channel (try_send, never stalls media). - ingest: per-monitor session state machine maps detection/description/ recording_saved onto Events/Frames rows (service/zmnext). - daemon: spawn/supervise the zm-core worker per monitor (stable id `zm-core --monitor-id N`, SIGTERM stop, reload = regen pipeline + restart), branched into start/stop/reconcile; zm-core whitelisted in spec validation. - pipeline: generate capture_rtsp_multi -> decode_detect -> store_event JSON from Monitors + Zones. - flag: repo::monitors::use_zmnext reads UseZmNext in isolation, degrading to legacy on a missing column (fork migration pending); entity not widened. - config: [zmnext] section; AppState wires the ingest task + daemon runtime. - docs/ZMNEXT_TASKS.md: runbook + open coordination items. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make zm-api own the event id end to end and hand it to zm-next's store_event at clip-open, so clips land natively in ZoneMinder's tree with no schema change and no file relocation. - protocol: EVENT_RECORDING_OPENING (0x0304), MSG_TYPE_COMMAND (0x11), and build_control_message (client->server encoder on the Monitor stream). - reader: split the connection into read/write halves; take_writer() hands the write half to the router task so replies don't contend with the read loop. - router: ControlReply (best-effort send_command_json) carried on every MonitorEventEnvelope; reader inner loop is now a biased select! that drains queued replies and writes them on the same connection. Real-socket round-trip test added. - ingest: recording_opening allocates/adopts the Events row, computes the Medium-scheme dir + video_name from the resolved storage path, sets Scheme/DefaultVideo, and replies assign_recording; recording_saved finalizes by the echoed event_id. detail gains RecordingOpeningDetail + event_id. - docs: ZMNEXT_TASKS.md specifies the 3-step store_event contract the fork must implement (recording_opening -> assign_recording (stage-then-rename) -> echo event_id in recording_saved). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thank you for your pull request. Before it can be merged, please agree to the Contributor License Agreement. I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
…EventClip fields Apply the zm-next deltas: the two recorders merged into one mode-switched `store` plugin, and the EVENT contract gained trigger/cause + richer EventClip. - pipeline: emit a single `store` plugin with a `mode` derived from the monitor function (Record->continuous, Mocord->both, Modect/Nodect->event; default continuous). continuous/both emit max_secs; event/both emit pre/post-roll, max_buffer_sec, trigger_types. New StoreMode enum + config tunables. Documents the same-filesystem requirement for the assigned `dir` vs store `root`. - ingest: set event Cause from the recording_opening `trigger` (continuous->Continuous, motion->Motion, detection/tracked_detection-> Detection, audio_event->Audio, else passthrough). recording_saved now treats event_id 0/absent as unassigned (assigned_event_id()) and falls back to the open session or a fresh row, carrying the EventClip `cause`. duration stays seconds. - detail: RecordingSavedDetail gains `cause` + assigned_event_id(); doc updates for the merged `store` plugin and the recording_opening trigger semantics. - docs: ZMNEXT_TASKS.md gains the store-mode mapping table, the same-filesystem note, and the updated recording_opening / EventClip JSON shapes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per zm-next: EVENTs (triggers, assign_recording) flow on a process-global bus independent of tree position, while tree position decides which FRAMES a plugin sees. Under decode_detect, store would record the low-res substream; as a child of capture it records the captured main stream and still receives event-mode triggers over the bus. output_mqtt likewise becomes a sibling. - pipeline: decode_detect, store, output_mqtt are now siblings under the capture root (was store/mqtt nested under decode_detect). Module doc + tests updated to the sibling layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wire zm-api to orchestrate zm-next (
zm-core) over the existing per-monitor stream-socket protocol. Off by default ([zmnext].enabled), reversible per camera; zero behaviour change until enabled.What's here
Monitorstream parsingstreaming/source/protocol.rs,stream_socket.rsstreaming/source/router.rsservice/zmnext/{detail,ingest}.rsdaemon/manager.rsservice/zmnext/pipeline.rsUseZmNextflag (graceful)repo/monitors.rs::use_zmnext0x11/0x0304, reader write-half, routerControlReply, ingestconfigure/zmnext.rs,server/state.rs,docs/ZMNEXT_TASKS.mdDesign highlights
Monitorstream routes to ingest; WebRTC/HLS/MSE consume only Video/Audio as before. Unknown types/tags skip per the additive protocol.store_eventat clip-open via a0x11 Command, so clips land natively in ZM's tree — no schema change, no file relocation.recording_savedfinalizes by the echoedevent_id.UseZmNextflag is graceful. Read via an isolated query that degrades tofalse(legacy) on a missing column, so this code is inert until the ZoneMinder fork ships the column — and activates automatically once it exists. ThemonitorsSeaORM entity is deliberately not widened.ManagedProcesssupervision (SIGTERM stop, backoff, watchdog, reconcile); "reload" = regenerate pipeline JSON + restart. Flipping the flag swaps daemons cleanly.Tests / gates
cargo fmtclean ·cargo clippy --all-targets --all-features -D warningsclean · 572 lib tests pass (incl. EVENT parsing, the control round-trip over a real socket, pipeline generation, ingest helpers).Cross-repo coordination (not in this PR)
Monitors.UseZmNext TINYINT NOT NULL DEFAULT 0— ZoneMinder fork migration. Until it lands, everything stays legacy.store_eventhandshake — zm-next must emitrecording_opening, consumeassign_recording(stage-then-rename), and echoevent_idinrecording_saved. Full contract indocs/ZMNEXT_TASKS.md.🤖 Generated with Claude Code