From 1e77b39d69c7e34a4178de217ea1c6c253d82892 Mon Sep 17 00:00:00 2001 From: GuanYuankai Date: Mon, 27 Oct 2025 03:06:06 +0000 Subject: [PATCH] =?UTF-8?q?4=E8=B7=AF=E8=A7=86=E9=A2=91=E9=87=87=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + config/config.json | 34 ++++++++ config/edge_proxy_data.db | Bin 32768 -> 32768 bytes src/config/config_manager.cc | 55 +++++++++++++ src/config/config_manager.h | 14 ++++ src/main.cpp | 46 ++--------- src/rknn/video_service.cc | 27 ++++--- src/rknn/video_service.h | 2 + .../video_service_manager.cc | 75 ++++++++++++++++++ .../video_service_manager.h | 33 ++++++++ 10 files changed, 236 insertions(+), 51 deletions(-) create mode 100644 src/videoServiceManager/video_service_manager.cc create mode 100644 src/videoServiceManager/video_service_manager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bbb267..1e636d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ add_library(edge_proxy_lib STATIC src/rknn/rkYolov5s.cc src/rknn/preprocess.cc src/rknn/postprocess.cc + src/videoServiceManager/video_service_manager.cc ) target_include_directories(edge_proxy_lib PUBLIC diff --git a/config/config.json b/config/config.json index 4beb041..ca9ca54 100644 --- a/config/config.json +++ b/config/config.json @@ -12,5 +12,39 @@ "tcp_server_ports": [ 12345 ], + "video_service": { + "enabled": true, + "model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn", + "streams": [ + { + "enabled": true, + "id": "ch1301", + "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301", + "output_rtsp": "rtsp://127.0.0.1:8554/ch1301", + "rknn_thread_num": 1 + }, + { + "enabled": true, + "id": "ch1101", + "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1101", + "output_rtsp": "rtsp://127.0.0.1:8554/ch1101", + "rknn_thread_num": 1 + }, + { + "enabled": true, + "id": "ch1401", + "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1401", + "output_rtsp": "rtsp://127.0.0.1:8554/ch1401", + "rknn_thread_num": 1 + }, + { + "enabled": true, + "id": "ch1501", + "input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1501", + "output_rtsp": "rtsp://127.0.0.1:8554/ch1501", + "rknn_thread_num": 1 + } + ] + }, "web_server_port": 8080 } \ No newline at end of file diff --git a/config/edge_proxy_data.db b/config/edge_proxy_data.db index f1f3d99d3076a2860f28e67e509d437bf4c29704..371ac64cc2391e3c4538d1add5b908f74701e162 100644 GIT binary patch delta 269 zcmZo@U}|V!njp={K2gS*k$q#r5_vX8{!a}2pEe5$yx`~KVPux)Ow36v%8k#-Pv2y~ zq5za`VBl{6N|*81*D-Q2Xsb#}H}bNx1V;vkxcbIB2ZY9Zc)EKO6y;Y`#urx>m!#%8 zI)`|MxrRFi`FZ-eKiR$b`GmITn|D9oJzoK+TH$HO%I8y7Jnw5)(6BVqGcZxrRCuwi z^~L^n1xo`x0|V9fdwO21-~McI!?XEI-|uO3h+xWcajYoF_;-){2^Rwc10(+r2L2yF k2fgDLVrORI1WG`~Z!_@U28v(gS5;xvR%PMD<3x}q07HObNB{r; delta 35 rcmZo@U}|V!njp={Hc`fzk!@qb68Xg}0s@O!3=}rA82pi6("piper_model_path", "/app/models/model.onnx"); +} + +bool ConfigManager::getIsVideoServiceEnabled() const { + std::shared_lock lock(m_mutex); + try { + if (m_config_json.contains("video_service")) { + return m_config_json["video_service"].value("enabled", false); + } + } catch (const json::type_error& e) { + spdlog::warn("Config type mismatch for key 'video_service.enabled'. Error: {}", e.what()); + } + return false; +} + +std::string ConfigManager::getVideoModelPath() const { + std::shared_lock lock(m_mutex); + try { + if (m_config_json.contains("video_service")) { + return m_config_json["video_service"].value("model_path", ""); + } + } catch (const json::type_error& e) { + spdlog::warn("Config type mismatch for key 'video_service.model_path'. Error: {}", e.what()); + } + return ""; +} + +std::vector ConfigManager::getVideoStreamConfigs() const { + std::vector configs; + + std::shared_lock lock(m_mutex); + + try { + if (m_config_json.contains("video_service") && + m_config_json["video_service"].contains("streams") && + m_config_json["video_service"]["streams"].is_array()) + { + for (const auto& stream_json : m_config_json["video_service"]["streams"]) { + VideoStreamConfig cfg; + cfg.id = stream_json.value("id", ""); + cfg.enabled = stream_json.value("enabled", false); + cfg.input_url = stream_json.value("input_url", ""); + cfg.output_rtsp = stream_json.value("output_rtsp", ""); + cfg.rknn_thread_num = stream_json.value("rknn_thread_num", 1); + + configs.push_back(cfg); + } + } else { + spdlog::warn("Config key 'video_service.streams' not found or is not an array."); + } + } catch (const json::exception& e) { + // 捕获所有可能的 JSON (Source 2) 解析异常 + spdlog::error("Error parsing 'video_service.streams': {}", e.what()); + } + + return configs; } \ No newline at end of file diff --git a/src/config/config_manager.h b/src/config/config_manager.h index 9ccbac6..f9b09c0 100644 --- a/src/config/config_manager.h +++ b/src/config/config_manager.h @@ -11,6 +11,14 @@ using json = nlohmann::json; class ConfigManager { public: + struct VideoStreamConfig { + std::string id; + bool enabled; + std::string input_url; + std::string output_rtsp; + int rknn_thread_num; + }; + static ConfigManager& getInstance(); bool load(const std::string& configFilePath); bool save(); @@ -79,6 +87,10 @@ public: std::string getAlarmRulesPath(); std::string getPiperExecutablePath(); std::string getPiperModelPath(); + + bool getIsVideoServiceEnabled() const; + std::string getVideoModelPath() const; + std::vector getVideoStreamConfigs() const; private: ConfigManager() = default; @@ -93,4 +105,6 @@ private: json m_config_json; mutable std::shared_mutex m_mutex; + + }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 783dd8d..aa8a862 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,7 +11,7 @@ #include "dataCache/live_data_cache.h" #include "dataStorage/data_storage.h" #include "config/config_manager.h" -#include "rknn/video_service.h" +#include "videoServiceManager/video_service_manager.h" #include "alarm/alarm_service.h" #include "tts/piper_tts_interface.h" @@ -87,31 +87,7 @@ int main(int argc, char* argv[]) { try { spdlog::info("Initializing Video Service..."); - std::unique_ptr video_service; - - const char* model_path = "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn"; - int thread_num = 3; - - std::string input_rtsp = "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301"; - - std::string output_rtsp = "rtsp://127.0.0.1:8554/processed"; - if (true) { //config.isVideoServiceEnabled() - try { - video_service = std::make_unique( - model_path, - thread_num, - input_rtsp, - output_rtsp - ); - spdlog::info("VideoService configured. Input: {}, Output: {}", - model_path, output_rtsp); - } catch (const std::exception& e) { - spdlog::error("Failed to initialize VideoService: {}. Video processing will be disabled.", e.what()); - video_service.reset(); // 确保为 null - } - } else { - spdlog::warn("VideoService is disabled in configuration."); - } + VideoServiceManager video_manager; DataCache data_cache; LiveDataCache live_data_cache; @@ -199,14 +175,11 @@ int main(int argc, char* argv[]) { alarm_service, config.getWebServerPort()); web_server.start(); - if (video_service) { - if (video_service->start()) { - spdlog::info("VideoService started successfully."); - } else { - spdlog::error("Failed to start VideoService. Video processing will not run."); - video_service.reset(); // 设置为 null,以便在关闭时安全跳过 - } - } + if (config.getIsVideoServiceEnabled()) { + video_manager.load_and_start(config); + } else { + spdlog::warn("VideoService is disabled in configuration."); + } boost::asio::signal_set signals(g_io_context, SIGINT, SIGTERM); signals.async_wait([&](const boost::system::error_code& error, int signal_number) { @@ -229,10 +202,7 @@ int main(int argc, char* argv[]) { mqtt_client.disconnect(); spdlog::info("[Shutdown] E. Stopping video Service loop..."); - if (video_service) { - spdlog::info("[Shutdown] F. Stopping video service..."); - video_service->stop(); - } + video_manager.stop_all(); // e. 最后,安全地停止io_context spdlog::info("[Shutdown] F. Stopping main event loop..."); diff --git a/src/rknn/video_service.cc b/src/rknn/video_service.cc index 967a496..53056d0 100644 --- a/src/rknn/video_service.cc +++ b/src/rknn/video_service.cc @@ -29,12 +29,13 @@ VideoService::VideoService(std::string model_path, output_rtsp_url_(output_rtsp_url), running_(false) { - // (新增) 初始化跟踪器状态 + log_prefix_ = "[VideoService: " + input_url + "]"; + next_track_id_ = 1; intrusion_time_threshold_ = 3.0; // 3秒 intrusion_zone_ = cv::Rect(0, 0, 0, 0); // 默认无效 - printf("VideoService created. Input: %s, Output: %s\n", input_url_.c_str(), output_rtsp_url_.c_str()); + spdlog::info("{} Created. Input: {}, Output: {}", log_prefix_, input_url_.c_str(), output_rtsp_url_.c_str()); } VideoService::~VideoService() { @@ -264,7 +265,7 @@ void VideoService::processing_loop() { detect_result_group_t detection_results; while (running_) { - auto t_start = std::chrono::high_resolution_clock::now(); + // auto t_start = std::chrono::high_resolution_clock::now(); { std::unique_lock lock(frame_mutex_); @@ -282,7 +283,7 @@ void VideoService::processing_loop() { new_frame_available_ = false; } - auto t_read = std::chrono::high_resolution_clock::now(); + // auto t_read = std::chrono::high_resolution_clock::now(); if (frame.empty()) { continue; @@ -301,7 +302,7 @@ void VideoService::processing_loop() { break; } - auto t_infer = std::chrono::high_resolution_clock::now(); + // auto t_infer = std::chrono::high_resolution_clock::now(); this->update_tracker(detection_results, frame.size()); this->draw_results(frame); @@ -311,17 +312,17 @@ void VideoService::processing_loop() { writer_.write(frame); } - auto t_write = std::chrono::high_resolution_clock::now(); + // auto t_write = std::chrono::high_resolution_clock::now(); // [保留] 性能日志 - double read_ms = std::chrono::duration_cast>(t_read - t_start).count(); - double infer_ms = std::chrono::duration_cast>(t_infer - t_read).count(); - double track_ms = std::chrono::duration_cast>(t_track_draw - t_infer).count(); - double write_ms = std::chrono::duration_cast>(t_write - t_track_draw).count(); - double total_ms = std::chrono::duration_cast>(t_write - t_start).count(); + // double read_ms = std::chrono::duration_cast>(t_read - t_start).count(); + // double infer_ms = std::chrono::duration_cast>(t_infer - t_read).count(); + // double track_ms = std::chrono::duration_cast>(t_track_draw - t_infer).count(); + // double write_ms = std::chrono::duration_cast>(t_write - t_track_draw).count(); + // double total_ms = std::chrono::duration_cast>(t_write - t_start).count(); - printf("Loop time: Total=%.1fms [Read=%.1f, Infer=%.1f, Track=%.1f, Write=%.1f]\n", - total_ms, read_ms, infer_ms, track_ms, write_ms); + // printf("Loop time: Total=%.1fms [Read=%.1f, Infer=%.1f, Track=%.1f, Write=%.1f]\n", + // total_ms, read_ms, infer_ms, track_ms, write_ms); } spdlog::info("VideoService: Processing loop finished."); diff --git a/src/rknn/video_service.h b/src/rknn/video_service.h index 9820389..cb103ea 100644 --- a/src/rknn/video_service.h +++ b/src/rknn/video_service.h @@ -75,4 +75,6 @@ private: std::map tracked_persons_; int next_track_id_; double intrusion_time_threshold_; + + std::string log_prefix_; }; \ No newline at end of file diff --git a/src/videoServiceManager/video_service_manager.cc b/src/videoServiceManager/video_service_manager.cc new file mode 100644 index 0000000..6183626 --- /dev/null +++ b/src/videoServiceManager/video_service_manager.cc @@ -0,0 +1,75 @@ +// video_service_manager.cc +#include "video_service_manager.h" +#include "spdlog/spdlog.h" + +VideoServiceManager::~VideoServiceManager() { + // 确保在析构时停止所有服务 + stop_all(); +} + +void VideoServiceManager::load_and_start(ConfigManager& config) { + if (!config.getIsVideoServiceEnabled()) { + spdlog::warn("VideoService is disabled in configuration. No streams will be started."); + return; + } + + model_path_ = config.getVideoModelPath(); + if (model_path_.empty()) { + spdlog::error("Video model path is not set in configuration. Cannot start video services."); + return; + } + + auto stream_configs = config.getVideoStreamConfigs(); + spdlog::info("Found {} video stream configurations.", stream_configs.size()); + + for (const auto& sc : stream_configs) { + if (!sc.enabled) { + spdlog::info("Video stream '{}' (input: {}) is disabled in config, skipping.", sc.id, sc.input_url); + continue; + } + + if (sc.rknn_thread_num <= 0) { + spdlog::warn("Video stream '{}' has invalid rknn_thread_num ({}). Defaulting to 1.", sc.id, sc.rknn_thread_num); + } + + int threads_for_this_stream = (sc.rknn_thread_num > 0) ? sc.rknn_thread_num : 1; + + try { + // 为每个流创建一个独立的 VideoService 实例 + auto service = std::make_unique( + model_path_, + threads_for_this_stream, + sc.input_url, + sc.output_rtsp + ); + + if (service->start()) { + spdlog::info("Successfully started video service for stream '{}' [Input: {}]. Output is [{}].", + sc.id, sc.input_url, sc.output_rtsp); + services_.push_back(std::move(service)); + } else { + spdlog::error("Failed to start video service for stream '{}' [Input: {}].", + sc.id, sc.input_url); + } + + } catch (const std::exception& e) { + spdlog::error("Exception while creating VideoService for stream '{}' [{}]: {}", + sc.id, sc.input_url, e.what()); + } + } + + spdlog::info("VideoServiceManager finished setup. {} streams are now running.", services_.size()); +} + +void VideoServiceManager::stop_all() { + spdlog::info("Stopping all video services ({})...", services_.size()); + + // 按顺序停止所有服务 + for (auto& service : services_) { + if (service) { + service->stop(); + } + } + services_.clear(); + spdlog::info("All video services stopped and cleared."); +} \ No newline at end of file diff --git a/src/videoServiceManager/video_service_manager.h b/src/videoServiceManager/video_service_manager.h new file mode 100644 index 0000000..5fd8f32 --- /dev/null +++ b/src/videoServiceManager/video_service_manager.h @@ -0,0 +1,33 @@ +// video_service_manager.h +#pragma once + +#include "config/config_manager.h" // 需要访问配置 +#include "rknn/video_service.h" // 需要创建 VideoService +#include +#include +#include + +class VideoServiceManager { +public: + VideoServiceManager() = default; + ~VideoServiceManager(); + + // 禁止拷贝和赋值 + VideoServiceManager(const VideoServiceManager&) = delete; + VideoServiceManager& operator=(const VideoServiceManager&) = delete; + + /** + * @brief 根据配置加载并启动所有配置为 "enabled" 的视频服务 + * @param config ConfigManager 的引用 + */ + void load_and_start(ConfigManager& config); + + /** + * @brief 停止并销毁所有正在运行的视频服务 + */ + void stop_all(); + +private: + std::vector> services_; + std::string model_path_; +}; \ No newline at end of file