【】内的代码是一个C++的视频分析服务项目完整代码，帮我仔细分析和审核可有内存泄漏和性能上的瓶颈。这个代码具体功能如下：读取配置文件中的RTSP流地址，然后通过GStreamer并发接入多路视
频流，接着调用ffmpeg进行抽帧，将抽帧形成的图片数据发送给Triton server，并将返回的分析结果传给上层应用。同时，
该项目还实现了多路并发、高性能、高可靠性系统调用，以及高效的图片抽帧、报警录像等功能。【1. config_manager.h

#pragma once

#include <string>
#include <vector>
#include <map>
#include <utility> // For std::pair

struct CameraConfig {
    std::string name;
    std::string rtsp_url;
    std::vector<std::string> algorithms;
    int frame_interval;
    std::map<std::string, std::string> alarm_strategies;
    std::string alarm_storage_path;
    std::vector<std::pair<std::string, std::string>> alarm_times; // Added field for alarm times
};

class ConfigManager {
public:
    static ConfigManager& getInstance();
    bool loadConfig(const std::string& config_path);
    const std::vector<CameraConfig>& getCameraConfigs() const;

private:
    ConfigManager() = default;
    std::vector<CameraConfig> camera_configs_;
};

2. config_manager.cpp

#include "config_manager.h"
#include "logger.h"
#include <fstream>
#include <sstream>
#include <stdexcept>

ConfigManager& ConfigManager::getInstance() {
    static ConfigManager instance;
    return instance;
}

bool ConfigManager::loadConfig(const std::string& config_path) {
    std::ifstream file(config_path);
    if (!file.is_open()) {
        LOG_ERROR("Failed to open config file: " + config_path);
        return false;
    }

    std::string line;
    CameraConfig current_config;

    while (std::getline(file, line)) {
        if (line.empty() || line[0] == '#') continue;

        if (line[0] == '[') {
            if (!current_config.name.empty()) {
                camera_configs_.push_back(current_config);
            }
            current_config = CameraConfig();
            current_config.name = line.substr(1, line.size() - 2);
            continue;
        }

        size_t pos = line.find('=');
        if (pos == std::string::npos) continue;

        std::string key = line.substr(0, pos);
        std::string value = line.substr(pos + 1);

        try {
            if (key == "rtsp_url") {
                current_config.rtsp_url = value;
            } else if (key == "frame_interval") {
                current_config.frame_interval = std::stoi(value);
            } else if (key == "algorithms") {
                std::stringstream ss(value);
                std::string algorithm;
                while (std::getline(ss, algorithm, ',')) {
                    current_config.algorithms.push_back(algorithm);
                }
            } else if (key == "alarm_storage_path") {
                current_config.alarm_storage_path = value;
            } else if (key == "alarm_strategy") {
                std::stringstream ss(value);
                std::string strategy;
                while (std::getline(ss, strategy, ',')) {
                    size_t colon_pos = strategy.find(':');
                    if (colon_pos != std::string::npos) {
                        std::string alg = strategy.substr(0, colon_pos);
                        std::string strat = strategy.substr(colon_pos + 1);
                        current_config.alarm_strategies[alg] = strat;
                    }
                }
            } else if (key == "alarm_time") {
                std::stringstream ss(value);
                std::string time_range;
                while (std::getline(ss, time_range, ';')) {
                    size_t dash_pos = time_range.find('-');
                    if (dash_pos != std::string::npos) {
                        std::string start_time = time_range.substr(0, dash_pos);
                        std::string end_time = time_range.substr(dash_pos + 1);
                        current_config.alarm_times.emplace_back(start_time, end_time);
                    }
                }
            }
        } catch (const std::exception& e) {
            LOG_ERROR("Error parsing config file: " + std::string(e.what()));
            return false;
        }
    }

    if (!current_config.name.empty()) {
        camera_configs_.push_back(current_config);
    }

    return true;
}

const std::vector<CameraConfig>& ConfigManager::getCameraConfigs() const {
    return camera_configs_;
}

3. video_capture.h

#pragma once

#include <string>
#include <memory>
#include <chrono>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <opencv2/opencv.hpp>
#include <queue>
#include <mutex>
#include <condition_variable>

#include "config_manager.h"
#include "logger.h"

class VideoCapture {
public:
    VideoCapture(const CameraConfig& config);
    ~VideoCapture();

    bool start();
    bool stop();

    bool isRunning() const;

    std::shared_ptr<cv::Mat> getFrame();
    void startRecording();
    void stopRecording();

    const std::string& getCameraName() const;

private:
    static GstFlowReturn onNewSample(GstElement* sink, gpointer data);
    static void onPadAdded(GstElement* element, GstPad* pad, gpointer data);
    static gboolean onBusMessage(GstBus* bus, GstMessage* msg, gpointer data);

    void reconnect();
    std::string sanitizeName(const std::string& name);
    std::string generateRecordingFilename();

    std::chrono::system_clock::time_point last_frame_time_;
    bool is_running_;
    GstElement* pipeline_;
    GstElement* recording_pipeline_;
    CameraConfig config_;

    std::queue<std::shared_ptr<cv::Mat>> frame_queue_;
    std::mutex queue_mutex_;
    std::condition_variable queue_cond_;

    std::mutex reconnect_mutex_;
    int reconnect_attempts_;
    std::vector<int> reconnect_intervals_;

    bool recording_;
    std::mutex recording_mutex_;

    std::chrono::system_clock::time_point last_frame_fail_time_;
    int frame_fail_count_;

    // Added members for recording
    GstElement* recording_queue_;
    GstElement* encoder_;
    GstElement* muxer_;
    GstElement* filesink_;
    GstElement* tee_;

    // Sanitized name for GStreamer elements
    std::string sanitized_name_;
};

4. video_capture.cpp

#include "video_capture.h"
#include <thread>

VideoCapture::VideoCapture(const CameraConfig& config)
    : config_(config), pipeline_(nullptr), recording_pipeline_(nullptr),
      is_running_(false), last_frame_time_(std::chrono::system_clock::now()),
      reconnect_attempts_(0), recording_(false), frame_fail_count_(0),
      recording_queue_(nullptr), encoder_(nullptr), muxer_(nullptr),
      filesink_(nullptr), tee_(nullptr) {
    if (!gst_is_initialized()) {
        gst_init(NULL, NULL);
    }

    reconnect_intervals_ = {5, 10, 30, 180}; // seconds
    sanitized_name_ = sanitizeName(config_.name);
}

VideoCapture::~VideoCapture() {
    stop();
}

std::string VideoCapture::sanitizeName(const std::string& name) {
    std::string sanitized;
    for (char c : name) {
        if (isalnum(c) || c == '_') {
            sanitized += c;
        } else {
            sanitized += '_';
        }
    }
    return sanitized;
}

bool VideoCapture::start() {
    std::lock_guard<std::mutex> lock(reconnect_mutex_);
    if (is_running_) return true;

    try {
        // Create pipeline
        pipeline_ = gst_pipeline_new(("pipeline_" + sanitized_name_).c_str());
        if (!pipeline_) {
            throw std::runtime_error("Failed to create pipeline");
        }

        // Create elements
        GstElement* source = gst_element_factory_make("rtspsrc", ("source_" + sanitized_name_).c_str());
        GstElement* decodebin = gst_element_factory_make("decodebin", ("decodebin_" + sanitized_name_).c_str());
        GstElement* videoconvert = gst_element_factory_make("videoconvert", ("videoconvert_" + sanitized_name_).c_str());
        tee_ = gst_element_factory_make("tee", ("tee_" + sanitized_name_).c_str());
        GstElement* queue_appsink = gst_element_factory_make("queue", ("queue_appsink_" + sanitized_name_).c_str());
        GstElement* appsink = gst_element_factory_make("appsink", ("appsink_" + sanitized_name_).c_str());

        if (!source || !decodebin || !videoconvert || !tee_ || !queue_appsink || !appsink) {
            throw std::runtime_error("Failed to create elements");
        }

        // Set properties
        g_object_set(G_OBJECT(source), "location", config_.rtsp_url.c_str(), NULL);
        g_object_set(G_OBJECT(appsink), "emit-signals", TRUE, "sync", FALSE, NULL);

        // Add elements to pipeline
        gst_bin_add_many(GST_BIN(pipeline_), source, decodebin, videoconvert, tee_, queue_appsink, appsink, NULL);

        // Link elements
        g_signal_connect(source, "pad-added", G_CALLBACK(onPadAdded), decodebin);
        g_signal_connect(decodebin, "pad-added", G_CALLBACK(onPadAdded), videoconvert);

        if (!gst_element_link_many(videoconvert, tee_, NULL)) {
            throw std::runtime_error("Failed to link videoconvert to tee");
        }

        if (!gst_element_link_many(queue_appsink, appsink, NULL)) {
            throw std::runtime_error("Failed to link queue_appsink to appsink");
        }

        // Link tee to queue_appsink
        GstPad* tee_src_pad = gst_element_get_request_pad(tee_, "src_%u");
        GstPad* queue_appsink_sink_pad = gst_element_get_static_pad(queue_appsink, "sink");
        if (gst_pad_link(tee_src_pad, queue_appsink_sink_pad) != GST_PAD_LINK_OK) {
            gst_object_unref(tee_src_pad);
            gst_object_unref(queue_appsink_sink_pad);
            throw std::runtime_error("Failed to link tee to queue_appsink");
        }
        gst_object_unref(tee_src_pad);
        gst_object_unref(queue_appsink_sink_pad);

        // Connect appsink new-sample signal
        g_signal_connect(appsink, "new-sample", G_CALLBACK(onNewSample), this);

        // Set up bus message handler
        GstBus* bus = gst_element_get_bus(pipeline_);
        gst_bus_add_watch(bus, onBusMessage, this);
        gst_object_unref(bus);

        // Start pipeline
        GstStateChangeReturn ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);
        if (ret == GST_STATE_CHANGE_FAILURE) {
            throw std::runtime_error("Failed to start pipeline");
        }

        is_running_ = true;
        reconnect_attempts_ = 0;
        LOG_INFO("Started video capture for camera " + config_.name);
        return true;
    } catch (const std::exception& e) {
        LOG_ERROR("Error starting video capture for camera " + config_.name + ": " + e.what());
        return false;
    }
}

bool VideoCapture::stop() {
    std::lock_guard<std::mutex> lock(reconnect_mutex_);
    if (!is_running_) return true;

    try {
        if (pipeline_) {
            gst_element_set_state(pipeline_, GST_STATE_NULL);
            gst_object_unref(pipeline_);
            pipeline_ = nullptr;
        }

        is_running_ = false;
        return true;
    } catch (const std::exception& e) {
        LOG_ERROR("Error stopping video capture for camera " + config_.name + ": " + e.what());
        return false;
    }
}

bool VideoCapture::isRunning() const {
    return is_running_;
}

std::shared_ptr<cv::Mat> VideoCapture::getFrame() {
    std::unique_lock<std::mutex> lock(queue_mutex_);
    if (frame_queue_.empty()) {
        queue_cond_.wait_for(lock, std::chrono::milliseconds(100));
    }
    if (frame_queue_.empty()) {
        return nullptr;
    }
    auto frame = frame_queue_.front();
    frame_queue_.pop();
    return frame;
}

void VideoCapture::startRecording() {
    std::lock_guard<std::mutex> lock(recording_mutex_);
    if (recording_) return;
    if (!is_running_) {
        LOG_ERROR("Camera " + config_.name + " is disconnected. Cannot start recording.");
        // Record failure to record
        LOG_ERROR("Failed to record due to disconnection for camera " + config_.name);
        return;
    }

    try {
        // Create recording elements
        recording_queue_ = gst_element_factory_make("queue", ("recording_queue_" + sanitized_name_).c_str());
        encoder_ = gst_element_factory_make("x264enc", ("encoder_" + sanitized_name_).c_str());
        muxer_ = gst_element_factory_make("mp4mux", ("muxer_" + sanitized_name_).c_str());
        filesink_ = gst_element_factory_make("filesink", ("filesink_" + sanitized_name_).c_str());

        if (!recording_queue_ || !encoder_ || !muxer_ || !filesink_) {
            throw std::runtime_error("Failed to create recording elements");
        }

        // Set filesink location
        std::string filename = generateRecordingFilename();
        g_object_set(G_OBJECT(filesink_), "location", filename.c_str(), NULL);

        // Add elements to pipeline
        gst_bin_add_many(GST_BIN(pipeline_), recording_queue_, encoder_, muxer_, filesink_, NULL);

        // Link elements
        if (!gst_element_link_many(recording_queue_, encoder_, muxer_, filesink_, NULL)) {
            throw std::runtime_error("Failed to link recording elements");
        }

        // Link tee to recording_queue_
        GstPad* tee_src_pad = gst_element_get_request_pad(tee_, "src_%u");
        GstPad* queue_sink_pad = gst_element_get_static_pad(recording_queue_, "sink");
        if (gst_pad_link(tee_src_pad, queue_sink_pad) != GST_PAD_LINK_OK) {
            gst_object_unref(tee_src_pad);
            gst_object_unref(queue_sink_pad);
            throw std::runtime_error("Failed to link tee to recording queue");
        }
        gst_object_unref(tee_src_pad);
        gst_object_unref(queue_sink_pad);

        // Sync state with parent
        gst_element_sync_state_with_parent(recording_queue_);
        gst_element_sync_state_with_parent(encoder_);
        gst_element_sync_state_with_parent(muxer_);
        gst_element_sync_state_with_parent(filesink_);

        recording_ = true;
        LOG_INFO("Started recording for camera " + config_.name);
    } catch (const std::exception& e) {
        LOG_ERROR("Error starting recording for camera " + config_.name + ": " + e.what());
    }
}

void VideoCapture::stopRecording() {
    std::lock_guard<std::mutex> lock(recording_mutex_);
    if (!recording_) return;

    try {
        // Unlink and remove recording elements
        GstPad* queue_sink_pad = gst_element_get_static_pad(recording_queue_, "sink");
        GstPad* tee_src_pad = gst_pad_get_peer(queue_sink_pad);
        gst_pad_unlink(tee_src_pad, queue_sink_pad);
        gst_element_release_request_pad(tee_, tee_src_pad);
        gst_object_unref(tee_src_pad);
        gst_object_unref(queue_sink_pad);

        // Set elements to NULL state and remove from pipeline
        gst_element_set_state(filesink_, GST_STATE_NULL);
        gst_element_set_state(muxer_, GST_STATE_NULL);
        gst_element_set_state(encoder_, GST_STATE_NULL);
        gst_element_set_state(recording_queue_, GST_STATE_NULL);

        gst_bin_remove_many(GST_BIN(pipeline_), recording_queue_, encoder_, muxer_, filesink_, NULL);

        recording_queue_ = nullptr;
        encoder_ = nullptr;
        muxer_ = nullptr;
        filesink_ = nullptr;

        recording_ = false;
        LOG_INFO("Stopped recording for camera " + config_.name);
    } catch (const std::exception& e) {
        LOG_ERROR("Error stopping recording for camera " + config_.name + ": " + e.what());
    }
}

const std::string& VideoCapture::getCameraName() const {
    return config_.name;
}

GstFlowReturn VideoCapture::onNewSample(GstElement* sink, gpointer data) {
    VideoCapture* self = static_cast<VideoCapture*>(data);

    try {
        // Check frame interval
        auto current_time = std::chrono::system_clock::now();
        auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - self->last_frame_time_);

        if (elapsed_time.count() < self->config_.frame_interval) {
            return GST_FLOW_OK; // Skip frame
        }
        self->last_frame_time_ = current_time;

        GstSample* sample = gst_app_sink_pull_sample(GST_APP_SINK(sink));
        if (!sample) {
            return GST_FLOW_OK;
        }

        GstBuffer* buffer = gst_sample_get_buffer(sample);
        GstMapInfo map;

        if (gst_buffer_map(buffer, &map, GST_MAP_READ)) {
            GstCaps* caps = gst_sample_get_caps(sample);
            GstStructure* structure = gst_caps_get_structure(caps, 0);
            int width, height;
            gst_structure_get_int(structure, "width", &width);
            gst_structure_get_int(structure, "height", &height);
            const gchar* format = gst_structure_get_string(structure, "format");

            cv::Mat frame;
            if (strcmp(format, "BGR") == 0) {
                frame = cv::Mat(height, width, CV_8UC3, (char*)map.data);
            } else if (strcmp(format, "RGB") == 0) {
                frame = cv::Mat(height, width, CV_8UC3, (char*)map.data);
                cv::cvtColor(frame, frame, cv::COLOR_RGB2BGR);
            } else if (strcmp(format, "RGBA") == 0) {
                cv::Mat rgba_frame(height, width, CV_8UC4, (char*)map.data);
                cv::cvtColor(rgba_frame, frame, cv::COLOR_RGBA2BGR);
            } else if (strcmp(format, "NV12") == 0) {
                cv::Mat nv12_frame(height * 3 / 2, width, CV_8UC1, (char*)map.data);
                cv::cvtColor(nv12_frame, frame, cv::COLOR_YUV2BGR_NV12);
            } else if (strcmp(format, "I420") == 0) {
                cv::Mat i420_frame(height * 3 / 2, width, CV_8UC1, (char*)map.data);
                cv::cvtColor(i420_frame, frame, cv::COLOR_YUV2BGR_I420);
            } else {
                LOG_ERROR("Unsupported video format: " + std::string(format));
                gst_buffer_unmap(buffer, &map);
                gst_sample_unref(sample);
                return GST_FLOW_OK;
            }

            // Clone the frame to ensure the data is copied
            auto frame_ptr = std::make_shared<cv::Mat>(frame.clone());
            {
                std::lock_guard<std::mutex> lock(self->queue_mutex_);
                self->frame_queue_.push(frame_ptr);
                // Control queue size to prevent excessive memory usage
                while (self->frame_queue_.size() > 10) {
                    self->frame_queue_.pop();
                }
            }
            self->queue_cond_.notify_one();

            gst_buffer_unmap(buffer, &map);
        } else {
            // Frame extraction failed, log error
            auto now = std::chrono::system_clock::now();
            if (now - self->last_frame_fail_time_ > std::chrono::minutes(1)) {
                LOG_ERROR("Frame extraction failed for camera " + self->config_.name);
                self->last_frame_fail_time_ = now;
            }
            self->frame_fail_count_++;
        }

        gst_sample_unref(sample);
    } catch (const std::exception& e) {
        LOG_ERROR("Error in onNewSample for camera " + self->config_.name + ": " + e.what());
    }

    return GST_FLOW_OK;
}

gboolean VideoCapture::onBusMessage(GstBus* bus, GstMessage* msg, gpointer data) {
    VideoCapture* self = static_cast<VideoCapture*>(data);

    switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_EOS:
            LOG_WARNING("Stream reached EOS for camera " + self->config_.name);
            self->reconnect();
            break;
        case GST_MESSAGE_ERROR: {
            GError* err;
            gchar* debug_info;
            gst_message_parse_error(msg, &err, &debug_info);
            LOG_ERROR("GStreamer error for camera " + self->config_.name + ": " + err->message);
            g_clear_error(&err);
            g_free(debug_info);
            self->reconnect();
            break;
        }
        default:
            break;
    }
    return TRUE;
}

void VideoCapture::reconnect() {
    std::lock_guard<std::mutex> lock(reconnect_mutex_);
    if (is_running_) {
        gst_element_set_state(pipeline_, GST_STATE_NULL);
        gst_object_unref(pipeline_);
        pipeline_ = nullptr;
        is_running_ = false;
    }

    int wait_time = reconnect_attempts_ < reconnect_intervals_.size() ? reconnect_intervals_[reconnect_attempts_] : reconnect_intervals_.back();
    LOG_INFO("Attempting to reconnect camera " + config_.name + " in " + std::to_string(wait_time) + " seconds");
    std::this_thread::sleep_for(std::chrono::seconds(wait_time));

    reconnect_attempts_++;
    start();
}

void VideoCapture::onPadAdded(GstElement* element, GstPad* pad, gpointer data) {
    GstElement* target = GST_ELEMENT(data);
    GstPad* sinkpad = gst_element_get_static_pad(target, "sink");
    gst_pad_link(pad, sinkpad);
    gst_object_unref(sinkpad);
}

std::string VideoCapture::generateRecordingFilename() {
    // Generate a filename based on current time and camera name
    auto now = std::chrono::system_clock::now();
    auto time_t_now = std::chrono::system_clock::to_time_t(now);
    std::stringstream ss;
    ss << config_.alarm_storage_path << "/"
       << config_.name << "_"
       << std::put_time(std::localtime(&time_t_now), "%Y%m%d_%H%M%S")
       << ".mp4";
    return ss.str();
}

5. alarm_manager.h

#pragma once

#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <chrono>
#include <mutex>
#include <map>

#include "config_manager.h"
#include "triton_inference.h"
#include "video_capture.h"

class AlarmManager {
public:
    AlarmManager(const std::string& alarm_log_path);
    void handleAlarm(const CameraConfig& config,
                     const std::vector<TritonInference::Detection>& detections,
                     std::shared_ptr<VideoCapture> video_capture);

private:
    bool checkAlarmCondition(const CameraConfig& config,
                             const std::vector<TritonInference::Detection>& detections);
    bool isWithinAlarmTime(const CameraConfig& config);

    void recordAlarm(const CameraConfig& config,
                     const std::string& image_path);

    std::string alarm_log_path_;

    std::unordered_map<std::string, std::chrono::system_clock::time_point> last_alarm_time_;
    std::mutex alarm_mutex_;
};

6. alarm_manager.cpp

#include "alarm_manager.h"
#include "logger.h"
#include <fstream>
#include <filesystem>
#include <iomanip>
#include <sstream>

namespace fs = std::filesystem;

AlarmManager::AlarmManager(const std::string& alarm_log_path)
    : alarm_log_path_(alarm_log_path) {
    fs::create_directories(alarm_log_path);
}

void AlarmManager::handleAlarm(const CameraConfig& config,
                               const std::vector<TritonInference::Detection>& detections,
                               std::shared_ptr<VideoCapture> video_capture) {

    if (!isWithinAlarmTime(config)) {
        LOG_INFO("Current time is not within alarm time for camera " + config.name);
        return;
    }

    if (!checkAlarmCondition(config, detections)) return;

    // Frequency control: Only one alarm per minute per camera
    {
        std::lock_guard<std::mutex> lock(alarm_mutex_);
        auto now = std::chrono::system_clock::now();
        auto last_time_it = last_alarm_time_.find(config.name);
        if (last_time_it != last_alarm_time_.end()) {
            auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - last_time_it->second).count();
            if (elapsed < 60) {
                LOG_INFO("Alarm for camera " + config.name + " suppressed due to frequency control.");
                return;
            }
        }
        last_alarm_time_[config.name] = now;
    }

    // Notify VideoCapture to start recording
    video_capture->startRecording();

    // Record alarm information
    std::string image_path = ""; // TODO: Obtain the corresponding alarm image path
    recordAlarm(config, image_path);
}

bool AlarmManager::checkAlarmCondition(
    const CameraConfig& config,
    const std::vector<TritonInference::Detection>& detections) {

    // Implement specific alarm strategy logic based on the configuration
    for (const auto& algorithm : config.algorithms) {
        auto strategy_it = config.alarm_strategies.find(algorithm);
        if (strategy_it != config.alarm_strategies.end()) {
            const std::string& strategy = strategy_it->second;

            // Simple parser for the strategy
            if (strategy.find(">") != std::string::npos) {
                size_t pos = strategy.find('>');
                std::string key = strategy.substr(0, pos);
                int threshold = std::stoi(strategy.substr(pos + 1));

                if (key == "person_count") {
                    int person_count = 0;
                    for (const auto& detection : detections) {
                        if (detection.class_id == 0) { // Assuming class_id 0 is person
                            person_count++;
                        }
                    }
                    if (person_count > threshold) {
                        return true;
                    }
                }
            } else if (strategy.find("=") != std::string::npos) {
                size_t pos = strategy.find('=');
                std::string key = strategy.substr(0, pos);
                std::string value = strategy.substr(pos + 1);

                if (value == "true") {
                    for (const auto& detection : detections) {
                        if ((key == "vehicle_present" && detection.class_id == 1) || // Assuming class_id 1 is vehicle
                            (key == "boat_present" && detection.class_id == 2)) {   // Assuming class_id 2 is boat
                            return true;
                        }
                    }
                }
            }
        }
    }

    return false; // No alarm condition met
}

bool AlarmManager::isWithinAlarmTime(const CameraConfig& config) {
    auto now = std::chrono::system_clock::now();
    std::time_t now_c = std::chrono::system_clock::to_time_t(now);
    tm* local_tm = std::localtime(&now_c);

    int current_seconds = local_tm->tm_hour * 3600 + local_tm->tm_min * 60 + local_tm->tm_sec;

    for (const auto& time_range : config.alarm_times) {
        std::istringstream ss_start(time_range.first);
        std::istringstream ss_end(time_range.second);
        int start_hour, start_minute, start_second = 0;
        int end_hour, end_minute, end_second = 0;
        char dummy;

        ss_start >> start_hour >> dummy >> start_minute >> dummy >> start_second;
        ss_end >> end_hour >> dummy >> end_minute >> dummy >> end_second;

        int start_seconds = start_hour * 3600 + start_minute * 60 + start_second;
        int end_seconds = end_hour * 3600 + end_minute * 60 + end_second;

        if (current_seconds >= start_seconds && current_seconds <= end_seconds) {
            return true;
        }
    }

    return false;
}

void AlarmManager::recordAlarm(const CameraConfig& config,
                               const std::string& image_path) {
    std::ofstream log_file(alarm_log_path_ + "/alarm_log.txt",
                           std::ios::app);

    if (log_file.is_open()) {
        auto now = std::chrono::system_clock::now();
        auto timestamp = std::chrono::system_clock::to_time_t(now);

        log_file << "Time: " << std::put_time(std::localtime(&timestamp), "%Y-%m-%d %H:%M:%S") << "\n"
                 << "Camera: " << config.name << "\n"
                 << "Alarm Image: " << image_path << "\n"
                 << "-------------------\n";

        log_file.close();
    }
}

7. triton_inference.h

#pragma once

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <memory>
#include "http_client.h"

namespace tc = triton::client;

class TritonInference {
public:
    TritonInference(const std::string& url, const std::vector<std::string>& model_names);

    struct Detection {
        cv::Rect box;
        float confidence;
        int class_id;
        std::string model_name;
    };

    std::vector<Detection> infer(const cv::Mat& image);

private:
    std::unique_ptr<tc::InferenceServerHttpClient> triton_client_;
    std::vector<std::string> model_names_;
    std::string input_name_;
    std::string output_name_;
    int input_h_;
    int input_w_;
    std::vector<std::string> class_names_;

    std::tuple<cv::Mat, float, cv::Point2f> preprocess(const cv::Mat& image);
    std::vector<float> prepare_input(const cv::Mat& image);
    std::vector<Detection> postprocess(const float* output_data, size_t output_size, float scale_factor, const cv::Point2f& offset, const cv::Size& original_size, const std::string& model_name);
};

8. triton_inference.cpp

#include "triton_inference.h"
#include "logger.h"

TritonInference::TritonInference(const std::string &url, const std::vector<std::string> &model_names)
    : model_names_(model_names)
{
    // Create HTTP client
    tc::Error err = tc::InferenceServerHttpClient::Create(
        &triton_client_, url, false);
    if (!err.IsOk())
    {
        throw std::runtime_error("Failed to create client: " + err.Message());
    }

    input_name_ = "images";
    output_name_ = "output0";
    input_h_ = 640;
    input_w_ = 640;

    // Initialize class names
    // Assuming class IDs: 0 - person, 1 - vehicle, 2 - boat
    class_names_ = {"person", "vehicle", "boat"};
}

std::vector<TritonInference::Detection> TritonInference::infer(const cv::Mat &image)
{
    if (image.empty())
    {
        throw std::runtime_error("Invalid image");
    }

    // Preprocess
    cv::Mat preprocessed;
    float scale_factor;
    cv::Point2f offset;
    std::tie(preprocessed, scale_factor, offset) = preprocess(image);

    // Prepare input data
    std::vector<float> input_data = prepare_input(preprocessed);

    // Create input tensor
    std::vector<int64_t> input_shape = {1, 3, input_h_, input_w_};

    std::vector<TritonInference::Detection> all_detections;

    for (const auto &model_name : model_names_)
    {
        try
        {
            tc::InferInput *input;
            tc::Error err = tc::InferInput::Create(&input, input_name_, input_shape, "FP32");
            if (!err.IsOk())
            {
                LOG_ERROR("Failed to create input: " + err.Message());
                continue;
            }
            std::shared_ptr<tc::InferInput> input_ptr(input);

            // Set input data
            err = input_ptr->AppendRaw(
                reinterpret_cast<uint8_t *>(input_data.data()),
                input_data.size() * sizeof(float));
            if (!err.IsOk())
            {
                LOG_ERROR("Failed to set input data: " + err.Message());
                continue;
            }

            // Create output
            tc::InferRequestedOutput *output;
            err = tc::InferRequestedOutput::Create(&output, output_name_);
            if (!err.IsOk())
            {
                LOG_ERROR("Failed to create output: " + err.Message());
                continue;
            }
            std::shared_ptr<tc::InferRequestedOutput> output_ptr(output);

            // Set inference options
            tc::InferOptions options(model_name);
            options.model_version_ = "1";

            // Perform inference
            std::vector<tc::InferInput *> inputs = {input_ptr.get()};
            std::vector<const tc::InferRequestedOutput *> outputs = {output_ptr.get()};
            tc::InferResult *result;

            err = triton_client_->Infer(&result, options, inputs, outputs);
            if (!err.IsOk())
            {
                LOG_ERROR("Failed to run inference: " + err.Message());
                continue;
            }
            std::shared_ptr<tc::InferResult> result_ptr(result);

            // Get output data
            const uint8_t *output_data;
            size_t output_byte_size;
            err = result_ptr->RawData(output_name_, &output_data, &output_byte_size);
            if (!err.IsOk())
            {
                LOG_ERROR("Failed to get output data: " + err.Message());
                continue;
            }

            // Postprocess
            std::vector<Detection> detections = postprocess(
                reinterpret_cast<const float *>(output_data),
                output_byte_size / sizeof(float),
                scale_factor,
                offset,
                image.size(),
                model_name);

            all_detections.insert(all_detections.end(), detections.begin(), detections.end());
        }
        catch (const std::exception &e)
        {
            LOG_ERROR("Error during inference: " + std::string(e.what()));
        }
    }

    return all_detections;
}

std::tuple<cv::Mat, float, cv::Point2f> TritonInference::preprocess(const cv::Mat &image)
{
    float ratio = std::min(
        static_cast<float>(input_w_) / image.cols,
        static_cast<float>(input_h_) / image.rows);

    int new_width = static_cast<int>(image.cols * ratio);
    int new_height = static_cast<int>(image.rows * ratio);

    cv::Mat resized;
    cv::resize(image, resized, cv::Size(new_width, new_height));

    cv::Mat canvas = cv::Mat::zeros(input_h_, input_w_, CV_8UC3);
    cv::Point2f offset(
        (input_w_ - new_width) / 2.0f,
        (input_h_ - new_height) / 2.0f);

    resized.copyTo(canvas(cv::Rect(
        offset.x, offset.y, new_width, new_height)));

    return {canvas, ratio, offset};
}

std::vector<float> TritonInference::prepare_input(const cv::Mat &image)
{
    cv::Mat blob;
    cv::dnn::blobFromImage(image, blob, 1.0 / 255.0,
                           cv::Size(input_w_, input_h_),
                           cv::Scalar(0, 0, 0), true, false, CV_32F);

    std::vector<float> input_data(blob.ptr<float>(),
                                  blob.ptr<float>() + blob.total());
    return input_data;
}

std::vector<TritonInference::Detection> TritonInference::postprocess(
    const float *output_data,
    size_t output_size,
    float scale_factor,
    const cv::Point2f &offset,
    const cv::Size &original_size,
    const std::string &model_name) {

    // Assuming the model outputs a flat array where each detection is represented by 6 values:
    // [batch_id, class_id, confidence, x1, y1, x2, y2]
    const int detection_size = 7; // Adjust according to the model's output
    size_t num_detections = output_size / detection_size;

    std::vector<Detection> detections;

    for (size_t i = 0; i < num_detections; ++i) {
        const float *det = output_data + i * detection_size;

        int batch_id = static_cast<int>(det[0]);
        int class_id = static_cast<int>(det[1]);
        float confidence = det[2];
        float x1 = (det[3] - offset.x) / scale_factor;
        float y1 = (det[4] - offset.y) / scale_factor;
        float x2 = (det[5] - offset.x) / scale_factor;
        float y2 = (det[6] - offset.y) / scale_factor;

        // Clamp coordinates
        x1 = std::max(0.0f, std::min(x1, static_cast<float>(original_size.width - 1)));
        y1 = std::max(0.0f, std::min(y1, static_cast<float>(original_size.height - 1)));
        x2 = std::max(0.0f, std::min(x2, static_cast<float>(original_size.width - 1)));
        y2 = std::max(0.0f, std::min(y2, static_cast<float>(original_size.height - 1)));

        cv::Rect box(cv::Point(x1, y1), cv::Point(x2, y2));

        Detection detection = {box, confidence, class_id, model_name};
        detections.push_back(detection);
    }

    return detections;
}】