4路视频采集
This commit is contained in:
parent
29ecc25221
commit
1e77b39d69
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
};
|
||||
46
src/main.cpp
46
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<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...");
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
|
|
@ -75,4 +75,6 @@ private:
|
|||
std::map<int, TrackedPerson> tracked_persons_;
|
||||
int next_track_id_;
|
||||
double intrusion_time_threshold_;
|
||||
|
||||
std::string log_prefix_;
|
||||
};
|
||||
|
|
@ -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.");
|
||||
}
|
||||
|
|
@ -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_;
|
||||
};
|
||||
Loading…
Reference in New Issue