add helper scripts

This commit is contained in:
Maik Jurischka committed 2026-10-20 14:45:42 +02:00
1 parent 1853360506
commit bebdeab0d5
2 files changed
+487

No files matched your search

+34
View File
@@ -103,6 +103,40 @@ Zeigt:
Zeigt alle verfügbaren Kamera-Auflösungen und Formate.
## Multi-Camera: alle Kameras auf einmal
`start_all_cameras.sh` erkennt alle angeschlossenen, von VIZION unterstützten
Kameras (via `vizion-ctl -l`, Fallback: `lsusb`/Cypress 04b4) und startet für
jede der Reihe nach eine `vizionStreamer`-Instanz mit eigenem Socket
(`/tmp/vizion_cam<idx>.sock`) und startet den Stream.
```bash
# Erkannte Kameras auflisten
./scripts/start_all_cameras.sh --list
# Alle Kameras per UDP streamen (Port 5000 + Kamera-Index)
./scripts/start_all_cameras.sh --udp 192.168.1.100 5000
# Alle Kameras als MJPEG-HTTP-Stream (Port 8080 + Kamera-Index)
./scripts/start_all_cameras.sh --mjpeg 8080
# Alle Kameras in Dateien aufnehmen (/tmp/cam<idx>.mp4)
./scripts/start_all_cameras.sh --file /home/user/recordings
# Lokale Anzeige
./scripts/start_all_cameras.sh --display
# Eigene Pipeline ({port} wird durch 5000+Kamera-Index ersetzt)
./scripts/start_all_cameras.sh --pipeline "videoconvert ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=192.168.1.100 port={port}"
# Alle gestarteten Instanzen stoppen
./scripts/start_all_cameras.sh --stop
```
Ohne Modus-Option wird standardmäßig per UDP nach `127.0.0.1` gestreamt.
Das Script läuft im Vordergrund und stoppt mit `Ctrl+C` alle Instanzen
sauber (Stream stoppen + Prozess beenden). Logs: `/tmp/vizion_cam<idx>.log`.
## Typischer Workflow
```bash
+453
View File
@@ -0,0 +1,453 @@
#!/bin/bash
#
# start_all_cameras.sh
#
# Sucht alle angeschlossenen, von VIZION unterstützten Kameras und startet
# für jede (der Reihe nach) eine vizionStreamer-Instanz mit eigenem
# Control-Socket und startet den Stream.
#
# Erkennung:
# 1. vizion-ctl -l (VizionSDK, /opt/vizionsdk/bin/vizion-ctl) - offizielle
# Kameradetection (USB + MIPI)
# 2. Fallback: lsusb (Cypress USB-Geräte, Vendor 04b4)
#
# Pro Kamera werden angelegt:
# /tmp/vizion_cam<idx>.sock Control-Socket
# /tmp/vizion_cam<idx>.pid PID-Datei
# /tmp/vizion_cam<idx>.log Log der Instanz
#
# Copyright (c) 2025 Maik Jurischka
# Lizenziert unter CC BY-NC-SA 4.0 - https://creativecommons.org/licenses/by-nc-sa/4.0/
set -u
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
BINARY="$ROOT_DIR/build/vizionStreamer"
# VizionSDK-Tool (offizielle Kameradetection)
VIZION_CTL="$(command -v vizion-ctl 2>/dev/null || true)"
[ -n "$VIZION_CTL" ] || VIZION_CTL="/opt/vizionsdk/bin/vizion-ctl"
SOCKET_PREFIX="/tmp/vizion_cam" # /tmp/vizion_cam<idx>.sock
PID_PREFIX="/tmp/vizion_cam" # /tmp/vizion_cam<idx>.pid
LOG_PREFIX="/tmp/vizion_cam" # /tmp/vizion_cam<idx>.log
WAIT_TIMEOUT=15 # Sekunden, die auf den Socket gewartet wird
# ---------------------------------------------------------------------------
# Ausgabe-Helfer
# ---------------------------------------------------------------------------
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
err() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2; }
usage() {
cat <<'EOF'
Nutzung: start_all_cameras.sh [Optionen]
Sucht alle angeschlossenen VIZION-Kameras und startet für jede (der Reihe
nach) einen Stream.
Optionen:
--list Nur erkannte Kameras auflisten
--udp <host> [port] UDP H.264-Stream (Standard: 127.0.0.1, Port 5000+Kamera-Index)
--mjpeg [port] MJPEG HTTP-Stream (Standard: Port 8080+Kamera-Index)
--file [dir] Aufnahme in Datei (Standard: /tmp/cam<idx>.mp4)
--display Lokale Anzeige (autovideosink)
--pipeline <pipe> Eigene GStreamer-Pipeline (für {port} wird 5000+Kamera-Index eingesetzt)
--stop Alle von diesem Script gestarteten Instanzen stoppen
-h, --help Diese Hilfe anzeigen
Beispiele:
./scripts/start_all_cameras.sh --list
./scripts/start_all_cameras.sh --udp 192.168.1.100 5000
./scripts/start_all_cameras.sh --mjpeg 8080
./scripts/start_all_cameras.sh --file /home/user/recordings
./scripts/start_all_cameras.sh --pipeline "videoconvert ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=192.168.1.100 port={port}"
Empfangen (UDP, auf dem Zielrechner):
gst-launch-1.0 udpsrc port=5000 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! autovideosink
EOF
}
# ---------------------------------------------------------------------------
# Socket-Helfer
# ---------------------------------------------------------------------------
# send_cmd <socket> <json> - JSON-Command an eine Instanz senden
send_cmd() {
echo "$2" | socat - "UNIX-CONNECT:$1" 2>/dev/null
}
# wait_for_socket <socket> - bis der Socket existiert warten
wait_for_socket() {
local sock="$1"
local i
for ((i = 0; i < WAIT_TIMEOUT * 2; i++)); do
[ -S "$sock" ] && return 0
sleep 0.5
done
return 1
}
# json_escape <string> - String für JSON-Strings escapen
json_escape() {
local s="$1"
s=${s//\\/\\\\}
s=${s//\"/\\\"}
printf '%s' "$s"
}
# ---------------------------------------------------------------------------
# Kamera-Erkennung
# ---------------------------------------------------------------------------
CAMERAS=()
DETECT_OUTPUT=""
detect_cameras() {
if [ -x "$VIZION_CTL" ]; then
DETECT_OUTPUT="$("$VIZION_CTL" -l 2>&1)"
# Indizes aus Zeilen wie "[0] VCI-AR0234 ..." extrahieren
local idxs
idxs="$(echo "$DETECT_OUTPUT" | grep -oE '^[[:space:]]*\[[0-9]+\]' | tr -d '[]' | sort -n -u)"
if [ -n "$idxs" ]; then
while IFS= read -r i; do
CAMERAS+=("$i")
done <<< "$idxs"
else
# Fallback: Anzahl aus "Found N camera(s)" o.ä. parsen
local n
n="$(echo "$DETECT_OUTPUT" | grep -oiE '[0-9]+[[:space:]]*camera' | grep -oE '^[0-9]+' | head -1)"
if [ -n "$n" ]; then
local i
for ((i = 0; i < n; i++)); do
CAMERAS+=("$i")
done
fi
fi
else
log "vizion-ctl nicht gefunden ($VIZION_CTL) - verwende lsusb als Fallback"
fi
# Fallback: Cypress USB-Geräte (Vendor 04b4) zählen
if [ ${#CAMERAS[@]} -eq 0 ] && command -v lsusb >/dev/null 2>&1; then
local nusb
nusb="$(lsusb 2>/dev/null | grep -cE 'ID 04b4:')"
if [ "$nusb" -gt 0 ]; then
log "lsusb hat $nusb Cypress-USB-Gerät(e) (Vendor 04b4) gefunden"
local i
for ((i = 0; i < nusb; i++)); do
CAMERAS+=("$i")
done
fi
fi
}
# ---------------------------------------------------------------------------
# Pipeline je nach Modus (Parameter: Kamera-Index)
# ---------------------------------------------------------------------------
make_pipeline() {
local i="$1"
case "$MODE" in
udp)
echo "videoconvert ! x264enc tune=zerolatency bitrate=2000 ! rtph264pay ! udpsink host=$UDP_HOST port=$((UDP_PORT + i))"
;;
mjpeg)
echo "videoconvert ! jpegenc ! multipartmux ! tcpserversink host=0.0.0.0 port=$((MJPEG_PORT + i))"
;;
file)
echo "videoconvert ! x264enc ! mp4mux ! filesink location=$FILE_DIR/cam${i}.mp4"
;;
display)
echo "videoconvert ! autovideosink"
;;
custom)
local p="$CUSTOM_PIPELINE"
if [[ "$p" == *"{port}"* ]]; then
p="${p//\{port\}/$((5000 + i))}"
fi
echo "$p"
;;
*)
echo ""
;;
esac
}
# ---------------------------------------------------------------------------
# Instanz starten (Parameter: Kamera-Index)
# ---------------------------------------------------------------------------
PIDS=()
start_camera() {
local i="$1"
local sock="$SOCKET_PREFIX${i}.sock"
local pidfile="$PID_PREFIX${i}.pid"
local logf="$LOG_PREFIX${i}.log"
local pipeline resp pid
# Läuft für diese Kamera bereits eine Instanz?
if [ -f "$pidfile" ] && kill -0 "$(cat "$pidfile" 2>/dev/null)" 2>/dev/null; then
log "Kamera $i: läuft bereits (PID $(cat "$pidfile")) - wird übersprungen"
return 0
fi
pipeline="$(make_pipeline "$i")"
if [ -z "$pipeline" ]; then
err "Kamera $i: keine Pipeline konfiguriert"
return 1
fi
# Eventuelle alte Socket-Datei entfernen (Instanz ist laut PID-Check nicht aktiv)
rm -f "$sock"
log "Kamera $i: starte vizionStreamer (Socket: $sock)"
"$BINARY" --camera "$i" --socket "$sock" >> "$logf" 2>&1 &
pid=$!
echo "$pid" > "$pidfile"
PIDS+=("$pid")
if ! wait_for_socket "$sock"; then
err "Kamera $i: Socket ist nicht aufgetaucht (Kamera nicht verfügbar? Log: $logf)"
wait "$pid" 2>/dev/null
rm -f "$pidfile"
return 1
fi
# Pipeline setzen
resp="$(send_cmd "$sock" "{\"command\":\"set_pipeline\",\"params\":{\"pipeline\":\"$(json_escape "$pipeline")\"}}")"
if ! echo "$resp" | grep -q '"status"[[:space:]]*:[[:space:]]*"success"'; then
err "Kamera $i: set_pipeline fehlgeschlagen: $resp"
return 1
fi
# Stream starten
send_cmd "$sock" '{"command":"start_stream"}' >/dev/null 2>&1
sleep 1
# Status prüfen
resp="$(send_cmd "$sock" '{"command":"get_status"}')"
if echo "$resp" | grep -q '"streaming"[[:space:]]*:[[:space:]]*true'; then
log "Kamera $i: [OK] Stream läuft | $pipeline"
return 0
else
err "Kamera $i: Stream konnte nicht gestartet werden: $resp"
return 1
fi
}
# ---------------------------------------------------------------------------
# Alle Instanzen stoppen
# ---------------------------------------------------------------------------
stop_all() {
local found=0
local pidfile idx sock pid
for pidfile in "$PID_PREFIX"*.pid; do
[ -e "$pidfile" ] || continue
idx="${pidfile#"$PID_PREFIX"}"
idx="${idx%.pid}"
sock="$SOCKET_PREFIX$idx.sock"
pid="$(cat "$pidfile" 2>/dev/null || true)"
# Zuerst den Stream stoppen (best effort)
if [ -S "$sock" ]; then
send_cmd "$sock" '{"command":"stop_stream"}' >/dev/null 2>&1
fi
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null
log "Kamera $idx: gestoppt (PID $pid)"
fi
rm -f "$pidfile"
found=1
done
[ "$found" -eq 1 ] || log "Keine laufenden Instanzen gefunden."
}
cleanup() {
echo
log "Stoppe alle Streams..."
stop_all
log "Fertig."
exit 0
}
# ---------------------------------------------------------------------------
# Argumente parsen
# ---------------------------------------------------------------------------
MODE=""
UDP_HOST="127.0.0.1"
UDP_PORT=5000
MJPEG_PORT=8080
FILE_DIR="/tmp"
CUSTOM_PIPELINE=""
LIST_ONLY=0
STOP_ONLY=0
while [ $# -gt 0 ]; do
case "$1" in
--list)
LIST_ONLY=1
;;
--stop)
STOP_ONLY=1
;;
--udp)
MODE="udp"
if [ $# -ge 2 ] && [[ "$2" != --* ]]; then UDP_HOST="$2"; shift; fi
if [ $# -ge 2 ] && [[ "$2" =~ ^[0-9]+$ ]]; then UDP_PORT="$2"; shift; fi
;;
--mjpeg)
MODE="mjpeg"
if [ $# -ge 2 ] && [[ "$2" =~ ^[0-9]+$ ]]; then MJPEG_PORT="$2"; shift; fi
;;
--file)
MODE="file"
if [ $# -ge 2 ] && [[ "$2" != --* ]]; then FILE_DIR="$2"; shift; fi
;;
--display)
MODE="display"
;;
--pipeline)
MODE="custom"
if [ $# -lt 2 ]; then
err "--pipeline benötigt ein Argument"
exit 1
fi
CUSTOM_PIPELINE="$2"
shift
;;
-h|--help)
usage
exit 0
;;
*)
err "Unbekannte Option: $1"
usage
exit 1
;;
esac
shift
done
# ---------------------------------------------------------------------------
# Hauptablauf
# ---------------------------------------------------------------------------
# Nur stoppen?
if [ $STOP_ONLY -eq 1 ]; then
stop_all
exit 0
fi
# Nur auflisten?
if [ $LIST_ONLY -eq 1 ]; then
detect_cameras
if [ ${#CAMERAS[@]} -eq 0 ]; then
echo "Keine VIZION-Kameras gefunden."
exit 1
fi
echo "Gefunden: ${#CAMERAS[@]} VIZION-Kamera(n)"
if [ -n "$DETECT_OUTPUT" ]; then
echo "$DETECT_OUTPUT"
else
for i in "${CAMERAS[@]}"; do
echo " [$i] (Cypress USB-Gerät)"
done
fi
exit 0
fi
# Standardmodus
if [ -z "$MODE" ]; then
MODE="udp"
log "Kein Modus angegeben - Standard: UDP nach 127.0.0.1 (mit --udp <host> an einen Remote-Rechner streamen)"
fi
# Abhängigkeiten prüfen
if [ ! -x "$BINARY" ]; then
err "vizionStreamer nicht gefunden: $BINARY"
err "Erst bauen: cmake -B build -S . && cmake --build build"
exit 1
fi
if ! command -v socat >/dev/null 2>&1; then
err "socat nicht gefunden (installieren mit: sudo apt install socat)"
exit 1
fi
if [ "$MODE" = "file" ]; then
mkdir -p "$FILE_DIR" || { err "Kann Verzeichnis $FILE_DIR nicht anlegen"; exit 1; }
fi
# Kameras erkennen
detect_cameras
if [ ${#CAMERAS[@]} -eq 0 ]; then
err "Keine VIZION-Kameras gefunden."
err "Verbindung prüfen (dmesg) und ggf. /opt/vizionsdk/init_driver.sh ausführen."
exit 1
fi
log "Gefunden: ${#CAMERAS[@]} VIZION-Kamera(n): ${CAMERAS[*]}"
# Ctrl+C -> alles sauber stoppen
trap cleanup INT TERM
# Der Reihe nach: für jede Kamera Instanz starten + Stream
OK_COUNT=0
FAIL_COUNT=0
for i in "${CAMERAS[@]}"; do
if start_camera "$i"; then
OK_COUNT=$((OK_COUNT + 1))
else
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done
# Zusammenfassung
echo
echo "========================================"
echo "Streams gestartet: $OK_COUNT / ${#CAMERAS[@]}"
case "$MODE" in
udp)
for i in "${CAMERAS[@]}"; do
echo " Kamera $i -> udp://$UDP_HOST:$((UDP_PORT + i))"
done
echo "Empfangen (Zielrechner, Kamera 0):"
echo " gst-launch-1.0 udpsrc port=$UDP_PORT ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! autovideosink"
;;
mjpeg)
for i in "${CAMERAS[@]}"; do
echo " Kamera $i -> http://<board-ip>:$((MJPEG_PORT + i))"
done
echo "Im Browser oder mit: ffplay http://<board-ip>:$MJPEG_PORT"
;;
file)
for i in "${CAMERAS[@]}"; do
echo " Kamera $i -> $FILE_DIR/cam${i}.mp4"
done
;;
display)
echo " Fenster werden lokal angezeigt"
;;
custom)
echo " Pipeline: $CUSTOM_PIPELINE"
;;
esac
if [ $FAIL_COUNT -gt 0 ]; then
echo " ($FAIL_COUNT Kamera(n) fehlgeschlagen - Logs unter /tmp/vizion_cam<idx>.log)"
fi
echo "========================================"
echo "Drücke Ctrl+C, um alle Streams zu stoppen."
# Überwachung: melden, wenn eine Instanz stirbt
while true; do
sleep 5
alive=0
for pid in ${PIDS[@]+"${PIDS[@]}"}; do
if kill -0 "$pid" 2>/dev/null; then
alive=$((alive + 1))
fi
done
if [ "$alive" -eq 0 ]; then
log "Alle Instanzen haben sich beendet."
break
fi
done