debug shared_buffer stream

This commit is contained in:
Maik Jurischka
2026-01-30 16:29:19 +01:00
parent cc97528eca
commit 9f0eac870f
12 changed files with 805 additions and 16 deletions

17
fix_exposure.sh Executable file
View 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."

View File

@@ -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
View 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
View 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
View 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."

View 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
View 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

View File

@@ -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");

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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
View 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