Program Listing for File cli_main.cpp

Program Listing for File cli_main.cpp#

Return to documentation for file (Src/cli_main.cpp)

#include <chrono>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <thread>

import kataglyphis.inference;
import kataglyphis.webrtc_streamer;
import kataglyphis.config_loader;

namespace {
volatile std::sig_atomic_t g_running = 1;

void signal_handler(int  /*signal*/) { g_running = 0; }

void print_usage(const char *program)
{
    std::cout << "Usage: " << program << " [OPTIONS]\n"
              << "\nOptions:\n"
              << "  --help, -h              Show this help message\n"
              << "  --version, -v           Show version information\n"
              << "  --webrtc                Start WebRTC streaming\n"
              << "  --config <path>         Load settings from JSON config file\n"
              << "  --server <uri>          Signalling server URI (default: ws://127.0.0.1:8443)\n"
              << "  --source <type>         Video source: libcamera, v4l2, test (default: libcamera)\n"
              << "  --device <path>         V4L2 device path (default: /dev/video0)\n"
              << "  --width <pixels>        Video width (default: 1280)\n"
              << "  --height <pixels>       Video height (default: 720)\n"
              << "  --fps <rate>            Framerate (default: 30)\n"
              << "  --encoder <type>        Encoder: h264-hw, h264-sw, vp8, vp9 (default: h264-hw)\n"
              << "  --bitrate <kbps>        Bitrate in kbps (default: 2000)\n"
              << "\nExamples:\n"
              << "  " << program << " --webrtc --config /path/to/webrtc_settings.json\n"
              << "  " << program << " --webrtc --server ws://192.168.1.100:8443\n"
              << "  " << program << " --webrtc --source v4l2 --device /dev/video0\n"
              << "  " << program << " --webrtc --source test  # For testing without camera\n";
}

auto state_to_string(kataglyphis::webrtc::StreamState state) -> const char *
{
    using kataglyphis::webrtc::StreamState;
    switch (state) {
    case StreamState::Idle:
        return "Idle";
    case StreamState::Connecting:
        return "Connecting";
    case StreamState::Negotiating:
        return "Negotiating";
    case StreamState::Streaming:
        return "Streaming";
    case StreamState::Paused:
        return "Paused";
    case StreamState::Error:
        return "Error";
    case StreamState::Disconnected:
        return "Disconnected";
    }
    return "Unknown";
}
}// namespace

auto main(int argc, char **argv) -> int
{
    mylib::MyCalculator calculator;

    // Parse arguments
    bool show_help = false;
    bool show_version = false;
    bool start_webrtc = false;
    std::string config_file_path;

    kataglyphis::webrtc::StreamConfig config;
    config.signalling_server_uri = "ws://127.0.0.1:8443";

    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "--help") == 0 || std::strcmp(argv[i], "-h") == 0) {
            show_help = true;
        } else if (std::strcmp(argv[i], "--version") == 0 || std::strcmp(argv[i], "-v") == 0) {
            show_version = true;
        } else if (std::strcmp(argv[i], "--webrtc") == 0) {
            start_webrtc = true;
        } else if (std::strcmp(argv[i], "--config") == 0 && i + 1 < argc) {
            config_file_path = argv[++i];
        } else if (std::strcmp(argv[i], "--server") == 0 && i + 1 < argc) {
            config.signalling_server_uri = argv[++i];
        } else if (std::strcmp(argv[i], "--source") == 0 && i + 1 < argc) {
            ++i;
            if (std::strcmp(argv[i], "libcamera") == 0) {
                config.source = kataglyphis::webrtc::VideoSource::Libcamera;
            } else if (std::strcmp(argv[i], "v4l2") == 0) {
                config.source = kataglyphis::webrtc::VideoSource::V4L2;
            } else if (std::strcmp(argv[i], "test") == 0) {
                config.source = kataglyphis::webrtc::VideoSource::TestPattern;
            } else {
                std::cerr << "Unknown source: " << argv[i] << '\n';
                return 1;
            }
        } else if (std::strcmp(argv[i], "--device") == 0 && i + 1 < argc) {
            config.v4l2_device = argv[++i];
        } else if (std::strcmp(argv[i], "--width") == 0 && i + 1 < argc) {
            config.width = static_cast<std::uint32_t>(std::atoi(argv[++i]));
        } else if (std::strcmp(argv[i], "--height") == 0 && i + 1 < argc) {
            config.height = static_cast<std::uint32_t>(std::atoi(argv[++i]));
        } else if (std::strcmp(argv[i], "--fps") == 0 && i + 1 < argc) {
            config.framerate = static_cast<std::uint32_t>(std::atoi(argv[++i]));
        } else if (std::strcmp(argv[i], "--encoder") == 0 && i + 1 < argc) {
            ++i;
            if (std::strcmp(argv[i], "h264-hw") == 0) {
                config.encoder = kataglyphis::webrtc::VideoEncoder::H264_Hardware;
            } else if (std::strcmp(argv[i], "h264-sw") == 0) {
                config.encoder = kataglyphis::webrtc::VideoEncoder::H264_Software;
            } else if (std::strcmp(argv[i], "vp8") == 0) {
                config.encoder = kataglyphis::webrtc::VideoEncoder::VP8;
            } else if (std::strcmp(argv[i], "vp9") == 0) {
                config.encoder = kataglyphis::webrtc::VideoEncoder::VP9;
            } else {
                std::cerr << "Unknown encoder: " << argv[i] << '\n';
                return 1;
            }
        } else if (std::strcmp(argv[i], "--bitrate") == 0 && i + 1 < argc) {
            config.bitrate_kbps = static_cast<std::uint32_t>(std::atoi(argv[++i]));
        }
    }

    if (show_help) {
        print_usage(argv[0]);
        return 0;
    }

    if (show_version) {
        std::cout << "KataglyphisCppInference " << calculator.version() << '\n';
        return 0;
    }

    if (!start_webrtc) {
        // Default behavior: show version
        std::cout << "KataglyphisCppInference " << calculator.version() << '\n';
        std::cout << "Use --help for usage information\n";
        return 0;
    }

    // Initialize WebRTC streaming
    std::cout << "Initializing WebRTC streaming...\n";

    auto init_result = kataglyphis::webrtc::WebRTCStreamer::initialize(&argc, &argv);
    if (!init_result) {
        std::cerr << "Failed to initialize GStreamer for WebRTC\n";
        return 1;
    }

    // Load configuration from JSON file if provided
    if (!config_file_path.empty()) {
        std::cout << "Loading configuration from: " << config_file_path << '\n';
        auto webrtc_config_result = kataglyphis::config::load_webrtc_config(config_file_path);
        if (webrtc_config_result) {
            const auto &webrtc_config = webrtc_config_result.value();
            // Apply JSON config values (CLI args will override these if specified)
            if (config.signalling_server_uri == "ws://127.0.0.1:8443") {
                config.signalling_server_uri = webrtc_config.signaling_server_url;
            }
            if (config.width == 1280) { config.width = webrtc_config.video.default_width; }
            if (config.height == 720) { config.height = webrtc_config.video.default_height; }
            if (config.framerate == 30) { config.framerate = webrtc_config.video.default_framerate; }
            if (config.bitrate_kbps == 2000) { config.bitrate_kbps = webrtc_config.video.default_bitrate_kbps; }
            config.stun_servers = webrtc_config.stun_servers;
            config.turn_servers = webrtc_config.turn_servers;
            std::cout << "Configuration loaded successfully\n";
        } else {
            std::cerr << "Warning: Failed to load config file, using defaults\n";
        }
    }

    kataglyphis::webrtc::WebRTCStreamer streamer;

    // Set up callbacks
    streamer.set_state_callback(
      [](kataglyphis::webrtc::StreamState old_state, kataglyphis::webrtc::StreamState new_state) -> void {
          std::cout << "State: " << state_to_string(old_state) << " -> " << state_to_string(new_state) << '\n';
      });

    streamer.set_error_callback([](kataglyphis::webrtc::WebRTCError  /*error*/, const std::string &message) -> void {
        std::cerr << "Error: " << message << '\n';
    });

    // Configure streamer
    auto configure_result = streamer.configure(config);
    if (!configure_result) {
        std::cerr << "Failed to configure WebRTC streamer\n";
        return 1;
    }

    std::cout << "Connecting to signalling server: " << config.signalling_server_uri << '\n';
    std::cout << "Producer ID: " << streamer.get_producer_id() << '\n';
    std::cout << "Resolution: " << config.width << "x" << config.height << "@" << config.framerate << "fps\n";

    // Start streaming
    auto start_result = streamer.start();
    if (!start_result) {
        std::cerr << "Failed to start WebRTC stream\n";
        return 1;
    }

    // Set up signal handler for graceful shutdown
    std::signal(SIGINT, signal_handler);
    std::signal(SIGTERM, signal_handler);

    std::cout << "Streaming... Press Ctrl+C to stop.\n";

    // Main loop - wait for signal
    while (g_running != 0) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

        // Check if still streaming
        auto state = streamer.get_state();
        if (state == kataglyphis::webrtc::StreamState::Error
            || state == kataglyphis::webrtc::StreamState::Disconnected) {
            std::cerr << "Stream ended unexpectedly\n";
            break;
        }
    }

    std::cout << "\nStopping stream...\n";
    streamer.stop();

    kataglyphis::webrtc::WebRTCStreamer::deinitialize();

    std::cout << "Done.\n";
    return 0;
}