4路视频采集

This commit is contained in:
GuanYuankai 2025-10-27 03:06:06 +00:00
parent 29ecc25221
commit 1e77b39d69
10 changed files with 236 additions and 51 deletions

View File

@ -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

View File

@ -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
}

Binary file not shown.

View File

@ -136,3 +136,58 @@ std::string ConfigManager::getPiperExecutablePath() {
std::string ConfigManager::getPiperModelPath() {
return get<std::string>("piper_model_path", "/app/models/model.onnx");
}
bool ConfigManager::getIsVideoServiceEnabled() const {
std::shared_lock<std::shared_mutex> 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<std::shared_mutex> 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::VideoStreamConfig> ConfigManager::getVideoStreamConfigs() const {
std::vector<VideoStreamConfig> configs;
std::shared_lock<std::shared_mutex> 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;
}

View File

@ -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();
@ -80,6 +88,10 @@ public:
std::string getPiperExecutablePath();
std::string getPiperModelPath();
bool getIsVideoServiceEnabled() const;
std::string getVideoModelPath() const;
std::vector<VideoStreamConfig> getVideoStreamConfigs() const;
private:
ConfigManager() = default;
~ConfigManager() = default;
@ -93,4 +105,6 @@ private:
json m_config_json;
mutable std::shared_mutex m_mutex;
};

View File

@ -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<VideoService> 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<VideoService>(
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...");

View File

@ -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<std::mutex> 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<std::chrono::duration<double, std::milli>>(t_read - t_start).count();
double infer_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_infer - t_read).count();
double track_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_track_draw - t_infer).count();
double write_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_write - t_track_draw).count();
double total_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_write - t_start).count();
// double read_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_read - t_start).count();
// double infer_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_infer - t_read).count();
// double track_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_track_draw - t_infer).count();
// double write_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(t_write - t_track_draw).count();
// double total_ms = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(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.");

View File

@ -75,4 +75,6 @@ private:
std::map<int, TrackedPerson> tracked_persons_;
int next_track_id_;
double intrusion_time_threshold_;
std::string log_prefix_;
};

View File

@ -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<VideoService>(
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.");
}

View File

@ -0,0 +1,33 @@
// video_service_manager.h
#pragma once
#include "config/config_manager.h" // 需要访问配置
#include "rknn/video_service.h" // 需要创建 VideoService
#include <vector>
#include <memory>
#include <string>
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<std::unique_ptr<VideoService>> services_;
std::string model_path_;
};