#pragma once #include #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, const std::string& profile) { if (profile == "legacy") { 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; return; } heartbeat_up = "/zxwl/sweeper/" + vehId + "/video/status"; video_down = "/zxwl/sweeper/" + vehId + "/video/query"; record_query = "/zxwl/sweeper/" + vehId + "/record/query"; record_play = "/zxwl/sweeper/" + vehId + "/record/play"; } }; 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; std::string topic_profile = "protocol"; int keep_alive = 2000; int qos = 1; bool clean_session = true; VehicleMQTTTopics topics; }; struct SRSConfig { struct LiveProfile { std::string push_host; int push_rtmp_port = 0; std::string playback_scheme = "http"; std::string playback_host; int playback_http_api_port = 0; std::string playback_rtc_eip; std::string vhost = "live"; }; 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_push_profile = "wan_194"; std::string live_playback_profile = "wan_194"; 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"; std::map live_profiles = { {"lan_194", {"192.168.4.194", 1935, "http", "192.168.4.194", 11985, "192.168.4.194:8000", "live"}}, {"wan_194", {"36.153.162.171", 19435, "http", "36.153.162.171", 11985, "36.153.162.171:8000", "live"}}, }; }; 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.topic_profile = m.value("topic_profile", cfg.mqtt.topic_profile); cfg.mqtt.keep_alive = m.value("mqtt_heart_threshold", cfg.mqtt.keep_alive); cfg.mqtt.topics.fill_with_veh_id(cfg.mqtt.vehicle_id, cfg.mqtt.topic_profile); 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_push_profile = s.value("live_push_profile", cfg.srs.live_push_profile); cfg.srs.live_playback_profile = s.value("live_playback_profile", cfg.srs.live_playback_profile); 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 (s.contains("live_profiles") && s["live_profiles"].is_object()) { cfg.srs.live_profiles.clear(); for (auto it = s["live_profiles"].begin(); it != s["live_profiles"].end(); ++it) { if (!it.value().is_object()) continue; SRSConfig::LiveProfile profile; const auto& p = it.value(); profile.push_host = p.value("push_host", profile.push_host); profile.push_rtmp_port = p.value("push_rtmp_port", profile.push_rtmp_port); profile.playback_scheme = p.value("playback_scheme", profile.playback_scheme); profile.playback_host = p.value("playback_host", profile.playback_host); profile.playback_http_api_port = p.value("playback_http_api_port", profile.playback_http_api_port); profile.playback_rtc_eip = p.value("playback_rtc_eip", profile.playback_rtc_eip); profile.vhost = p.value("vhost", profile.vhost); cfg.srs.live_profiles[it.key()] = profile; } } } 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 topic profile: " + cfg.mqtt.topic_profile); 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;