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:
|
||||
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::unique_ptr<GStreamerPipeline> gstPipeline_;
|
||||
@@ -48,5 +49,6 @@ private:
|
||||
std::mutex mutex_;
|
||||
VxFormat currentFormat_;
|
||||
std::unique_ptr<uint8_t[]> buffer_;
|
||||
std::unique_ptr<uint8_t[]> rgbBuffer_; // Buffer for RGB conversion
|
||||
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>
|
||||
|
||||
CameraController::CameraController(std::shared_ptr<VxCamera> camera)
|
||||
: camera_(camera), gstPipeline_("videoconvert ! autovideosink") {
|
||||
: camera_(camera), gstPipeline_("fakesink") {
|
||||
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 long expValue = value.empty() ? 0 : std::stol(value);
|
||||
|
||||
if (VxSetUVCImageProcessing(camera_, VX_UVC_IMAGE_PROPERTIES::UVC_IMAGE_EXPOSURE,
|
||||
expValue, flag) != 0) {
|
||||
std::cout << "[DEBUG] Setting exposure: mode=" << mode << " (flag=" << flag
|
||||
<< "), 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");
|
||||
}
|
||||
|
||||
@@ -249,8 +256,15 @@ std::string CameraController::handleSetGamma(const std::string& value) {
|
||||
std::string CameraController::handleSetGain(const std::string& value) {
|
||||
try {
|
||||
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 createSuccessResponse("Gain set successfully");
|
||||
|
||||
@@ -128,6 +128,19 @@ bool SharedMemoryWriter::writeFrame(const uint8_t* data, const size_t size,
|
||||
// 3. Copy frame data
|
||||
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)
|
||||
header->write_sequence.fetch_add(1, std::memory_order_release);
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
StreamingEngine::StreamingEngine(std::shared_ptr<VxCamera> camera)
|
||||
: camera_(std::move(camera)), running_(false), currentFormat_(), bufferSize_(0) {
|
||||
@@ -44,26 +46,41 @@ bool StreamingEngine::start(const std::string& gstPipeline) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start camera streaming
|
||||
if (VxStartStreaming(camera_) != 0) {
|
||||
std::cerr << "Failed to start camera streaming" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Ensure camera is not already streaming (clean slate)
|
||||
VxStopStreaming(camera_);
|
||||
|
||||
// Get current format to allocate buffer
|
||||
// Get current format to allocate buffer BEFORE starting streaming
|
||||
std::vector<VxFormat> fmtList;
|
||||
if (VxGetFormatList(camera_, fmtList) != 0 || fmtList.empty()) {
|
||||
std::cerr << "Failed to get format list" << std::endl;
|
||||
VxStopStreaming(camera_);
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
const size_t calculatedBufferSize = currentFormat_.width * currentFormat_.height * 4;
|
||||
bufferSize_ = calculatedBufferSize;
|
||||
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
|
||||
if (useGStreamer) {
|
||||
gstPipeline_->setPipelineDescription(gstPipeline);
|
||||
@@ -130,6 +147,18 @@ void StreamingEngine::acquisitionLoop() {
|
||||
const VX_CAPTURE_RESULT result = VxGetImage(camera_, buffer_.get(), &dataSize, 1000);
|
||||
|
||||
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
|
||||
const uint64_t timestamp_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
@@ -147,6 +176,15 @@ void StreamingEngine::acquisitionLoop() {
|
||||
|
||||
// Push frame to GStreamer pipeline if active
|
||||
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,
|
||||
currentFormat_.width, currentFormat_.height,
|
||||
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_->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,
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 << "========================================" << std::endl << 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 << R"( echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:)" << socketPath << 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