diff --git a/README.md b/README.md index 2486275..017279f 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,32 @@ cmake --build build ./build/vizionStreamer ``` +### Multi-Camera + +Run one instance per camera, each with its own camera index and socket path: + +```bash +./build/vizionStreamer --camera 0 --socket /tmp/vizion_cam0.sock & +./build/vizionStreamer --camera 1 --socket /tmp/vizion_cam1.sock & +``` + +Point the control scripts at a specific instance with `VIZION_SOCKET`: + +```bash +VIZION_SOCKET=/tmp/vizion_cam0.sock ./scripts/start_stream.sh +VIZION_SOCKET=/tmp/vizion_cam1.sock ./scripts/set_pipeline_udp.sh 192.168.1.100 5001 +``` + +Without the env var, scripts default to `/tmp/vizion_control.sock` (single-camera usage). + +CLI options: + +``` +-c, --camera Camera index to open (default: auto-detect VCI-AR0234, else 0) +-s, --socket Control socket path (default: /tmp/vizion_control.sock) +-h, --help Show help +``` + ### Start Streaming (Default Pipeline) In another terminal: diff --git a/scripts/get_formats.sh b/scripts/get_formats.sh index c7a6cc6..ee09d61 100755 --- a/scripts/get_formats.sh +++ b/scripts/get_formats.sh @@ -1,6 +1,6 @@ #!/bin/bash # Get available camera formats -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" echo '{"command":"get_formats"}' | socat - UNIX-CONNECT:$SOCKET | python3 -m json.tool 2>/dev/null || echo '{"command":"get_formats"}' | socat - UNIX-CONNECT:$SOCKET diff --git a/scripts/get_status.sh b/scripts/get_status.sh index 06de8b4..81efb8a 100755 --- a/scripts/get_status.sh +++ b/scripts/get_status.sh @@ -1,6 +1,6 @@ #!/bin/bash # Get streaming status -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" echo '{"command":"get_status"}' | socat - UNIX-CONNECT:$SOCKET | python3 -m json.tool 2>/dev/null || echo '{"command":"get_status"}' | socat - UNIX-CONNECT:$SOCKET diff --git a/scripts/set_pipeline_display.sh b/scripts/set_pipeline_display.sh index 93a8450..efe9833 100755 --- a/scripts/set_pipeline_display.sh +++ b/scripts/set_pipeline_display.sh @@ -1,7 +1,7 @@ #!/bin/bash # Set pipeline for local display (default) -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" echo "Setting display pipeline (default)..." echo '{"command":"set_pipeline","params":{"pipeline":"videoconvert ! autovideosink"}}' | socat - UNIX-CONNECT:$SOCKET diff --git a/scripts/set_pipeline_file.sh b/scripts/set_pipeline_file.sh index 6822fc9..d5c8602 100755 --- a/scripts/set_pipeline_file.sh +++ b/scripts/set_pipeline_file.sh @@ -1,7 +1,7 @@ #!/bin/bash # Set pipeline for recording to file -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" FILE="${1:-/tmp/vizion_recording.mp4}" echo "Setting file recording pipeline to $FILE..." diff --git a/scripts/set_pipeline_mjpeg.sh b/scripts/set_pipeline_mjpeg.sh index 35665bb..b726db1 100755 --- a/scripts/set_pipeline_mjpeg.sh +++ b/scripts/set_pipeline_mjpeg.sh @@ -1,7 +1,7 @@ #!/bin/bash # Set pipeline for MJPEG HTTP streaming -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" PORT="${1:-8080}" # Check if gst-plugins-good with souphttpsink is available diff --git a/scripts/set_pipeline_mjpeg_passthrough.sh b/scripts/set_pipeline_mjpeg_passthrough.sh index 64468ca..b7e02d5 100755 --- a/scripts/set_pipeline_mjpeg_passthrough.sh +++ b/scripts/set_pipeline_mjpeg_passthrough.sh @@ -2,7 +2,7 @@ # Optimized pipeline for cameras that already output MJPEG format # Passes through MJPEG data without re-encoding -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" PORT="${1:-8080}" # Check if gst-plugins-good with souphttpsink is available diff --git a/scripts/set_pipeline_udp.sh b/scripts/set_pipeline_udp.sh index d7ba913..eef73a9 100755 --- a/scripts/set_pipeline_udp.sh +++ b/scripts/set_pipeline_udp.sh @@ -1,7 +1,7 @@ #!/bin/bash # Set pipeline for UDP streaming (H.264) -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" HOST="${1:-192.168.1.100}" PORT="${2:-5000}" diff --git a/scripts/start_stream.sh b/scripts/start_stream.sh index cae0433..e96fe09 100755 --- a/scripts/start_stream.sh +++ b/scripts/start_stream.sh @@ -1,7 +1,7 @@ #!/bin/bash # Quick start script - starts streaming with default pipeline -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" echo "Starting stream with default pipeline (videoconvert ! autovideosink)..." echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:$SOCKET diff --git a/scripts/stop_stream.sh b/scripts/stop_stream.sh index 71caa14..61c26cc 100755 --- a/scripts/stop_stream.sh +++ b/scripts/stop_stream.sh @@ -1,7 +1,7 @@ #!/bin/bash # Stop streaming -SOCKET="/tmp/vizion_control.sock" +SOCKET="${VIZION_SOCKET:-/tmp/vizion_control.sock}" echo "Stopping stream..." echo '{"command":"stop_stream"}' | socat - UNIX-CONNECT:$SOCKET diff --git a/src/main.cpp b/src/main.cpp index 8ae9bef..67d8fe9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,11 +21,65 @@ void signalHandler(int signal) { g_running = false; } -int main() { +static void printUsage() { + std::cout << "Usage: vizionStreamer [options]\n" + << " -c, --camera Camera index to open (default: auto-detect VCI-AR0234, else 0)\n" + << " -s, --socket Control socket path (default: /tmp/vizion_control.sock)\n" + << " -h, --help Show this help\n" + << "\n" + << "Run multiple instances for multiple cameras:\n" + << " ./vizionStreamer --camera 0 --socket /tmp/vizion_cam0.sock\n" + << " ./vizionStreamer --camera 1 --socket /tmp/vizion_cam1.sock\n"; +} + +int main(int argc, char* argv[]) { // Setup signal handlers for clean shutdown signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); + // Parse CLI arguments + int cameraIndex = -1; // -1 = auto-detect + std::string socketPath = "/tmp/vizion_control.sock"; + + bool argsOk = true; + try { + for (int i = 1; i < argc && argsOk; i++) { + const std::string arg = argv[i]; + if (arg == "-h" || arg == "--help") { + printUsage(); + return 0; + } else if (arg == "-c" || arg == "--camera") { + if (i + 1 >= argc) { + std::cerr << "Missing value for " << arg << std::endl; + argsOk = false; + } else { + cameraIndex = std::stoi(argv[++i]); + } + } else if (arg.rfind("--camera=", 0) == 0) { + cameraIndex = std::stoi(arg.substr(9)); + } else if (arg == "-s" || arg == "--socket") { + if (i + 1 >= argc) { + std::cerr << "Missing value for " << arg << std::endl; + argsOk = false; + } else { + socketPath = argv[++i]; + } + } else if (arg.rfind("--socket=", 0) == 0) { + socketPath = arg.substr(8); + } else { + std::cerr << "Unknown argument: " << arg << std::endl; + argsOk = false; + } + } + } catch (const std::exception& e) { + std::cerr << "Invalid argument: " << e.what() << std::endl; + argsOk = false; + } + if (!argsOk) { + printUsage(); + return -1; + } + // List available cameras std::vector devList; const int deviceCount = VxDiscoverCameraDevices(devList); @@ -40,19 +94,26 @@ int main() { std::cout << "[" << i << "] " << devList[i] << std::endl; } - // Find VCI-AR0234 camera - int cameraIndex = -1; - for (size_t i = 0; i < devList.size(); i++) { - if (devList[i].find("VCI-AR0234") != std::string::npos) { - cameraIndex = static_cast(i); - std::cout << "Selected camera [" << cameraIndex << "]: " << devList[i] << std::endl; - break; + if (cameraIndex >= 0) { + if (cameraIndex >= deviceCount) { + std::cout << "Camera index " << cameraIndex << " out of range (0-" << deviceCount - 1 << ")" << std::endl; + return -1; + } + std::cout << "Selected camera [" << cameraIndex << "]: " << devList[cameraIndex] << std::endl; + } else { + // Auto-detect: prefer VCI-AR0234, fall back to index 0 + for (size_t i = 0; i < devList.size(); i++) { + if (devList[i].find("VCI-AR0234") != std::string::npos) { + cameraIndex = static_cast(i); + std::cout << "Selected camera [" << cameraIndex << "]: " << devList[i] << std::endl; + break; + } } - } - if (cameraIndex == -1) { - std::cout << "VCI-AR0234 camera not found, using default (index 0)" << std::endl; - cameraIndex = 0; + if (cameraIndex == -1) { + std::cout << "VCI-AR0234 camera not found, using default (index 0)" << std::endl; + cameraIndex = 0; + } } // Open camera @@ -81,7 +142,6 @@ int main() { const auto controller = std::make_shared(cam); - const std::string socketPath = "/tmp/vizion_control.sock"; SocketServer server(socketPath); if (!server.start([controller](const std::string& cmd) {