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/rkYolov5s.cc
|
||||||
src/rknn/preprocess.cc
|
src/rknn/preprocess.cc
|
||||||
src/rknn/postprocess.cc
|
src/rknn/postprocess.cc
|
||||||
|
src/videoServiceManager/video_service_manager.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(edge_proxy_lib PUBLIC
|
target_include_directories(edge_proxy_lib PUBLIC
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,39 @@
|
||||||
"tcp_server_ports": [
|
"tcp_server_ports": [
|
||||||
12345
|
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
|
"web_server_port": 8080
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
|
@ -136,3 +136,58 @@ std::string ConfigManager::getPiperExecutablePath() {
|
||||||
std::string ConfigManager::getPiperModelPath() {
|
std::string ConfigManager::getPiperModelPath() {
|
||||||
return get<std::string>("piper_model_path", "/app/models/model.onnx");
|
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 {
|
class ConfigManager {
|
||||||
public:
|
public:
|
||||||
|
struct VideoStreamConfig {
|
||||||
|
std::string id;
|
||||||
|
bool enabled;
|
||||||
|
std::string input_url;
|
||||||
|
std::string output_rtsp;
|
||||||
|
int rknn_thread_num;
|
||||||
|
};
|
||||||
|
|
||||||
static ConfigManager& getInstance();
|
static ConfigManager& getInstance();
|
||||||
bool load(const std::string& configFilePath);
|
bool load(const std::string& configFilePath);
|
||||||
bool save();
|
bool save();
|
||||||
|
|
@ -80,6 +88,10 @@ public:
|
||||||
std::string getPiperExecutablePath();
|
std::string getPiperExecutablePath();
|
||||||
std::string getPiperModelPath();
|
std::string getPiperModelPath();
|
||||||
|
|
||||||
|
bool getIsVideoServiceEnabled() const;
|
||||||
|
std::string getVideoModelPath() const;
|
||||||
|
std::vector<VideoStreamConfig> getVideoStreamConfigs() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ConfigManager() = default;
|
ConfigManager() = default;
|
||||||
~ConfigManager() = default;
|
~ConfigManager() = default;
|
||||||
|
|
@ -93,4 +105,6 @@ private:
|
||||||
json m_config_json;
|
json m_config_json;
|
||||||
mutable std::shared_mutex m_mutex;
|
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 "dataCache/live_data_cache.h"
|
||||||
#include "dataStorage/data_storage.h"
|
#include "dataStorage/data_storage.h"
|
||||||
#include "config/config_manager.h"
|
#include "config/config_manager.h"
|
||||||
#include "rknn/video_service.h"
|
#include "videoServiceManager/video_service_manager.h"
|
||||||
#include "alarm/alarm_service.h"
|
#include "alarm/alarm_service.h"
|
||||||
#include "tts/piper_tts_interface.h"
|
#include "tts/piper_tts_interface.h"
|
||||||
|
|
||||||
|
|
@ -87,31 +87,7 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
spdlog::info("Initializing Video Service...");
|
spdlog::info("Initializing Video Service...");
|
||||||
std::unique_ptr<VideoService> video_service;
|
VideoServiceManager video_manager;
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
DataCache data_cache;
|
DataCache data_cache;
|
||||||
LiveDataCache live_data_cache;
|
LiveDataCache live_data_cache;
|
||||||
|
|
@ -199,14 +175,11 @@ int main(int argc, char* argv[]) {
|
||||||
alarm_service,
|
alarm_service,
|
||||||
config.getWebServerPort());
|
config.getWebServerPort());
|
||||||
web_server.start();
|
web_server.start();
|
||||||
if (video_service) {
|
if (config.getIsVideoServiceEnabled()) {
|
||||||
if (video_service->start()) {
|
video_manager.load_and_start(config);
|
||||||
spdlog::info("VideoService started successfully.");
|
} else {
|
||||||
} else {
|
spdlog::warn("VideoService is disabled in configuration.");
|
||||||
spdlog::error("Failed to start VideoService. Video processing will not run.");
|
}
|
||||||
video_service.reset(); // 设置为 null,以便在关闭时安全跳过
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::asio::signal_set signals(g_io_context, SIGINT, SIGTERM);
|
boost::asio::signal_set signals(g_io_context, SIGINT, SIGTERM);
|
||||||
signals.async_wait([&](const boost::system::error_code& error, int signal_number) {
|
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();
|
mqtt_client.disconnect();
|
||||||
|
|
||||||
spdlog::info("[Shutdown] E. Stopping video Service loop...");
|
spdlog::info("[Shutdown] E. Stopping video Service loop...");
|
||||||
if (video_service) {
|
video_manager.stop_all();
|
||||||
spdlog::info("[Shutdown] F. Stopping video service...");
|
|
||||||
video_service->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// e. 最后,安全地停止io_context
|
// e. 最后,安全地停止io_context
|
||||||
spdlog::info("[Shutdown] F. Stopping main event loop...");
|
spdlog::info("[Shutdown] F. Stopping main event loop...");
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,13 @@ VideoService::VideoService(std::string model_path,
|
||||||
output_rtsp_url_(output_rtsp_url),
|
output_rtsp_url_(output_rtsp_url),
|
||||||
running_(false)
|
running_(false)
|
||||||
{
|
{
|
||||||
// (新增) 初始化跟踪器状态
|
log_prefix_ = "[VideoService: " + input_url + "]";
|
||||||
|
|
||||||
next_track_id_ = 1;
|
next_track_id_ = 1;
|
||||||
intrusion_time_threshold_ = 3.0; // 3秒
|
intrusion_time_threshold_ = 3.0; // 3秒
|
||||||
intrusion_zone_ = cv::Rect(0, 0, 0, 0); // 默认无效
|
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() {
|
VideoService::~VideoService() {
|
||||||
|
|
@ -264,7 +265,7 @@ void VideoService::processing_loop() {
|
||||||
detect_result_group_t detection_results;
|
detect_result_group_t detection_results;
|
||||||
|
|
||||||
while (running_) {
|
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_);
|
std::unique_lock<std::mutex> lock(frame_mutex_);
|
||||||
|
|
@ -282,7 +283,7 @@ void VideoService::processing_loop() {
|
||||||
new_frame_available_ = false;
|
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()) {
|
if (frame.empty()) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -301,7 +302,7 @@ void VideoService::processing_loop() {
|
||||||
break;
|
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->update_tracker(detection_results, frame.size());
|
||||||
|
|
||||||
this->draw_results(frame);
|
this->draw_results(frame);
|
||||||
|
|
@ -311,17 +312,17 @@ void VideoService::processing_loop() {
|
||||||
writer_.write(frame);
|
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 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 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 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 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 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",
|
// printf("Loop time: Total=%.1fms [Read=%.1f, Infer=%.1f, Track=%.1f, Write=%.1f]\n",
|
||||||
total_ms, read_ms, infer_ms, track_ms, write_ms);
|
// total_ms, read_ms, infer_ms, track_ms, write_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::info("VideoService: Processing loop finished.");
|
spdlog::info("VideoService: Processing loop finished.");
|
||||||
|
|
|
||||||
|
|
@ -75,4 +75,6 @@ private:
|
||||||
std::map<int, TrackedPerson> tracked_persons_;
|
std::map<int, TrackedPerson> tracked_persons_;
|
||||||
int next_track_id_;
|
int next_track_id_;
|
||||||
double intrusion_time_threshold_;
|
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