2025-09-08 14:55:07 +08:00
|
|
|
|
// main.cpp
|
|
|
|
|
|
#include "app_config.hpp"
|
|
|
|
|
|
#include "rtsp_manager.hpp"
|
|
|
|
|
|
#include "logger.hpp"
|
2025-09-08 15:25:59 +08:00
|
|
|
|
#include "mqtt_client_wrapper.hpp"
|
|
|
|
|
|
#include <thread>
|
2025-09-08 14:55:07 +08:00
|
|
|
|
#include <atomic>
|
|
|
|
|
|
#include <csignal>
|
2025-09-10 12:49:41 +08:00
|
|
|
|
#include <unistd.h> // write, STDOUT_FILENO
|
|
|
|
|
|
#include <chrono>
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-09-10 13:03:16 +08:00
|
|
|
|
// 可通过这些开关快速启用/禁用线程进行调试
|
2025-09-10 13:15:42 +08:00
|
|
|
|
constexpr bool ENABLE_RTSP_THREAD = false; // 设置为 false 禁用 RTSP 线程
|
|
|
|
|
|
constexpr bool ENABLE_MQTT_THREAD = true; // 设置为 false 禁用 MQTT 线程
|
2025-09-10 13:03:16 +08:00
|
|
|
|
|
2025-09-08 14:55:07 +08:00
|
|
|
|
std::atomic<bool> g_running(true);
|
|
|
|
|
|
|
2025-09-10 12:49:41 +08:00
|
|
|
|
static void minimal_signal_handler(int signum)
|
2025-09-08 14:55:07 +08:00
|
|
|
|
{
|
2025-09-10 12:49:41 +08:00
|
|
|
|
// 只做非常有限且 async-signal-safe 的操作
|
|
|
|
|
|
g_running.store(false, std::memory_order_relaxed);
|
|
|
|
|
|
const char msg[] = "[MAIN] Signal received, initiating shutdown...\n";
|
|
|
|
|
|
write(STDERR_FILENO, msg, sizeof(msg) - 1);
|
|
|
|
|
|
// 不要调用 LOG_*、malloc、std::string、mutex、exit 等
|
2025-09-08 14:55:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main()
|
|
|
|
|
|
{
|
2025-09-10 12:49:41 +08:00
|
|
|
|
// 安装信号处理(SIGINT 和 SIGTERM)
|
|
|
|
|
|
struct sigaction sigAct{};
|
|
|
|
|
|
sigAct.sa_handler = minimal_signal_handler;
|
|
|
|
|
|
sigemptyset(&sigAct.sa_mask);
|
|
|
|
|
|
sigAct.sa_flags = 0;
|
|
|
|
|
|
sigaction(SIGINT, &sigAct, nullptr);
|
|
|
|
|
|
sigaction(SIGTERM, &sigAct, nullptr);
|
2025-09-08 14:55:07 +08:00
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化日志文件
|
2025-09-08 15:32:07 +08:00
|
|
|
|
Logger::set_log_to_file(get_executable_dir_file_path("app.log"));
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-09-08 15:32:07 +08:00
|
|
|
|
// 从可执行文件所在目录读取配置文件
|
|
|
|
|
|
g_app_config = AppConfig::load_from_file(get_executable_dir_file_path("config.json"));
|
2025-09-08 14:55:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (const std::exception &e)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR(std::string("Failed to load config: ") + e.what());
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 12:49:41 +08:00
|
|
|
|
// 线程退出标志,用于超时等待
|
|
|
|
|
|
std::atomic<bool> rtsp_thread_exited(false);
|
|
|
|
|
|
std::atomic<bool> mqtt_thread_exited(false);
|
|
|
|
|
|
|
2025-09-09 11:08:34 +08:00
|
|
|
|
// 先在主线程初始化 GStreamer
|
|
|
|
|
|
RTSPManager::init();
|
|
|
|
|
|
|
2025-09-10 12:55:48 +08:00
|
|
|
|
std::thread rtsp_thread;
|
|
|
|
|
|
std::thread mqtt_thread;
|
2025-09-10 12:54:39 +08:00
|
|
|
|
|
2025-09-10 13:03:16 +08:00
|
|
|
|
// 启动 RTSP 线程(如果启用)
|
|
|
|
|
|
if (ENABLE_RTSP_THREAD)
|
|
|
|
|
|
{
|
|
|
|
|
|
rtsp_thread = std::thread([&]()
|
|
|
|
|
|
{
|
|
|
|
|
|
RTSPManager::start(g_app_config.cameras);
|
|
|
|
|
|
rtsp_thread_exited.store(true, std::memory_order_relaxed); });
|
|
|
|
|
|
LOG_INFO("[MAIN] RTSP thread started");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("[MAIN] RTSP thread disabled by build-time toggle");
|
|
|
|
|
|
}
|
2025-09-10 12:49:41 +08:00
|
|
|
|
|
2025-09-10 13:03:16 +08:00
|
|
|
|
// 启动 MQTT 线程(如果启用)
|
|
|
|
|
|
if (ENABLE_MQTT_THREAD)
|
|
|
|
|
|
{
|
|
|
|
|
|
mqtt_thread = std::thread([&]()
|
|
|
|
|
|
{
|
|
|
|
|
|
mqtt_client_thread_func();
|
|
|
|
|
|
mqtt_thread_exited.store(true, std::memory_order_relaxed); });
|
|
|
|
|
|
LOG_INFO("[MAIN] MQTT thread started");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("[MAIN] MQTT thread disabled by build-time toggle");
|
|
|
|
|
|
}
|
2025-09-08 15:25:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 等待退出信号
|
2025-09-10 12:49:41 +08:00
|
|
|
|
while (g_running.load(std::memory_order_relaxed))
|
2025-09-08 15:51:10 +08:00
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
|
|
|
|
|
2025-09-10 12:49:41 +08:00
|
|
|
|
LOG_INFO("[MAIN] Shutdown requested, stopping services...");
|
|
|
|
|
|
|
|
|
|
|
|
// 等线程优雅退出:总等待时间 (可调整)
|
|
|
|
|
|
const auto max_wait = std::chrono::seconds(5);
|
|
|
|
|
|
const auto poll_interval = std::chrono::milliseconds(100);
|
|
|
|
|
|
auto deadline = std::chrono::steady_clock::now() + max_wait;
|
|
|
|
|
|
|
2025-09-10 13:03:16 +08:00
|
|
|
|
if (ENABLE_RTSP_THREAD)
|
2025-09-10 12:49:41 +08:00
|
|
|
|
{
|
2025-09-10 13:12:27 +08:00
|
|
|
|
RTSPManager::stop();
|
|
|
|
|
|
|
|
|
|
|
|
auto stop_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10);
|
|
|
|
|
|
while (RTSPManager::is_any_streaming() && std::chrono::steady_clock::now() < stop_deadline)
|
2025-09-10 12:49:41 +08:00
|
|
|
|
{
|
2025-09-10 13:12:27 +08:00
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
2025-09-10 12:49:41 +08:00
|
|
|
|
}
|
2025-09-10 13:03:16 +08:00
|
|
|
|
|
|
|
|
|
|
if (rtsp_thread.joinable())
|
2025-09-10 13:12:27 +08:00
|
|
|
|
rtsp_thread.join();
|
2025-09-10 12:49:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 13:03:16 +08:00
|
|
|
|
// 重置 MQTT 线程等待的截止时间
|
2025-09-10 12:49:41 +08:00
|
|
|
|
deadline = std::chrono::steady_clock::now() + max_wait;
|
2025-09-10 13:03:16 +08:00
|
|
|
|
|
|
|
|
|
|
if (ENABLE_MQTT_THREAD)
|
2025-09-10 11:06:42 +08:00
|
|
|
|
{
|
2025-09-10 13:03:16 +08:00
|
|
|
|
while (!mqtt_thread_exited.load(std::memory_order_relaxed) &&
|
|
|
|
|
|
std::chrono::steady_clock::now() < deadline)
|
2025-09-10 12:49:41 +08:00
|
|
|
|
{
|
2025-09-10 13:03:16 +08:00
|
|
|
|
std::this_thread::sleep_for(poll_interval);
|
2025-09-10 12:49:41 +08:00
|
|
|
|
}
|
2025-09-10 13:03:16 +08:00
|
|
|
|
|
|
|
|
|
|
if (mqtt_thread.joinable())
|
2025-09-10 12:49:41 +08:00
|
|
|
|
{
|
2025-09-10 13:03:16 +08:00
|
|
|
|
if (mqtt_thread_exited.load(std::memory_order_relaxed))
|
|
|
|
|
|
{
|
|
|
|
|
|
mqtt_thread.join();
|
|
|
|
|
|
LOG_INFO("[MAIN] MQTT thread finished and joined.");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_WARN("[MAIN] MQTT thread did not exit within timeout.");
|
|
|
|
|
|
}
|
2025-09-10 12:49:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 13:03:16 +08:00
|
|
|
|
// 如果有线程仍未退出,则强制终止
|
|
|
|
|
|
bool any_failed = false;
|
|
|
|
|
|
if (ENABLE_RTSP_THREAD && rtsp_thread.joinable() && !rtsp_thread_exited.load(std::memory_order_relaxed))
|
|
|
|
|
|
any_failed = true;
|
|
|
|
|
|
if (ENABLE_MQTT_THREAD && mqtt_thread.joinable() && !mqtt_thread_exited.load(std::memory_order_relaxed))
|
|
|
|
|
|
any_failed = true;
|
|
|
|
|
|
|
|
|
|
|
|
if (any_failed)
|
2025-09-10 12:49:41 +08:00
|
|
|
|
{
|
|
|
|
|
|
LOG_ERROR("[MAIN] Threads did not exit in time. Forcing immediate termination.");
|
2025-09-10 13:03:16 +08:00
|
|
|
|
_exit(1);
|
2025-09-10 11:06:42 +08:00
|
|
|
|
}
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-09-08 15:45:22 +08:00
|
|
|
|
LOG_INFO("[MAIN] Program exited cleanly.");
|
2025-09-08 14:55:07 +08:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|