Commit Graph
7 Commits
Author SHA1 Message Date
Steve GilvarryandClaude Fable 5.1 3d98b216ca fix: frame the stream socket snapshot when a consumer connects
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>
2026-09-23 15:07:15 +10:00
Steve GilvarryandClaude Fable 5.1 7d9d97f387 fix: apply both stream parameter sets to the stream socket in one generation
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>
2026-09-23 15:07:14 +10:00
Claude 21042ba2f7 fix: send audio HELLO first and keep rtsp sessions across a zmc restart
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
2026-09-20 00:36:35 +00:00
Claude 2cb01222b0 docs: update stream socket protocol notes from PR review
- 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
2026-09-19 23:00:30 +00:00
Steve GilvarryandClaude Fable 5.1 922f3aeb39 docs: describe stream socket NAL framing, keyframe cache and consumer events
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>
2026-09-16 05:12:43 +10:00
SteveGilvarryandClaude Fable 5.1 0a392802f8 feat: emit capture-fault and state-change events on the monitor stream socket
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>
2026-09-16 05:02:24 +10:00
SteveGilvarryandClaude Fable 5 8b6d8c4179 docs: document the monitor stream socket and its wire protocol
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>
2026-09-16 05:02:24 +10:00