transport_conn_close previously called close(conn->fd), but the detached read thread also calls close(conn->fd) when it exits. If the kernel reused the fd number before the read thread ran, the thread's close() would hit the new connection — explaining connections that appeared to not terminate. Fix: use shutdown(SHUT_RDWR) instead. This signals EOF to the remote end and unblocks the blocked read() without releasing the fd. The read thread remains the sole owner of the fd and is the only one to call close(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
video-setup
A graph-based multi-peer video routing system written in C11. Nodes are media processes connected by typed transport edges; the graph carries video streams between sources, relays, and sinks with priority as a first-class property — so a low-latency monitoring feed and a high-quality archival feed can coexist on the same source.
Designed to run on resource-constrained hardware (Raspberry Pi capturing raw MJPEG, forwarding over TCP) alongside more capable machines that handle processing, relay, display, and archival.
Documentation
- architecture.md — system design: graph model, transport protocol, relay design, codec layer, discovery, multi-site plan, device resilience, X11 integration
- docs/protocol.md — wire protocol reference: frame format, all message types, payload schemas, stream lifecycle, discovery
- planning.md — module build order and current status
- conventions.md — C code and project conventions
CLI Tools
- docs/cli/media_ctrl_cli.md — media device and topology tool
- docs/cli/v4l2_ctrl_cli.md — V4L2 camera control tool
test_image_cli— generate test patterns and write PPM output for visual inspectionxorg_cli— display a test pattern in the viewer window; exercises scale/anchor modes and text overlaysv4l2_view_cli— live camera viewer; auto-selects highest-FPS format, displays FPS overlaystream_send_cli— capture MJPEG from V4L2, connect to a receiver over TCP, stream VIDEO_FRAME messages with per-stream statsstream_recv_cli— listen for incoming TCP stream, display received MJPEG frames with fps/Mbps overlayreconciler_cli— simulated state machine experiment; validate the reconciler with fake resources before wiring into the nodecontroller_cli— interactive REPL; connects to a running node; supports enum-devices, enum-controls, get/set-control, start/stop-ingest, start/stop-display
Structure
src/modules/ C modules, one directory each
include/ public headers
dev/cli/ CLI driver programs for each module
dev/web/ development web UI (Node.js/Express) — connects to live nodes for V4L2 inspection and control
tools/ build-time code generators (e.g. gen_font_atlas — bitmap font atlas for xorg text overlays)
docs/ documentation
Status
Core modules and the video node binary are working end-to-end. The node can be queried over the wire protocol for device enumeration and V4L2 camera control. V4L2 ingest is live — a source node captures MJPEG and streams it to a sink node which displays it in an xorg window. The node supports both source (START_INGEST) and display sink (START_DISPLAY) roles. A reconciler manages V4L2 device and transport connection state. The development web UI connects to live nodes for inspection and control. Relay, archive, and codec have not started.
| Module | Status | Notes |
|---|---|---|
common |
done | Error types, base definitions |
config |
done | INI file loader, schema-driven defaults, typed getters |
media_ctrl |
done | Media Controller topology, pad format config |
v4l2_ctrl |
done | V4L2 control enumeration, get/set |
serial |
done | Little-endian binary serialization primitives |
transport |
done | Framed TCP stream, single-write send |
discovery |
done | UDP multicast announcements, peer table, found/lost callbacks |
protocol |
done | Typed write/read functions for all message types |
test_image |
done | Test pattern generator — colour bars, luminance ramp, grid; YUV420/BGRA output |
xorg |
done | GLFW+OpenGL viewer sink — YUV420/BGRA/MJPEG input, all scale/anchor modes, bitmap font atlas text overlays; screen grab and XRandR queries not yet implemented |
reconciler |
done | Generic wanted/current state machine reconciler — BFS pathfinding, event + periodic tick |
ingest |
done | V4L2 capture loop — open device, negotiate MJPEG, MMAP buffers, capture thread with on_frame callback |
node |
done | Video node binary — source role (START/STOP_INGEST) and display sink role (START/STOP_DISPLAY); multi-window xorg viewer; declarative reconciler for device and connection state |
dev/web |
done | Development web UI — connects to live nodes, V4L2 inspection and control |
frame_alloc |
not started | Per-frame allocation with byte budget and ref counting |
relay |
not started | Input dispatch to output queues (low-latency and completeness modes) |
archive |
not started | Write frames to disk, control messages to binary log |
codec |
not started | Per-frame encode/decode (MJPEG, QOI, ZSTD-raw, VA-API H.264) |
web node |
not started | Node.js peer — binary protocol socket side + HTTP/WebSocket to browser |