The cached snapshot was framed when the status last changed, so after a
generation bump or further events a new consumer received it stamped
with a stale generation and an old event-sequence baseline. Keep only
the body and frame it in AcceptClient, so the header carries the
generation and sequence in effect at the moment of connection.
Tests: a snapshot cached at generation 0 with no events is delivered to
a later consumer with the current generation and sequence.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two problems with announcing streams one at a time. SetAudioParams only
bumped the generation when audio had been announced before, so audio
joining a video-only stream was appended to the current generation after
its video HELLO, which the protocol says is the last HELLO of a
generation. And a re-prime that changed both streams bumped twice:
consumers saw, and a connecting consumer was handed, an intermediate
generation pairing the new audio with the old video, which
zm_rtsp_server built a session for and tore down again.
Add StreamSocket::SetStreams(video, audio), which applies the whole set
under one lock: unchanged is a no-op, the first announcement stays in
generation 0, and any change once a video HELLO has gone out - new
parameters, a stream appearing, a stream disappearing - is exactly one
bump with every remaining stream re-announced, audio first. The single
stream setters and the new ClearVideoParams are wrappers over the same
logic, and a null or codec-less parameter set means "no such stream".
PrimeCapture passes both streams in one call.
Tests: audio joining an announced video stream bumps the generation;
SetStreams keeps the initial announcement in generation 0, changes both
streams in one generation with consistent pairing, and drops a video
stream the source no longer has.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Correct three problems in the stream socket generation tracking added by
the previous review fix-up:
- ClearAudioParams bumped the generation without restarting the video
sequence or dropping the cached keyframe, so a late joiner received a
HELLO at generation N+1 followed by a KEYFRAME still stamped N, and
sequences did not restart as documented. It now does the same full bump
as SetVideoParams/SetAudioParams.
- zm_rtsp_server rebuilt the xop session whenever the generation changed,
even with identical codec parameters. Generations restart at 0 when zmc
restarts, so every zmc restart dropped the RTSP clients; the original
code kept the session in that case. Unchanged parameters now just adopt
the new generation without a teardown.
- Deciding whether audio belongs to the current generation by comparing
per-stream generation numbers raced the two HELLOs of a generation
(double rebuild when Update() ran between them) and cannot tell a
producer restart apart. The producer now sends the audio HELLO before
the video HELLO within a generation (on connect and on every bump, and
PrimeCapture announces audio before video), so the video HELLO always
completes a generation's parameter set. The consumer forgets the
previous generation's HELLOs when a new generation starts and on
disconnect, and builds only once the video HELLO of the latest
generation is in. It also records whether audio was announced at build
time rather than whether a packer was created, so an unsupported audio
codec no longer triggers a rebuild on every pass.
The ordering guarantee is documented in the protocol header and the
stream socket docs. Tests pin the audio-first order on connect and on a
video reconfigure, and the keyframe drop and sequence restart on audio
removal.
refs #5143
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4UcdJLt1bxwdpcigGxZRD
- Describe pts_us as signed microseconds with AV_NOPTS_VALUE for unknown.
- Document that the socket path is published in the shared-memory
stream_socket_path field, not only derivable from the convention.
- Clarify the snapshot event's sequence: it equals the next EVENT's
sequence, and the snapshot is a state message a consumer must not treat
as a duplicate of that following EVENT.
refs #5143
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4UcdJLt1bxwdpcigGxZRD
The MEDIA description promised Annex B for H.264/H.265, but zmc forwards
packets as the camera's demuxer produced them: RTSP sources give Annex B,
MP4/MKV files give AVCC length-prefixed NALs. Say so, explain how a
consumer tells them apart from the HELLO extradata, and note that
zm_rtsp_server handles Annex B only. Also document that image-only
cameras announce no media stream, that the keyframe cache is cleared
when the camera closes, that the uid check uses getpeereid off Linux,
and that the C++ consumer now surfaces EVENT frames.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Wire the monitor lifecycle events into zmc. Monitor::SetState() replaces
the scattered shared_data->state assignments in Analyse() and connect(),
publishing a state_changed event (with previous and new state id and name)
whenever the analysis state actually changes. Monitor::SendStreamHealthEvent()
emits the capture-fault edges and tracks the active fault for the snapshot;
the snapshot is refreshed on every state or health change and after a
successful prime, so a consumer connecting mid-fault learns current status.
zmc's capture loop emits the six health transitions once per edge:
connection failed/restored around connect(), prime_capture failed/restored
around PrimeCapture(), capture_failed on pre/capture/post failure, and a
single capture_resumed once the pipeline recovers. Because the stream
socket survives camera reconnects, these are observable exactly when media
has stopped. A small mutex guards the health fields shared between the
capture thread (health events) and analysis thread (state events).
Documents the EVENT frame in docs/stream_socket.rst and decodes it in
tools/zm_stream_socket_dump.py. Full build and ctest pass (121 tests).
refs #2875
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds docs/stream_socket.rst covering the socket path convention, access
control and queue tuning settings, and the version 1 wire protocol
(header layout, HELLO TLVs, MEDIA/KEYFRAME/STATS/BYE semantics,
sequence/generation loss and discontinuity signalling), with pointers
to the reference encoder/decoder, the C++ consumer class and the python
dump tool. Linked from the documentation index.
This also serves as the migration reference for external consumers of
the removed media FIFOs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>