- controller_cli: drain semaphore and reset pending_cmd in do_connect
so stale posts from old connection don't unblock the next command
- protocol: add Proto_Display_Device_Info; extend
proto_write_enum_devices_response and proto_read_enum_devices_response
with display section; backward-compatible (absent in older messages)
- node: handle_enum_devices snapshots active Display_Slots under mutex
and includes them in the response
- controller_cli: on_display callback prints display window info in
enum-devices output
- query_cli: updated to pass NULL on_display (no display interest)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace double wall-time with uint64_t monotonic milliseconds for
last_frame_ms and last_no_signal_ms. Integer ms is the right type
for a threshold comparison — no floating point needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The loop runs at ~200Hz; frames arrive at ~30fps. Most iterations have no
pending frame even during active streaming, so no-signal was rendering
between real frames. Fix: track last_frame_t and suppress no-signal while
a live stream is present (< 1s since last frame).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLOCK_MONOTONIC returns seconds since boot (~50000+s on a running system).
At that magnitude, float32 loses fractional precision in the hash function
and all cells evaluate to near-zero, producing a black screen instead of noise.
Wrapping to fmod(now, 1000.0) keeps the value small enough for the shader.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a viewer window has no incoming stream, renders animated analog-TV
noise (hash-based, scanlines, phosphor tint) at configurable fps (default
15) with a centred "NO SIGNAL" text overlay.
- xorg: FRAG_NOSIGNAL_SRC shader + xorg_viewer_render_no_signal(v, time, noise_res)
- main: Display_Slot gains no_signal_fps + last_no_signal_t; display_loop_tick
drives no-signal render on idle slots via clock_gettime rate limiting
- protocol: START_DISPLAY extended by 2 bytes — no_signal_fps (0=default 15)
+ reserved; reader is backward-compatible (defaults 0 if length < 18)
- controller_cli: no_signal_fps optional arg on start-display
- docs: protocol.md updated with new field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 'force' phony prerequisite to all sub-make delegation rules in
dev/cli/Makefile and src/node/Makefile so the sub-make is always
invoked and can check source timestamps itself; previously a stale
.o would never be rebuilt by a dependent Makefile
- Move stream_stats_record_frame inside the successful send branch in
on_ingest_frame so stats reflect actual delivered frames rather than
capture throughput; avoids misleading Mbps readings when the
transport is disconnected
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each ingest stream gets two reconciler resources (device, transport) with
dependencies: transport waits for device OPEN (needs format for STREAM_OPEN),
device waits for transport CONNECTED before starting capture.
START_INGEST sets wanted state and triggers a tick; the reconciler drives
device CLOSED→OPEN→STREAMING and transport DISCONNECTED→CONNECTED over
subsequent ticks. STOP_INGEST reverses both.
External events (transport drop, ingest thread error) use
reconciler_force_current to push state backward; the periodic 500ms timer
thread re-drives toward wanted state automatically.
All 8 stream slots are pre-allocated at startup. on_ingest_frame sends
VIDEO_FRAME messages over the outbound transport connection, protected by
a per-stream conn_mutex.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
src/node/main.c: ctrl_enum_cb was discarding menu_count and menu_items,
causing empty dropdowns for all MENU/INTEGER_MENU controls. Added a
menu item pool (MAX_MENU_POOL=128 items) to Ctrl_Build; the callback now
copies items into the pool and sets menu_count/menu_items on the control.
docs/protocol.md: add missing sections — str8 primitive, ENUM_DEVICES,
ENUM_CONTROLS (with control type/flag tables and menu item notes),
GET_CONTROL, and SET_CONTROL schemas.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Protocol module: framed binary encoding for control requests/responses
(ENUM_DEVICES, ENUM_CONTROLS, GET/SET_CONTROL, STREAM_OPEN/CLOSE)
- video-node: scans /dev/media* and /dev/video*, serves V4L2 device
topology and controls over TCP; uses UDP discovery for peer announce
- query_cli: auto-discovers a node, queries devices and controls
- protocol_cli: low-level protocol frame decoder for debugging
- dev/web: Express 5 ESM web inspector — live SSE discovery picker,
REST bridge to video-node, controls UI with sliders/selects/checkboxes
- Makefile: sequential module builds before cli/node to fix make -j races
- common.mk: add DEPFLAGS (-MMD -MP) for automatic header dependencies
- All module Makefiles: split compile/link, generate .d dependency files
- discovery: replace 100ms poll loop with pthread_cond_timedwait;
respond to all announcements (not just new peers) for instant re-discovery
- ENUM_DEVICES response: carry device_caps (V4L2_CAP_*) per video node
so clients can distinguish capture nodes from metadata nodes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>