#pragma once #include #include #include #include #include #include #include #include "logger.hpp" using json = nlohmann::json; using ordered_json = nlohmann::ordered_json; enum class StreamType { MAIN, SUB }; struct Camera { std::string device; std::string name; int width = 1280; int height = 720; int fps = 30; int bitrate = 2000000; bool enabled = false; StreamType stream_type = StreamType::MAIN; }; struct VehicleMQTTTopics { std::string heartbeat_up; std::string video_down; std::string record_query; std::string record_play; void fill_with_veh_id(const std::string& vehId) { heartbeat_up = "/kun/vehicle/video/status/" + vehId; video_down = "/kun/vehicle/video/request/" + vehId; record_query = "/kun/vehicle/video/record/query/" + vehId; record_play = "/kun/vehicle/video/record/play/" + vehId; } }; struct MQTTConfig { std::string vehicle_id; std::string server_ip; int server_port = 1883; bool need_username_pwd = true; std::string client_id; std::string username; std::string password; int keep_alive = 2000; int qos = 1; bool clean_session = true; VehicleMQTTTopics topics; }; struct SRSConfig { std::string root = "/opt/vehicle-video-service/srs"; std::string record_config = "/opt/vehicle-video-service/srs/conf/record.conf"; std::string stream_app = "camera"; bool live_enabled = true; bool record_enabled = true; std::string live_host = "127.0.0.1"; int live_rtmp_port = 1935; int live_http_api_port = 1985; int live_http_server_port = 8080; int live_rtc_port = 8000; std::string live_vhost = "live"; std::string record_host = "127.0.0.1"; int record_rtmp_port = 2935; int record_http_api_port = 2985; int record_http_server_port = 2980; std::string record_vhost = "record"; std::string record_path = "/media/record"; int dvr_duration_sec = 60; int retention_days = 14; double usage_threshold = 0.90; std::string public_interface = "enP2p33s0"; }; struct WebConfig { bool enabled = true; std::string bind = "0.0.0.0"; int port = 18080; std::string username = "admin"; std::string password = "admin123"; }; struct AppConfig { std::vector cameras; MQTTConfig mqtt; SRSConfig srs; WebConfig web; static AppConfig load_from_file(const std::string& filepath) { AppConfig cfg; std::ifstream ifs(filepath); if (!ifs.is_open()) { LOG_ERROR("[Config] Failed to open config file: " + filepath); throw std::runtime_error("Failed to open config file: " + filepath); } json j; ifs >> j; if (j.contains("cameras")) { for (auto& c : j["cameras"]) { Camera cam; cam.device = c.value("device", ""); cam.name = c.value("name", ""); cam.width = c.value("width", cam.width); cam.height = c.value("height", cam.height); cam.fps = c.value("fps", cam.fps); cam.bitrate = c.value("bitrate", cam.bitrate); cam.enabled = c.value("enabled", cam.enabled); cfg.cameras.push_back(cam); LOG_INFO("[Config] Loaded camera: " + cam.name + " (" + cam.device + "), enabled=" + std::to_string(cam.enabled) + ", bitrate=" + std::to_string(cam.bitrate)); } } if (!j.contains("mqtt_server")) { LOG_ERROR("[Config] Missing 'mqtt_server' section"); throw std::runtime_error("Config file missing 'mqtt_server'"); } auto& m = j["mqtt_server"]; cfg.mqtt.vehicle_id = std::to_string(m.value("veh_id", 0)); cfg.mqtt.server_ip = m.value("address", ""); cfg.mqtt.server_port = m.value("port", cfg.mqtt.server_port); cfg.mqtt.need_username_pwd = m.value("need_username_pwd", cfg.mqtt.need_username_pwd); cfg.mqtt.client_id = m.value("client_id", ""); cfg.mqtt.username = m.value("username", ""); cfg.mqtt.password = m.value("password", ""); cfg.mqtt.keep_alive = m.value("mqtt_heart_threshold", cfg.mqtt.keep_alive); cfg.mqtt.topics.fill_with_veh_id(cfg.mqtt.vehicle_id); if (j.contains("srs")) { auto& s = j["srs"]; cfg.srs.root = s.value("root", cfg.srs.root); cfg.srs.record_config = s.value("record_config", cfg.srs.record_config); cfg.srs.stream_app = s.value("stream_app", cfg.srs.stream_app); cfg.srs.live_enabled = s.value("live_enabled", cfg.srs.live_enabled); cfg.srs.record_enabled = s.value("record_enabled", cfg.srs.record_enabled); cfg.srs.live_host = s.value("live_host", cfg.srs.live_host); cfg.srs.live_rtmp_port = s.value("live_rtmp_port", cfg.srs.live_rtmp_port); cfg.srs.live_http_api_port = s.value("live_http_api_port", cfg.srs.live_http_api_port); cfg.srs.live_http_server_port = s.value("live_http_server_port", cfg.srs.live_http_server_port); cfg.srs.live_rtc_port = s.value("live_rtc_port", cfg.srs.live_rtc_port); cfg.srs.live_vhost = s.value("live_vhost", cfg.srs.live_vhost); cfg.srs.record_host = s.value("record_host", cfg.srs.record_host); cfg.srs.record_rtmp_port = s.value("record_rtmp_port", cfg.srs.record_rtmp_port); cfg.srs.record_http_api_port = s.value("record_http_api_port", cfg.srs.record_http_api_port); cfg.srs.record_http_server_port = s.value("record_http_server_port", cfg.srs.record_http_server_port); cfg.srs.record_vhost = s.value("record_vhost", cfg.srs.record_vhost); cfg.srs.record_path = s.value("record_path", cfg.srs.record_path); cfg.srs.dvr_duration_sec = s.value("dvr_duration_sec", cfg.srs.dvr_duration_sec); cfg.srs.retention_days = s.value("retention_days", cfg.srs.retention_days); cfg.srs.usage_threshold = s.value("usage_threshold", cfg.srs.usage_threshold); cfg.srs.public_interface = s.value("public_interface", cfg.srs.public_interface); } if (j.contains("web")) { auto& w = j["web"]; cfg.web.enabled = w.value("enabled", cfg.web.enabled); cfg.web.bind = w.value("bind", cfg.web.bind); cfg.web.port = w.value("port", cfg.web.port); cfg.web.username = w.value("username", cfg.web.username); cfg.web.password = w.value("password", cfg.web.password); } LOG_INFO("[Config] Loaded MQTT server: " + cfg.mqtt.server_ip + ":" + std::to_string(cfg.mqtt.server_port)); LOG_INFO("[Config] MQTT client ID: " + cfg.mqtt.client_id); LOG_INFO("[Config] MQTT Credentials - username: " + cfg.mqtt.username + ", password: " + cfg.mqtt.password); LOG_INFO("[Config] MQTT Topics: " + cfg.mqtt.topics.heartbeat_up + ", " + cfg.mqtt.topics.video_down); LOG_INFO("[Config] MQTT keepAlive: " + std::to_string(cfg.mqtt.keep_alive)); LOG_INFO("[Config] SRS root: " + cfg.srs.root); LOG_INFO("[Config] SRS record config: " + cfg.srs.record_config); return cfg; } }; inline std::string get_executable_dir() { char result[PATH_MAX] = {0}; ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); if (count == -1) { LOG_ERROR("[Config] Failed to read /proc/self/exe"); throw std::runtime_error("Failed to read /proc/self/exe"); } std::string full_path(result, count); auto pos = full_path.find_last_of('/'); if (pos == std::string::npos) { LOG_ERROR("[Config] Failed to find executable directory"); throw std::runtime_error("Failed to find executable directory"); } return full_path.substr(0, pos); } inline std::string get_executable_dir_file_path(const std::string& filename) { std::string dir = get_executable_dir(); if (dir.back() != '/') dir += '/'; return dir + filename; } extern AppConfig g_app_config;