The camera on a Pi host

You operate the fleet and want to know how a Pi’s camera reaches the board’s page, to fix or change a feed. From this repository’s gst-libcam.sh and cam.service (main 0c974d5) and the gateway roles stream_server and webrtc in fpgas.online-infra (main, read 2026-10-07).

On the Pi

fpgas-cam.service runs /usr/local/bin/fpgas-gst-libcam.sh, which:

  1. finds a camera: a CSI camera through libcamera first, else a USB (UVC) capture device, such as the HDMI grabbers on the NeTV2 hosts;

  2. if it finds neither after a few tries, exits with status 78. The unit does not restart on 78 (RestartPreventExitStatus=78): a CSI camera can only be connected with the board off. After plugging in a USB grabber: sudo systemctl restart fpgas-cam;

  3. encodes H.264 at 6 frames per second (FPS, default 6), with the hardware encoder (v4l2h264enc) where there is one and x264enc where there is not (a Pi 5), with one keyframe per second, and a clock (clockoverlay) drawn over the picture;

  4. publishes to rtmp://<the Pi's default gateway>/pib/<the Pi's short name>. The gateway is read from the route table (ip -json route show default, parsed with jq), not from a setting; RTMP_DEST in the environment replaces the whole URL (gst-libcam.sh lines 15 and 66-84, read 2026-10-07).

Any other failure (the stream server restarting, say) is retried every second.

On the gateway

stream_server runs nginx-rtmp, which takes the Pi’s stream and serves it as HLS under /live (https://<site>/live/<name>.m3u8, which a desktop player such as VLC opens). Where webrtc_additional_hosts is set (in host_vars/fpgas.online.yml, so on tweed), webrtc runs mediamtx, which serves the same streams over WebRTC (WHEP).

The HLS under /live is served with Cache-Control: no-cache, because a cached live playlist is stale by definition (stream_server, live-hls.conf.j2, infra main, read 2026-10-07). The board pages embed that playlist, and each also offers the direct URL for a desktop player, vlc https://<domain>/live/pi<N>.m3u8 (from the earlier docs page, not re-checked).

On the gateway, systemctl status nginx mediamtx and journalctl -u nginx show whether a stream arrives; on the Pi, journalctl -u fpgas-cam shows the pipeline’s own messages.

Latency

Latency is a deliberate trade. nginx-rtmp can only cut an HLS fragment at a keyframe, so the GOP length is the floor on fragment length, and the player starts three fragments behind the newest. A 60-frame GOP at 6 fps meant 10-second fragments and about 40 seconds glass-to-glass (measured 2026-08-30); one keyframe per second plus a 900 ms server fragment brings that to roughly 5 seconds (header comment of gst-libcam.sh, main 4e75e21, read 2026-10-07). The WebRTC path above is separate.

Which Pis have a camera

No list is kept per Pi. On a Pi, systemctl status fpgas-cam says: running, or stopped with status 78 where there is no camera.