Commit Graph

21 Commits

Author SHA1 Message Date
835cbbafba Fix connect accumulation; add display sinks to enum-devices
- 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>
2026-03-29 19:48:22 +00:00
ba2c3cb6cd controller_cli: readline, discovery integration, peers/connect commands
- readline replaces fgets — line editing and command history
- Discovery runs at startup (always); discovered peers print inline as they appear
- --host is now optional; without it, starts in discovery-only mode
- New REPL commands:
    peers              list discovered nodes with index
    connect            connect to first discovered peer
    connect <idx>      connect to peer by index
    connect <host:port> connect directly
- connect switching closes the old connection before opening the new one
- Commands that require a connection print "not connected" when conn is NULL
- Makefile: add $(DISCOVERY_OBJ) and -lreadline to controller_cli link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 19:26:38 +00:00
54d48c9c8e Add no-signal animation to display windows
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>
2026-03-29 19:20:53 +00:00
32d31cbd1e Add display sink: START_DISPLAY/STOP_DISPLAY, multi-window xorg, random port
Protocol:
- Add PROTO_CMD_START_DISPLAY (0x000A) and PROTO_CMD_STOP_DISPLAY (0x000B)
  with write/read functions; Proto_Start_Display carries stream_id, window
  position/size, scale and anchor; PROTO_DISPLAY_SCALE_*/ANCHOR_* constants

Node display sink:
- Display_Slot struct with wanted_state/current_state (DISP_CLOSED/DISP_OPEN);
  handlers set wanted state, display_loop_tick on main thread reconciles
- Up to MAX_DISPLAYS (4) simultaneous viewer windows
- on_frame routes incoming VIDEO_FRAME messages to matching display slot;
  transport thread deposits payload, main thread consumes without holding lock
  during JPEG decode/upload
- Main thread runs GL event loop when xorg is available; headless fallback
  joins reconciler timer thread as before

Xorg multi-window:
- Ref-count glfwInit/glfwTerminate via glfw_acquire/glfw_release so closing
  one viewer does not terminate GLFW for remaining windows
- Add glfwMakeContextCurrent before GL calls in push_yuv420, push_bgra,
  push_mjpeg and poll so each viewer uses its own GL context correctly

Transport random port:
- Bind port 0 lets the OS assign a free port; getsockname reads it back
  into server->bound_port after bind
- Add transport_server_get_port() accessor
- Default tcp_port changed from 8000 to 0 (random); node prints actual
  port after server start so it is always visible in output
- Add --port PORT CLI override (before config-file argument)

controller_cli:
- Add start-display and stop-display commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 08:03:21 +00:00
28216999e0 Fix make sub-make staleness and stats delivery accounting
- 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>
2026-03-29 08:03:02 +00:00
a2f438bbbb Add controller_cli — interactive node controller REPL
Connects to a running video node by host:port. Supports:
  enum-devices, enum-controls, get-control, set-control,
  start-ingest, stop-ingest

Uses semaphore-based request/response synchronisation (same pattern as
query_cli). start-ingest maps directly to the new START_INGEST protocol
command with optional format/size/fps args; defaults to auto-select.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 02:22:57 +00:00
639a84b1b9 Add reconciler and ingest modules with CLI driver
reconciler: generic resource state machine — BFS pathfinding from current
to wanted state, dependency constraints, event/periodic tick model.
reconciler_cli exercises it with simulated device/transport/stream resources.

ingest: V4L2 capture module — open device, negotiate MJPEG format, MMAP
buffer pool, capture thread with on_frame callback. start/stop lifecycle
designed for reconciler management. Transport-agnostic: caller wires
on_frame to proto_write_video_frame.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 01:52:17 +00:00
61c81398bb feat: node-to-node MJPEG streaming CLIs and shared V4L2 format header
Add stream_send_cli (V4L2 capture → TCP → VIDEO_FRAME) and
stream_recv_cli (TCP → threaded frame slot → GLFW display) to
exercise end-to-end streaming between two nodes on the same machine
or across the network.

Add include/stream_stats.h (header-only rolling-window fps/Mbps tracker)
and include/v4l2_fmt.h (header-only V4L2 format enumeration shared between
v4l2_view_cli and stream_send_cli). Refactor v4l2_view_cli to use the
shared header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 22:31:54 +00:00
611376dbc1 feat: xorg text overlays, font atlas generator, v4l2_view_cli
- tools/gen_font_atlas: Python/Pillow build tool — skyline packs DejaVu
  Sans glyphs 32-255 into a grayscale atlas, emits build/gen/font_atlas.h
  with pixel data and Font_Glyph[256] metrics table
- xorg: bitmap font atlas text overlay rendering (GL_R8 atlas texture,
  alpha-blended glyph quads, dark background rect per overlay)
- xorg: add xorg_viewer_set_overlay_text / clear_overlays API
- xorg: add xorg_viewer_handle_events for streaming use (events only,
  no redundant render)
- xorg_cli: show today's date as white text overlay
- v4l2_view_cli: new tool — V4L2 capture with format auto-selection
  (highest FPS then largest resolution), MJPEG/YUYV, measured FPS overlay
- docs: update README, planning, architecture to reflect current status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 22:13:59 +00:00
7fd79e6120 feat: xorg viewer scale modes, resize fix, arch notes
Scale modes (STRETCH/FIT/FILL/1:1) with CENTER/TOP_LEFT anchor:
- UV crop via u_uv_scale/u_uv_offset uniforms in vertex shader
- glViewport sub-rect + glClear for FIT and 1:1 modes
- xorg_viewer_set_scale() / xorg_viewer_set_anchor() setters
- Stub implementations for both

Resize fix: glfwSetWindowUserPointer + framebuffer_size_callback calls
render() synchronously during resize so image tracks window edge
immediately. Forward declaration added to fix implicit decl error.

Q/Escape close the window via key_callback.

xorg_cli: --scale and --anchor arguments added.

architecture.md:
- Scale mode table and anchor docs in Frame Viewer Sink section
- Render loop design note: frame-driven not timer-driven, resize callback
  rationale, threading note (GL context ownership, frame queue)
- Text overlay section: tier 1 bitmap atlas (Pillow build tool, skyline
  packing, quad rendering), tier 2 HarfBuzz+FreeType, migration path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 21:30:28 +00:00
a1b52145d0 feat: add test_image module, xorg viewer sink, and feature flag build system
Feature flags (FEATURES= make variable):
  glfw       — GLFW + OpenGL viewer (libglfw3, libglew)
  vulkan     — future Vulkan renderer
  turbojpeg  — MJPEG decode/encode (libturbojpeg)
  xorg       — XRandR geometry + screen grab (libx11, libxrandr)
  vaapi      — VA-API hardware codec (libva)
Each flag injects -DHAVE_<FEATURE> and the relevant pkg-config flags.
Headless build: make (no FEATURES set).

test_image module (src/modules/test_image/):
  Generates test frames in YUV420, YUV422, and BGRA.
  Patterns: SMPTE 75% colour bars, greyscale ramp, white/black grid.
  BT.601 limited-range RGB→YCbCr in write_pixel().

test_image_cli (dev/cli/):
  Generates a frame and writes it as a PPM file for visual verification.
  Usage: test_image_cli [--pattern bars|ramp|grid]
                        [--width N] [--height N]
                        [--format yuv420|yuv422|bgra]
                        --out FILE.ppm

xorg module (src/modules/xorg/):
  xorg.c      — full GLFW+OpenGL implementation (compiled with FEATURES=glfw)
  xorg_stub.c — no-op stub (compiled otherwise; xorg_available() returns false)
  Renderer: full-screen quad via gl_VertexID, three GL_R8 textures for YUV,
  BT.601 matrix in fragment shader, GL_BGRA texture for packed frames.
  MJPEG path: tjDecompressToYUVPlanes → planar YUV → upload (requires turbojpeg).
  push_yuv420/push_bgra/push_mjpeg all usable independently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:54:07 +00:00
62c25247ef Add protocol module, video-node binary, query/web CLI tools
- 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>
2026-03-27 01:04:56 +00:00
ba26bd0cb7 Add config module: INI loader with schema-driven defaults
Config_Def schema tables declare section/key/type/default per module.
Typed getters: config_get_str, _u16, _u32, _flags.
FLAGS type parses space/comma-separated tokens via a Config_Flag_Def table.
config_defaults() gives schema defaults without a file.
config_dump() prints effective values for diagnostics.

config_cli: load a file or --defaults and dump effective config.
dev/example.cfg: sample config covering node, discovery, transport.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:37:53 +00:00
e8f52e8fe6 Fix missing unistd.h in discovery_cli (pause declaration)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:20:32 +00:00
fe350e531e Add discovery module: UDP multicast announcements and peer tracking
Sends 6-byte framed announcements to 224.0.0.251:5353 on startup and
every interval_ms (default 5s). Receive thread maintains a peer table
(max 64 entries); fires on_peer_found for new peers, on_peer_lost when
a peer misses timeout_intervals (default 3) consecutive intervals.

Own announcements are filtered by name+site_id. SO_REUSEADDR+REUSEPORT
allows multiple processes on the same host for testing.

discovery_cli: announce <name> <tcp_port> [flags] — prints found/lost events.

Also notes future config module in planning.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:19:56 +00:00
197ab7d5db Simplify frame header to message_type + payload_length (6 bytes)
Removes channel_id from the header. All message-specific identifiers
(stream_id, request_id, etc.) now live at the start of the payload,
interpreted by each message type handler. A relay seeing an unknown
type can skip or forward it using only payload_length, with no
knowledge of the payload structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:00:43 +00:00
ff48559b12 Add transport module: TCP framing, thread-per-connection, inbound limit
transport_server_create/start: binds TCP, spawns accept thread, closes
excess inbound connections when max_connections is reached.

transport_connect: outbound TCP, spawns read thread before returning.

transport_send_frame: packs 8-byte header with serial put_*, then writes
header + payload under a per-connection mutex (thread-safe).

Read thread: reads header, validates payload_length <= max_payload, mallocs
payload, calls on_frame (callback owns and must free payload). On error or
disconnect calls on_disconnect then frees conn.

transport_cli: server mode echoes received frames; client mode sends 3
test frames and prints echoes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 21:31:55 +00:00
e1b848333b Extract shared Makefile config into common.mk
CC, CFLAGS, and BUILD are now defined once in common.mk at the repo root.
Each module and CLI Makefile sets ROOT then includes common.mk, eliminating
the repeated definitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 22:15:03 +00:00
eb65181fe7 Add serial/protocol modules to plan, binary format to arch, -flto to Makefiles
architecture.md: replace JSON control payloads with binary serialization;
add Protocol Serialization section describing little-endian wire format,
put/get buffer layer, and write_*/read_* protocol layer.

planning.md: mark common/media_ctrl/v4l2_ctrl done; insert serial (#4)
and protocol (#6) modules with descriptions.

conventions.md: document -flto and its implication (no manual static for
inlining — compiler handles it at link time).

Makefiles: add -flto to CFLAGS in all four Makefiles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 22:14:20 +00:00
4ba2a8118d Fix MEDIA_LNK_FL_* and MEDIA_PAD_FL_* visibility in CLI translation unit
The CLI is a separate translation unit that doesn't include <linux/media.h>,
so the kernel flag constants were undefined there. Fix by defining our own
public constants in media_ctrl.h:
- MEDIA_LINK_FL_{ENABLED,IMMUTABLE,DYNAMIC} for link flags
- MEDIA_PAD_FLAG_{SINK,SOURCE} for pad flags (distinct names avoid
  redefinition conflict with kernel's MEDIA_PAD_FL_* in media_ctrl.c)

media_ctrl.c now translates kernel flags to our constants on the way out.
Added fallback #defines for kernel constants that were missing them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:51:49 +00:00
a29c556851 Add common, media_ctrl and v4l2_ctrl modules with CLI drivers and docs
Modules (src/modules/):
- common/error: App_Error struct with structured detail union, error codes,
  app_error_print(); designed for future upgrade to preprocessor-generated
  location codes
- media_ctrl: media device enumeration, topology query (entities/pads/links),
  link enable/disable via Media Controller API (/dev/media*)
- v4l2_ctrl: control enumeration (with menu item fetching), get/set via
  V4L2 ext controls API, device discovery (/dev/video*)

All modules use -std=c11 -D_GNU_SOURCE, build artifacts go to build/ only.
Kernel-version-dependent constants guarded with #ifdef + #warning.

CLI drivers (dev/cli/):
- media_ctrl_cli: list, info, topology, set-link subcommands
- v4l2_ctrl_cli: list, controls, get, set subcommands

Docs (docs/cli/):
- media_ctrl_cli.md and v4l2_ctrl_cli.md with usage, examples, and
  context within the video routing system

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:40:37 +00:00