debug shared_buffer stream
This commit is contained in:
17
fix_exposure.sh
Executable file
17
fix_exposure.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to fix camera exposure settings
|
||||||
|
# This stops the stream, enables auto-exposure, and restarts the stream
|
||||||
|
|
||||||
|
SOCKET="/tmp/vizion_control.sock"
|
||||||
|
|
||||||
|
echo "Stopping stream..."
|
||||||
|
echo '{"command":"stop_stream"}' | socat - UNIX-CONNECT:$SOCKET
|
||||||
|
|
||||||
|
echo "Setting auto-exposure..."
|
||||||
|
echo '{"command":"set_exposure","params":{"mode":"auto","value":"0"}}' | socat - UNIX-CONNECT:$SOCKET
|
||||||
|
|
||||||
|
echo "Starting stream..."
|
||||||
|
echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:$SOCKET
|
||||||
|
|
||||||
|
echo "Done! Camera should now show proper image."
|
||||||
@@ -39,6 +39,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void acquisitionLoop();
|
void acquisitionLoop();
|
||||||
|
size_t convertToRGB(const uint8_t* src, uint8_t* dst, int width, int height, VX_IMAGE_FORMAT format);
|
||||||
|
|
||||||
std::shared_ptr<VxCamera> camera_;
|
std::shared_ptr<VxCamera> camera_;
|
||||||
std::unique_ptr<GStreamerPipeline> gstPipeline_;
|
std::unique_ptr<GStreamerPipeline> gstPipeline_;
|
||||||
@@ -48,5 +49,6 @@ private:
|
|||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
VxFormat currentFormat_;
|
VxFormat currentFormat_;
|
||||||
std::unique_ptr<uint8_t[]> buffer_;
|
std::unique_ptr<uint8_t[]> buffer_;
|
||||||
|
std::unique_ptr<uint8_t[]> rgbBuffer_; // Buffer for RGB conversion
|
||||||
size_t bufferSize_;
|
size_t bufferSize_;
|
||||||
};
|
};
|
||||||
|
|||||||
145
service/README.md
Normal file
145
service/README.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# VizionStreamer Systemd Service
|
||||||
|
|
||||||
|
Automatischer Start und Überwachung des VizionStreamer mit systemd.
|
||||||
|
|
||||||
|
## Inhalt
|
||||||
|
|
||||||
|
- `vizionstreamer.service` - Systemd service file
|
||||||
|
- `watchdog.sh` - Watchdog-Script (startet Streamer neu bei Crash)
|
||||||
|
- `install.sh` - Installations-Script
|
||||||
|
- `uninstall.sh` - Deinstallations-Script
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd service
|
||||||
|
sudo ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Script:
|
||||||
|
1. Kopiert die Service-Datei nach `/etc/systemd/system/`
|
||||||
|
2. Aktiviert den Service (Start beim Booten)
|
||||||
|
3. Startet den Service sofort
|
||||||
|
|
||||||
|
## Deinstallation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd service
|
||||||
|
sudo ./uninstall.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Befehle
|
||||||
|
|
||||||
|
### Service verwalten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Service starten
|
||||||
|
sudo systemctl start vizionstreamer
|
||||||
|
|
||||||
|
# Service stoppen
|
||||||
|
sudo systemctl stop vizionstreamer
|
||||||
|
|
||||||
|
# Service neu starten
|
||||||
|
sudo systemctl restart vizionstreamer
|
||||||
|
|
||||||
|
# Status anzeigen
|
||||||
|
sudo systemctl status vizionstreamer
|
||||||
|
|
||||||
|
# Autostart deaktivieren
|
||||||
|
sudo systemctl disable vizionstreamer
|
||||||
|
|
||||||
|
# Autostart aktivieren
|
||||||
|
sudo systemctl enable vizionstreamer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logs anzeigen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Live-Logs verfolgen
|
||||||
|
sudo journalctl -u vizionstreamer -f
|
||||||
|
|
||||||
|
# Letzte 100 Zeilen
|
||||||
|
sudo journalctl -u vizionstreamer -n 100
|
||||||
|
|
||||||
|
# Logs seit heute
|
||||||
|
sudo journalctl -u vizionstreamer --since today
|
||||||
|
|
||||||
|
# Alle Logs
|
||||||
|
sudo journalctl -u vizionstreamer --no-pager
|
||||||
|
```
|
||||||
|
|
||||||
|
## Funktionen
|
||||||
|
|
||||||
|
✅ **Automatischer Start** beim Booten
|
||||||
|
✅ **Watchdog** startet Prozess bei Crash neu (max. 10 Versuche)
|
||||||
|
✅ **Systemd Integration** - Logs in journald
|
||||||
|
✅ **Graceful Shutdown** - Sauberes Beenden
|
||||||
|
✅ **Restart-Limit** - Verhindert endlose Restart-Loops
|
||||||
|
|
||||||
|
## Konfiguration
|
||||||
|
|
||||||
|
### Service-Einstellungen anpassen
|
||||||
|
|
||||||
|
Service-Datei bearbeiten:
|
||||||
|
```bash
|
||||||
|
sudo systemctl edit vizionstreamer --full
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach Änderungen:
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl restart vizionstreamer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Watchdog-Intervall ändern
|
||||||
|
|
||||||
|
In `watchdog.sh`:
|
||||||
|
```bash
|
||||||
|
CHECK_INTERVAL=5 # Sekunden (Standard: 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Service startet nicht
|
||||||
|
|
||||||
|
1. Status prüfen:
|
||||||
|
```bash
|
||||||
|
sudo systemctl status vizionstreamer
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Logs prüfen:
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u vizionstreamer -n 50
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Manuell testen:
|
||||||
|
```bash
|
||||||
|
./watchdog.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permissions
|
||||||
|
|
||||||
|
Falls Permission-Probleme:
|
||||||
|
```bash
|
||||||
|
sudo usermod -a -G video maik
|
||||||
|
sudo usermod -a -G audio maik
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service deaktivieren (temporär)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop vizionstreamer
|
||||||
|
sudo systemctl disable vizionstreamer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Systemanforderungen
|
||||||
|
|
||||||
|
- Linux mit systemd
|
||||||
|
- Zugriff auf `/dev/video*` Geräte
|
||||||
|
- User `maik` muss Zugriff auf Kamera haben
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Bei Problemen siehe:
|
||||||
|
- `sudo journalctl -u vizionstreamer -f`
|
||||||
|
- `tail -f ../watchdog.log`
|
||||||
73
service/install.sh
Executable file
73
service/install.sh
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Installation script for VizionStreamer systemd service
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SERVICE_NAME="vizionstreamer.service"
|
||||||
|
SERVICE_FILE="$(pwd)/vizionstreamer.service"
|
||||||
|
SYSTEMD_DIR="/etc/systemd/system"
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== VizionStreamer Service Installation ===${NC}\n"
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if service file exists
|
||||||
|
if [ ! -f "$SERVICE_FILE" ]; then
|
||||||
|
echo -e "${RED}Error: Service file not found: $SERVICE_FILE${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if vizionStreamer executable exists
|
||||||
|
if [ ! -f "/home/maik/CLionProjects/vizionStreamer/build/vizionStreamer" ]; then
|
||||||
|
echo -e "${YELLOW}Warning: vizionStreamer executable not found!${NC}"
|
||||||
|
echo "Please build the project first: cmake --build build"
|
||||||
|
read -p "Continue anyway? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop service if already running
|
||||||
|
if systemctl is-active --quiet $SERVICE_NAME; then
|
||||||
|
echo "Stopping existing service..."
|
||||||
|
systemctl stop $SERVICE_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy service file
|
||||||
|
echo "Installing service file to $SYSTEMD_DIR..."
|
||||||
|
cp "$SERVICE_FILE" "$SYSTEMD_DIR/"
|
||||||
|
|
||||||
|
# Reload systemd
|
||||||
|
echo "Reloading systemd daemon..."
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
# Enable service
|
||||||
|
echo "Enabling service to start on boot..."
|
||||||
|
systemctl enable $SERVICE_NAME
|
||||||
|
|
||||||
|
# Start service
|
||||||
|
echo "Starting service..."
|
||||||
|
systemctl start $SERVICE_NAME
|
||||||
|
|
||||||
|
# Show status
|
||||||
|
echo -e "\n${GREEN}Installation complete!${NC}\n"
|
||||||
|
systemctl status $SERVICE_NAME --no-pager
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}=== Useful Commands ===${NC}"
|
||||||
|
echo " Start: sudo systemctl start $SERVICE_NAME"
|
||||||
|
echo " Stop: sudo systemctl stop $SERVICE_NAME"
|
||||||
|
echo " Restart: sudo systemctl restart $SERVICE_NAME"
|
||||||
|
echo " Status: sudo systemctl status $SERVICE_NAME"
|
||||||
|
echo " Logs: sudo journalctl -u $SERVICE_NAME -f"
|
||||||
|
echo " Disable: sudo systemctl disable $SERVICE_NAME"
|
||||||
53
service/uninstall.sh
Executable file
53
service/uninstall.sh
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Uninstallation script for VizionStreamer systemd service
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SERVICE_NAME="vizionstreamer.service"
|
||||||
|
SYSTEMD_DIR="/etc/systemd/system"
|
||||||
|
|
||||||
|
echo -e "${YELLOW}=== VizionStreamer Service Uninstallation ===${NC}\n"
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if service exists
|
||||||
|
if [ ! -f "$SYSTEMD_DIR/$SERVICE_NAME" ]; then
|
||||||
|
echo -e "${YELLOW}Service not installed.${NC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop service
|
||||||
|
if systemctl is-active --quiet $SERVICE_NAME; then
|
||||||
|
echo "Stopping service..."
|
||||||
|
systemctl stop $SERVICE_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disable service
|
||||||
|
if systemctl is-enabled --quiet $SERVICE_NAME; then
|
||||||
|
echo "Disabling service..."
|
||||||
|
systemctl disable $SERVICE_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove service file
|
||||||
|
echo "Removing service file..."
|
||||||
|
rm -f "$SYSTEMD_DIR/$SERVICE_NAME"
|
||||||
|
|
||||||
|
# Reload systemd
|
||||||
|
echo "Reloading systemd daemon..."
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
# Reset failed state if any
|
||||||
|
systemctl reset-failed $SERVICE_NAME 2>/dev/null || true
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}Uninstallation complete!${NC}"
|
||||||
|
echo "Service '$SERVICE_NAME' has been removed."
|
||||||
33
service/vizionstreamer.service
Normal file
33
service/vizionstreamer.service
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=VizionStreamer Camera Service with Watchdog
|
||||||
|
After=network.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=maik
|
||||||
|
Group=maik
|
||||||
|
WorkingDirectory=/home/maik/CLionProjects/vizionStreamer
|
||||||
|
ExecStart=/home/maik/CLionProjects/vizionStreamer/service/watchdog.sh
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10s
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
Environment="PATH=/usr/local/bin:/usr/bin:/bin"
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=vizionstreamer
|
||||||
|
|
||||||
|
# Security settings (optional, kann bei Problemen auskommentiert werden)
|
||||||
|
# NoNewPrivileges=true
|
||||||
|
# PrivateTmp=true
|
||||||
|
|
||||||
|
# Graceful shutdown
|
||||||
|
TimeoutStopSec=30
|
||||||
|
KillMode=mixed
|
||||||
|
KillSignal=SIGTERM
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
119
service/watchdog.sh
Executable file
119
service/watchdog.sh
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# VizionStreamer Watchdog Script
|
||||||
|
# Automatically restarts the streamer if it crashes
|
||||||
|
# Usage: ./watchdog.sh
|
||||||
|
|
||||||
|
STREAMER_PATH="./build/vizionStreamer"
|
||||||
|
CHECK_INTERVAL=5 # Seconds between checks
|
||||||
|
LOG_FILE="./watchdog.log"
|
||||||
|
PID_FILE="./vizionStreamer.pid"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to log messages
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to start the streamer
|
||||||
|
start_streamer() {
|
||||||
|
log "${GREEN}Starting VizionStreamer...${NC}"
|
||||||
|
$STREAMER_PATH &
|
||||||
|
STREAMER_PID=$!
|
||||||
|
echo $STREAMER_PID > "$PID_FILE"
|
||||||
|
log "VizionStreamer started with PID: $STREAMER_PID"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if process is running
|
||||||
|
is_running() {
|
||||||
|
if [ -f "$PID_FILE" ]; then
|
||||||
|
PID=$(cat "$PID_FILE")
|
||||||
|
if ps -p $PID > /dev/null 2>&1; then
|
||||||
|
return 0 # Running
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1 # Not running
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to stop the streamer
|
||||||
|
stop_streamer() {
|
||||||
|
if [ -f "$PID_FILE" ]; then
|
||||||
|
PID=$(cat "$PID_FILE")
|
||||||
|
if ps -p $PID > /dev/null 2>&1; then
|
||||||
|
log "${YELLOW}Stopping VizionStreamer (PID: $PID)...${NC}"
|
||||||
|
kill $PID
|
||||||
|
sleep 2
|
||||||
|
# Force kill if still running
|
||||||
|
if ps -p $PID > /dev/null 2>&1; then
|
||||||
|
log "${RED}Force killing VizionStreamer...${NC}"
|
||||||
|
kill -9 $PID
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f "$PID_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Trap SIGINT and SIGTERM to cleanly shutdown
|
||||||
|
cleanup() {
|
||||||
|
log "${YELLOW}Watchdog shutting down...${NC}"
|
||||||
|
stop_streamer
|
||||||
|
log "${GREEN}Watchdog stopped${NC}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup SIGINT SIGTERM
|
||||||
|
|
||||||
|
# Check if vizionStreamer executable exists
|
||||||
|
if [ ! -f "$STREAMER_PATH" ]; then
|
||||||
|
log "${RED}ERROR: VizionStreamer not found at $STREAMER_PATH${NC}"
|
||||||
|
log "Please build the project first: cmake --build build"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Main watchdog loop
|
||||||
|
log "${GREEN}=== VizionStreamer Watchdog Started ===${NC}"
|
||||||
|
log "Check interval: ${CHECK_INTERVAL} seconds"
|
||||||
|
log "Press Ctrl+C to stop"
|
||||||
|
|
||||||
|
RESTART_COUNT=0
|
||||||
|
|
||||||
|
# Start streamer initially
|
||||||
|
start_streamer
|
||||||
|
sleep 2 # Give it time to start
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sleep $CHECK_INTERVAL
|
||||||
|
|
||||||
|
if ! is_running; then
|
||||||
|
RESTART_COUNT=$((RESTART_COUNT + 1))
|
||||||
|
log "${RED}VizionStreamer died! Restart count: $RESTART_COUNT${NC}"
|
||||||
|
|
||||||
|
# Optional: limit restart attempts
|
||||||
|
if [ $RESTART_COUNT -gt 10 ]; then
|
||||||
|
log "${RED}ERROR: Too many restarts (10). Giving up.${NC}"
|
||||||
|
log "Check the logs for recurring errors."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up old PID file
|
||||||
|
rm -f "$PID_FILE"
|
||||||
|
|
||||||
|
# Wait a bit before restarting to avoid rapid restart loops
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
start_streamer
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
# Still running - reset restart counter on successful check
|
||||||
|
if [ $RESTART_COUNT -gt 0 ]; then
|
||||||
|
RESTART_COUNT=0
|
||||||
|
log "${GREEN}VizionStreamer stable again${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
CameraController::CameraController(std::shared_ptr<VxCamera> camera)
|
CameraController::CameraController(std::shared_ptr<VxCamera> camera)
|
||||||
: camera_(camera), gstPipeline_("videoconvert ! autovideosink") {
|
: camera_(camera), gstPipeline_("fakesink") {
|
||||||
streamingEngine_ = std::make_shared<StreamingEngine>(camera);
|
streamingEngine_ = std::make_shared<StreamingEngine>(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,8 +154,15 @@ std::string CameraController::handleSetExposure(const std::string& mode, const s
|
|||||||
const int flag = (mode == "auto") ? 1 : 0;
|
const int flag = (mode == "auto") ? 1 : 0;
|
||||||
const long expValue = value.empty() ? 0 : std::stol(value);
|
const long expValue = value.empty() ? 0 : std::stol(value);
|
||||||
|
|
||||||
if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_EXPOSURE,
|
std::cout << "[DEBUG] Setting exposure: mode=" << mode << " (flag=" << flag
|
||||||
expValue, flag) != 0) {
|
<< "), value=" << expValue << std::endl;
|
||||||
|
|
||||||
|
const int result = VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_EXPOSURE,
|
||||||
|
expValue, flag);
|
||||||
|
|
||||||
|
std::cout << "[DEBUG] VxSetUVCImageProcessing returned: " << result << std::endl;
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
return createErrorResponse("Failed to set exposure");
|
return createErrorResponse("Failed to set exposure");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,8 +256,15 @@ std::string CameraController::handleSetGamma(const std::string& value) {
|
|||||||
std::string CameraController::handleSetGain(const std::string& value) {
|
std::string CameraController::handleSetGain(const std::string& value) {
|
||||||
try {
|
try {
|
||||||
const long val = std::stol(value);
|
const long val = std::stol(value);
|
||||||
if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_GAIN,
|
|
||||||
val, 0) != 0) {
|
std::cout << "[DEBUG] Setting gain: value=" << val << std::endl;
|
||||||
|
|
||||||
|
const int result = VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_GAIN,
|
||||||
|
val, 0);
|
||||||
|
|
||||||
|
std::cout << "[DEBUG] VxSetUVCImageProcessing (gain) returned: " << result << std::endl;
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
return createErrorResponse("Failed to set gain");
|
return createErrorResponse("Failed to set gain");
|
||||||
}
|
}
|
||||||
return createSuccessResponse("Gain set successfully");
|
return createSuccessResponse("Gain set successfully");
|
||||||
|
|||||||
@@ -128,6 +128,19 @@ bool SharedMemoryWriter::writeFrame(const uint8_t* data, const size_t size,
|
|||||||
// 3. Copy frame data
|
// 3. Copy frame data
|
||||||
memcpy(frame_data, data, size);
|
memcpy(frame_data, data, size);
|
||||||
|
|
||||||
|
// Debug: Log every 60 frames
|
||||||
|
static uint64_t debug_counter = 0;
|
||||||
|
if (++debug_counter % 60 == 0) {
|
||||||
|
std::cout << "[SHM] Frame #" << frame_counter_
|
||||||
|
<< " written. write_seq=" << header->write_sequence.load()
|
||||||
|
<< " size=" << size
|
||||||
|
<< " first 4 bytes: ";
|
||||||
|
for (int i = 0; i < 4 && i < size; i++) {
|
||||||
|
printf("%02x ", frame_data[i]);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Increment sequence (even = write complete)
|
// 4. Increment sequence (even = write complete)
|
||||||
header->write_sequence.fetch_add(1, std::memory_order_release);
|
header->write_sequence.fetch_add(1, std::memory_order_release);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
StreamingEngine::StreamingEngine(std::shared_ptr<VxCamera> camera)
|
StreamingEngine::StreamingEngine(std::shared_ptr<VxCamera> camera)
|
||||||
: camera_(std::move(camera)), running_(false), currentFormat_(), bufferSize_(0) {
|
: camera_(std::move(camera)), running_(false), currentFormat_(), bufferSize_(0) {
|
||||||
@@ -44,26 +46,41 @@ bool StreamingEngine::start(const std::string& gstPipeline) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start camera streaming
|
// Ensure camera is not already streaming (clean slate)
|
||||||
if (VxStartStreaming(camera_) != 0) {
|
VxStopStreaming(camera_);
|
||||||
std::cerr << "Failed to start camera streaming" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current format to allocate buffer
|
// Get current format to allocate buffer BEFORE starting streaming
|
||||||
std::vector<VxFormat> fmtList;
|
std::vector<VxFormat> fmtList;
|
||||||
if (VxGetFormatList(camera_, fmtList) != 0 || fmtList.empty()) {
|
if (VxGetFormatList(camera_, fmtList) != 0 || fmtList.empty()) {
|
||||||
std::cerr << "Failed to get format list" << std::endl;
|
std::cerr << "Failed to get format list" << std::endl;
|
||||||
VxStopStreaming(camera_);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
currentFormat_ = fmtList[0];
|
currentFormat_ = fmtList[0];
|
||||||
|
|
||||||
|
// Ensure format is properly set before streaming
|
||||||
|
if (VxSetFormat(camera_, currentFormat_) != 0) {
|
||||||
|
std::cerr << "Failed to set format before streaming" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Starting stream with format: " << currentFormat_.width << "x"
|
||||||
|
<< currentFormat_.height << " @ " << currentFormat_.framerate << " fps" << std::endl;
|
||||||
|
|
||||||
|
// Start camera streaming
|
||||||
|
const int startResult = VxStartStreaming(camera_);
|
||||||
|
if (startResult != 0) {
|
||||||
|
std::cerr << "Failed to start camera streaming (error code: " << startResult << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate buffer (assume worst case: uncompressed)
|
// Allocate buffer (assume worst case: uncompressed)
|
||||||
const size_t calculatedBufferSize = currentFormat_.width * currentFormat_.height * 4;
|
const size_t calculatedBufferSize = currentFormat_.width * currentFormat_.height * 4;
|
||||||
bufferSize_ = calculatedBufferSize;
|
bufferSize_ = calculatedBufferSize;
|
||||||
buffer_ = std::make_unique<uint8_t[]>(bufferSize_);
|
buffer_ = std::make_unique<uint8_t[]>(bufferSize_);
|
||||||
|
|
||||||
|
// Allocate RGB conversion buffer for shared memory
|
||||||
|
rgbBuffer_ = std::make_unique<uint8_t[]>(currentFormat_.width * currentFormat_.height * 3);
|
||||||
|
|
||||||
// Start GStreamer pipeline if configured
|
// Start GStreamer pipeline if configured
|
||||||
if (useGStreamer) {
|
if (useGStreamer) {
|
||||||
gstPipeline_->setPipelineDescription(gstPipeline);
|
gstPipeline_->setPipelineDescription(gstPipeline);
|
||||||
@@ -130,6 +147,18 @@ void StreamingEngine::acquisitionLoop() {
|
|||||||
const VX_CAPTURE_RESULT result = VxGetImage(camera_, buffer_.get(), &dataSize, 1000);
|
const VX_CAPTURE_RESULT result = VxGetImage(camera_, buffer_.get(), &dataSize, 1000);
|
||||||
|
|
||||||
if (result == VX_CAPTURE_RESULT::VX_SUCCESS && dataSize > 0) {
|
if (result == VX_CAPTURE_RESULT::VX_SUCCESS && dataSize > 0) {
|
||||||
|
// Debug: Check buffer immediately after VxGetImage (every 60 frames)
|
||||||
|
static uint64_t captureCount = 0;
|
||||||
|
captureCount++;
|
||||||
|
if (captureCount % 60 == 1) {
|
||||||
|
std::cout << "\n[RAW BUFFER Frame #" << captureCount << "] Right after VxGetImage:" << std::endl;
|
||||||
|
std::cout << "[RAW] First 16 bytes: ";
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
printf("%02x ", buffer_.get()[i]);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// Get timestamp for frame
|
// Get timestamp for frame
|
||||||
const uint64_t timestamp_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
const uint64_t timestamp_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
@@ -147,6 +176,15 @@ void StreamingEngine::acquisitionLoop() {
|
|||||||
|
|
||||||
// Push frame to GStreamer pipeline if active
|
// Push frame to GStreamer pipeline if active
|
||||||
if (gstPipeline_->isRunning()) {
|
if (gstPipeline_->isRunning()) {
|
||||||
|
// Debug: Check buffer before GStreamer push
|
||||||
|
if (captureCount % 60 == 1) {
|
||||||
|
std::cout << "[BEFORE GST] First 16 bytes: ";
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
printf("%02x ", buffer_.get()[i]);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (!gstPipeline_->pushBuffer(buffer_.get(), dataSize,
|
if (!gstPipeline_->pushBuffer(buffer_.get(), dataSize,
|
||||||
currentFormat_.width, currentFormat_.height,
|
currentFormat_.width, currentFormat_.height,
|
||||||
formatStr)) {
|
formatStr)) {
|
||||||
@@ -154,12 +192,68 @@ void StreamingEngine::acquisitionLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push frame to shared memory if enabled
|
// Push frame to shared memory if enabled (always as RGB)
|
||||||
if (shmWriter_ && shmWriter_->isCreated()) {
|
if (shmWriter_ && shmWriter_->isCreated()) {
|
||||||
if (!shmWriter_->writeFrame(buffer_.get(), dataSize,
|
static uint64_t shmFrameCount = 0;
|
||||||
|
shmFrameCount++;
|
||||||
|
|
||||||
|
const uint8_t* frameData = buffer_.get();
|
||||||
|
size_t frameSize = dataSize;
|
||||||
|
std::string outputFormat = "RGB";
|
||||||
|
|
||||||
|
// Debug: Print every 60 frames to see current data
|
||||||
|
if (shmFrameCount % 60 == 1) {
|
||||||
|
std::cout << "\n[DEBUG SHM Frame #" << shmFrameCount << "] Input format: " << formatStr
|
||||||
|
<< " | Size: " << dataSize << std::endl;
|
||||||
|
std::cout << "[DEBUG] First 16 input bytes (hex): ";
|
||||||
|
for (int i = 0; i < 16 && i < dataSize; i++) {
|
||||||
|
printf("%02x ", buffer_.get()[i]);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// For UYVY, show interpretation
|
||||||
|
if (currentFormat_.format == VX_IMAGE_FORMAT::UYVY) {
|
||||||
|
std::cout << "[DEBUG] UYVY: U=" << (int)buffer_.get()[0]
|
||||||
|
<< " Y0=" << (int)buffer_.get()[1]
|
||||||
|
<< " V=" << (int)buffer_.get()[2]
|
||||||
|
<< " Y1=" << (int)buffer_.get()[3] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to RGB if needed
|
||||||
|
if (currentFormat_.format != VX_IMAGE_FORMAT::RGB) {
|
||||||
|
frameSize = convertToRGB(buffer_.get(), rgbBuffer_.get(),
|
||||||
currentFormat_.width, currentFormat_.height,
|
currentFormat_.width, currentFormat_.height,
|
||||||
formatStr, timestamp_ns)) {
|
currentFormat_.format);
|
||||||
|
frameData = rgbBuffer_.get();
|
||||||
|
|
||||||
|
// Debug: Print every 60 frames after conversion
|
||||||
|
if (shmFrameCount % 60 == 1) {
|
||||||
|
std::cout << "[DEBUG] RGB output - First 12 bytes: ";
|
||||||
|
for (int i = 0; i < 12 && i < frameSize; i++) {
|
||||||
|
printf("%02x ", frameData[i]);
|
||||||
|
}
|
||||||
|
std::cout << "\n[DEBUG] First 2 RGB pixels: R=" << (int)frameData[0]
|
||||||
|
<< " G=" << (int)frameData[1] << " B=" << (int)frameData[2]
|
||||||
|
<< " | R=" << (int)frameData[3] << " G=" << (int)frameData[4]
|
||||||
|
<< " B=" << (int)frameData[5] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shmWriter_->writeFrame(frameData, frameSize,
|
||||||
|
currentFormat_.width, currentFormat_.height,
|
||||||
|
outputFormat, timestamp_ns)) {
|
||||||
std::cerr << "Failed to write frame to shared memory" << std::endl;
|
std::cerr << "Failed to write frame to shared memory" << std::endl;
|
||||||
|
} else {
|
||||||
|
// Debug: Print info every 30 frames
|
||||||
|
if (shmFrameCount % 30 == 0) {
|
||||||
|
std::cout << "[DEBUG] SHM: Written " << shmFrameCount << " frames. "
|
||||||
|
<< "Last frame first 8 bytes: ";
|
||||||
|
for (int i = 0; i < 8 && i < frameSize; i++) {
|
||||||
|
printf("%02x ", frameData[i]);
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,3 +317,110 @@ void StreamingEngine::disableSharedMemory() {
|
|||||||
std::cout << "Shared memory disabled" << std::endl;
|
std::cout << "Shared memory disabled" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t StreamingEngine::convertToRGB(const uint8_t* src, uint8_t* dst, const int width, const int height, const VX_IMAGE_FORMAT format) {
|
||||||
|
const size_t rgbSize = width * height * 3;
|
||||||
|
|
||||||
|
// Helper lambda for safe clamping
|
||||||
|
auto clamp = [](int value) -> uint8_t {
|
||||||
|
if (value < 0) return 0;
|
||||||
|
if (value > 255) return 255;
|
||||||
|
return static_cast<uint8_t>(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case VX_IMAGE_FORMAT::BGR: {
|
||||||
|
// BGR to RGB: swap R and B channels
|
||||||
|
for (int i = 0; i < width * height; i++) {
|
||||||
|
dst[i * 3 + 0] = src[i * 3 + 2]; // R = B
|
||||||
|
dst[i * 3 + 1] = src[i * 3 + 1]; // G = G
|
||||||
|
dst[i * 3 + 2] = src[i * 3 + 0]; // B = R
|
||||||
|
}
|
||||||
|
return rgbSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VX_IMAGE_FORMAT::YUY2: {
|
||||||
|
// YUY2 (YUYV) to RGB conversion
|
||||||
|
// Format: Y0 U Y1 V (4 bytes = 2 pixels)
|
||||||
|
for (int i = 0; i < width * height / 2; i++) {
|
||||||
|
const int y0 = src[i * 4 + 0];
|
||||||
|
const int u = src[i * 4 + 1];
|
||||||
|
const int y1 = src[i * 4 + 2];
|
||||||
|
const int v = src[i * 4 + 3];
|
||||||
|
|
||||||
|
const int c0 = y0 - 16;
|
||||||
|
const int c1 = y1 - 16;
|
||||||
|
const int d = u - 128;
|
||||||
|
const int e = v - 128;
|
||||||
|
|
||||||
|
// First pixel (RGB)
|
||||||
|
const int r0 = (298 * c0 + 409 * e + 128) >> 8;
|
||||||
|
const int g0 = (298 * c0 - 100 * d - 208 * e + 128) >> 8;
|
||||||
|
const int b0 = (298 * c0 + 516 * d + 128) >> 8;
|
||||||
|
|
||||||
|
dst[i * 6 + 0] = clamp(r0);
|
||||||
|
dst[i * 6 + 1] = clamp(g0);
|
||||||
|
dst[i * 6 + 2] = clamp(b0);
|
||||||
|
|
||||||
|
// Second pixel (RGB)
|
||||||
|
const int r1 = (298 * c1 + 409 * e + 128) >> 8;
|
||||||
|
const int g1 = (298 * c1 - 100 * d - 208 * e + 128) >> 8;
|
||||||
|
const int b1 = (298 * c1 + 516 * d + 128) >> 8;
|
||||||
|
|
||||||
|
dst[i * 6 + 3] = clamp(r1);
|
||||||
|
dst[i * 6 + 4] = clamp(g1);
|
||||||
|
dst[i * 6 + 5] = clamp(b1);
|
||||||
|
}
|
||||||
|
return rgbSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VX_IMAGE_FORMAT::UYVY: {
|
||||||
|
// UYVY to RGB conversion
|
||||||
|
// Format: U Y0 V Y1 (4 bytes = 2 pixels)
|
||||||
|
for (int i = 0; i < width * height / 2; i++) {
|
||||||
|
const int u = src[i * 4 + 0];
|
||||||
|
const int y0 = src[i * 4 + 1];
|
||||||
|
const int v = src[i * 4 + 2];
|
||||||
|
const int y1 = src[i * 4 + 3];
|
||||||
|
|
||||||
|
const int c0 = y0 - 16;
|
||||||
|
const int c1 = y1 - 16;
|
||||||
|
const int d = u - 128;
|
||||||
|
const int e = v - 128;
|
||||||
|
|
||||||
|
// First pixel (RGB)
|
||||||
|
const int r0 = (298 * c0 + 409 * e + 128) >> 8;
|
||||||
|
const int g0 = (298 * c0 - 100 * d - 208 * e + 128) >> 8;
|
||||||
|
const int b0 = (298 * c0 + 516 * d + 128) >> 8;
|
||||||
|
|
||||||
|
dst[i * 6 + 0] = clamp(r0);
|
||||||
|
dst[i * 6 + 1] = clamp(g0);
|
||||||
|
dst[i * 6 + 2] = clamp(b0);
|
||||||
|
|
||||||
|
// Second pixel (RGB)
|
||||||
|
const int r1 = (298 * c1 + 409 * e + 128) >> 8;
|
||||||
|
const int g1 = (298 * c1 - 100 * d - 208 * e + 128) >> 8;
|
||||||
|
const int b1 = (298 * c1 + 516 * d + 128) >> 8;
|
||||||
|
|
||||||
|
dst[i * 6 + 3] = clamp(r1);
|
||||||
|
dst[i * 6 + 4] = clamp(g1);
|
||||||
|
dst[i * 6 + 5] = clamp(b1);
|
||||||
|
}
|
||||||
|
return rgbSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
case VX_IMAGE_FORMAT::RGB: {
|
||||||
|
// Already RGB, just copy
|
||||||
|
std::memcpy(dst, src, rgbSize);
|
||||||
|
return rgbSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
std::cerr << "Unsupported format for RGB conversion, copying as-is" << std::endl;
|
||||||
|
// Copy as-is for unsupported formats
|
||||||
|
const size_t copySize = std::min(rgbSize, static_cast<size_t>(width * height * 4));
|
||||||
|
std::memcpy(dst, src, copySize);
|
||||||
|
return copySize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ int main() {
|
|||||||
std::cout << "License: CC BY-NC-SA 4.0 -> https://creativecommons.org/licenses/by-nc-sa/4.0/" << std::endl;
|
std::cout << "License: CC BY-NC-SA 4.0 -> https://creativecommons.org/licenses/by-nc-sa/4.0/" << std::endl;
|
||||||
std::cout << "========================================" << std::endl << std::endl;
|
std::cout << "========================================" << std::endl << std::endl;
|
||||||
std::cout << "Control socket: " << socketPath << std::endl;
|
std::cout << "Control socket: " << socketPath << std::endl;
|
||||||
std::cout << "Default pipeline: videoconvert ! autovideosink" << std::endl;
|
std::cout << "Default pipeline: fakesink (no display)" << std::endl;
|
||||||
std::cout << "\nQuick start:" << std::endl;
|
std::cout << "\nQuick start:" << std::endl;
|
||||||
std::cout << R"( echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:)" << socketPath << std::endl;
|
std::cout << R"( echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:)" << socketPath << std::endl;
|
||||||
std::cout << "\nTo change pipeline before starting:" << std::endl;
|
std::cout << "\nTo change pipeline before starting:" << std::endl;
|
||||||
|
|||||||
119
watchdog.sh
Executable file
119
watchdog.sh
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# VizionStreamer Watchdog Script
|
||||||
|
# Automatically restarts the streamer if it crashes
|
||||||
|
# Usage: ./watchdog.sh
|
||||||
|
|
||||||
|
STREAMER_PATH="./build/vizionStreamer"
|
||||||
|
CHECK_INTERVAL=5 # Seconds between checks
|
||||||
|
LOG_FILE="./watchdog.log"
|
||||||
|
PID_FILE="./vizionStreamer.pid"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to log messages
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to start the streamer
|
||||||
|
start_streamer() {
|
||||||
|
log "${GREEN}Starting VizionStreamer...${NC}"
|
||||||
|
$STREAMER_PATH &
|
||||||
|
STREAMER_PID=$!
|
||||||
|
echo $STREAMER_PID > "$PID_FILE"
|
||||||
|
log "VizionStreamer started with PID: $STREAMER_PID"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if process is running
|
||||||
|
is_running() {
|
||||||
|
if [ -f "$PID_FILE" ]; then
|
||||||
|
PID=$(cat "$PID_FILE")
|
||||||
|
if ps -p $PID > /dev/null 2>&1; then
|
||||||
|
return 0 # Running
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1 # Not running
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to stop the streamer
|
||||||
|
stop_streamer() {
|
||||||
|
if [ -f "$PID_FILE" ]; then
|
||||||
|
PID=$(cat "$PID_FILE")
|
||||||
|
if ps -p $PID > /dev/null 2>&1; then
|
||||||
|
log "${YELLOW}Stopping VizionStreamer (PID: $PID)...${NC}"
|
||||||
|
kill $PID
|
||||||
|
sleep 2
|
||||||
|
# Force kill if still running
|
||||||
|
if ps -p $PID > /dev/null 2>&1; then
|
||||||
|
log "${RED}Force killing VizionStreamer...${NC}"
|
||||||
|
kill -9 $PID
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f "$PID_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Trap SIGINT and SIGTERM to cleanly shutdown
|
||||||
|
cleanup() {
|
||||||
|
log "${YELLOW}Watchdog shutting down...${NC}"
|
||||||
|
stop_streamer
|
||||||
|
log "${GREEN}Watchdog stopped${NC}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup SIGINT SIGTERM
|
||||||
|
|
||||||
|
# Check if vizionStreamer executable exists
|
||||||
|
if [ ! -f "$STREAMER_PATH" ]; then
|
||||||
|
log "${RED}ERROR: VizionStreamer not found at $STREAMER_PATH${NC}"
|
||||||
|
log "Please build the project first: cmake --build build"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Main watchdog loop
|
||||||
|
log "${GREEN}=== VizionStreamer Watchdog Started ===${NC}"
|
||||||
|
log "Check interval: ${CHECK_INTERVAL} seconds"
|
||||||
|
log "Press Ctrl+C to stop"
|
||||||
|
|
||||||
|
RESTART_COUNT=0
|
||||||
|
|
||||||
|
# Start streamer initially
|
||||||
|
start_streamer
|
||||||
|
sleep 2 # Give it time to start
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sleep $CHECK_INTERVAL
|
||||||
|
|
||||||
|
if ! is_running; then
|
||||||
|
RESTART_COUNT=$((RESTART_COUNT + 1))
|
||||||
|
log "${RED}VizionStreamer died! Restart count: $RESTART_COUNT${NC}"
|
||||||
|
|
||||||
|
# Optional: limit restart attempts
|
||||||
|
if [ $RESTART_COUNT -gt 10 ]; then
|
||||||
|
log "${RED}ERROR: Too many restarts (10). Giving up.${NC}"
|
||||||
|
log "Check the logs for recurring errors."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up old PID file
|
||||||
|
rm -f "$PID_FILE"
|
||||||
|
|
||||||
|
# Wait a bit before restarting to avoid rapid restart loops
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
start_streamer
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
# Still running - reset restart counter on successful check
|
||||||
|
if [ $RESTART_COUNT -gt 0 ]; then
|
||||||
|
RESTART_COUNT=0
|
||||||
|
log "${GREEN}VizionStreamer stable again${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user