#!/usr/bin/env bash # Grab one camera frame to a local JPEG. # Usage: snap out.jpg set -uo pipefail [ $# -eq 1 ] || { echo "usage: snap out.jpg" >&2; exit 1; } OUT="$1" # The camera occasionally throws a transient I/O error or wedges; retry a # few times with a hard timeout so a bad state can never hang the caller. for attempt in 1 2 3; do if timeout 25 rpicam-still -n -t 800 --width 2592 --height 1944 -o /dev/shm/snap.jpg 2>/dev/null; then mv /dev/shm/snap.jpg "$OUT" echo "$OUT" exit 0 fi sleep 3 done # Camera busy (some process owns it) — fall back to the vilib MJPEG stream, if running. : > /tmp/_stream.raw curl -s --max-time 5 "http://localhost:9000/mjpg" -o /tmp/_stream.raw 2>/dev/null || true if python3 - "$OUT" <<'PY' import sys d = open('/tmp/_stream.raw', 'rb').read() i = d.find(b'\xff\xd8'); j = d.find(b'\xff\xd9', i) if i < 0 or j <= i: sys.exit(1) open(sys.argv[1], 'wb').write(d[i:j+2]) PY then echo "$OUT" else echo "ERROR: camera did not produce a frame after 3 attempts and no fallback stream" >&2 exit 1 fi