2025-09-08 14:55:07 +08:00
|
|
|
|
// main.cpp
|
2025-10-17 13:38:55 +08:00
|
|
|
|
#include <unistd.h>
|
2025-10-15 08:50:01 +08:00
|
|
|
|
|
2025-09-08 14:55:07 +08:00
|
|
|
|
#include <atomic>
|
2025-09-10 12:49:41 +08:00
|
|
|
|
#include <chrono>
|
2025-10-17 13:38:55 +08:00
|
|
|
|
#include <csignal>
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
|
|
|
|
#include "app_config.hpp"
|
|
|
|
|
|
#include "logger.hpp"
|
|
|
|
|
|
#include "mqtt_client_wrapper.hpp"
|
2025-11-14 09:42:17 +08:00
|
|
|
|
#include "record_manager.hpp"
|
2025-10-17 13:38:55 +08:00
|
|
|
|
#include "rtmp_manager.hpp"
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-10-15 09:01:15 +08:00
|
|
|
|
std::atomic<bool> g_running(true);
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-10-15 08:50:01 +08:00
|
|
|
|
static void signal_handler(int signum)
|
2025-09-08 14:55:07 +08:00
|
|
|
|
{
|
2025-09-10 12:49:41 +08:00
|
|
|
|
g_running.store(false, std::memory_order_relaxed);
|
2025-10-15 08:50:01 +08:00
|
|
|
|
const char msg[] = "[MAIN] Signal received, shutting down...\n";
|
2025-09-10 12:49:41 +08:00
|
|
|
|
write(STDERR_FILENO, msg, sizeof(msg) - 1);
|
2025-09-08 14:55:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-14 10:56:44 +08:00
|
|
|
|
void test_mp4_info(const std::string& path)
|
|
|
|
|
|
{
|
|
|
|
|
|
MP4FileHandle file = MP4Read(path.c_str());
|
|
|
|
|
|
if (file == MP4_INVALID_FILE_HANDLE)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::cerr << "Failed to open: " << path << std::endl;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "=== MP4 Info for: " << path << " ===\n";
|
|
|
|
|
|
|
|
|
|
|
|
// duration (in seconds)
|
|
|
|
|
|
MP4Duration duration = MP4GetDuration(file);
|
|
|
|
|
|
uint32_t timeScale = MP4GetTimeScale(file);
|
|
|
|
|
|
double seconds = (double)duration / timeScale;
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Duration: " << seconds << " sec\n";
|
|
|
|
|
|
|
|
|
|
|
|
// video track
|
|
|
|
|
|
MP4TrackId videoTrack = MP4FindTrackId(file, 0, MP4_VIDEO_TRACK_TYPE);
|
|
|
|
|
|
if (videoTrack != MP4_INVALID_TRACK_ID)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t width = MP4GetTrackVideoWidth(file, videoTrack);
|
|
|
|
|
|
uint32_t height = MP4GetTrackVideoHeight(file, videoTrack);
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "Video Track: YES\n";
|
|
|
|
|
|
std::cout << "Resolution: " << width << " x " << height << "\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
std::cout << "Video Track: NO\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// audio track
|
|
|
|
|
|
MP4TrackId audioTrack = MP4FindTrackId(file, 0, MP4_AUDIO_TRACK_TYPE);
|
|
|
|
|
|
if (audioTrack != MP4_INVALID_TRACK_ID)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::cout << "Audio Track: YES\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
std::cout << "Audio Track: NO\n";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MP4Close(file);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-08 14:55:07 +08:00
|
|
|
|
int main()
|
|
|
|
|
|
{
|
2025-10-17 15:29:38 +08:00
|
|
|
|
// ---------- 信号处理 ----------
|
2025-10-15 08:50:01 +08:00
|
|
|
|
struct sigaction sa{};
|
|
|
|
|
|
sa.sa_handler = signal_handler;
|
|
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
|
|
sa.sa_flags = 0;
|
|
|
|
|
|
sigaction(SIGINT, &sa, nullptr);
|
|
|
|
|
|
sigaction(SIGTERM, &sa, nullptr);
|
2025-09-08 14:55:07 +08:00
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
|
|
2025-10-17 15:29:38 +08:00
|
|
|
|
// ---------- 初始化日志 ----------
|
2025-11-13 14:42:01 +08:00
|
|
|
|
Logger::init(get_executable_dir_file_path("logs"), 7);
|
2025-10-17 15:29:38 +08:00
|
|
|
|
LOG_INFO("[MAIN] ===== Vehicle Video Service Starting =====");
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// std::string base = "/sata/record/";
|
|
|
|
|
|
// RecordManager rm(base);
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// std::cout << "[RecordManager] scanning " << base << " ..." << std::endl;
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// rm.scanAll();
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// std::cout << "\n=== 全部扫描结果 ===\n";
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// // 遍历所有 stream
|
|
|
|
|
|
// rm.dumpIndex();
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// // 测试一次查询:你可以换成真实时间戳
|
|
|
|
|
|
// std::cout << "\n=== 测试 querySegments() ===\n";
|
2025-10-17 15:29:38 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// // 随便选一个时间区间,比如 2025-11-13 10:23:00 ~ 11:00:00
|
|
|
|
|
|
// // 你可以换成真实值
|
|
|
|
|
|
// int64_t start = RecordManager::toMsTimestamp("2025-11-13 10:00:00");
|
|
|
|
|
|
// int64_t end = RecordManager::toMsTimestamp("2025-11-13 14:30:00");
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// auto segments = rm.querySegments("AHD1_main", start, end);
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// std::cout << "找到录像段数量 = " << segments.size() << "\n";
|
2025-11-14 09:42:17 +08:00
|
|
|
|
|
2025-11-14 10:58:24 +08:00
|
|
|
|
// for (auto& seg : segments)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// std::cout << "\n--- Segment " << seg.index << " ---\n";
|
|
|
|
|
|
// std::cout << "segmentId = " << seg.segment_id << "\n";
|
|
|
|
|
|
// std::cout << "start_ms = " << seg.start_ms << "\n";
|
|
|
|
|
|
// std::cout << "end_ms = " << seg.end_ms << "\n";
|
|
|
|
|
|
// std::cout << "files = " << seg.files.size() << "\n";
|
|
|
|
|
|
|
|
|
|
|
|
// for (auto& f : seg.files)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// std::cout << " file: " << f.path << "\n [" << f.start_ms << " ~ " << f.end_ms << "]\n";
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
2025-09-08 14:55:07 +08:00
|
|
|
|
|
2025-11-14 11:00:56 +08:00
|
|
|
|
test_mp4_info("/sata/record/AHD1_main/2025-11-13/13/13-40-44.mp4");
|
|
|
|
|
|
test_mp4_info("/sata/record/AHD1_main/2025-11-13/13/13-59-16.mp4");
|
2025-11-14 10:56:44 +08:00
|
|
|
|
|
2025-11-14 09:42:17 +08:00
|
|
|
|
// try
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // 加载配置
|
|
|
|
|
|
// g_app_config = AppConfig::load_from_file(get_executable_dir_file_path("config.json"));
|
|
|
|
|
|
// LOG_INFO("[MAIN] Loaded config from config.json");
|
|
|
|
|
|
// }
|
|
|
|
|
|
// catch (const std::exception& e)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// LOG_ERROR(std::string("[MAIN] Failed to load config: ") + e.what());
|
|
|
|
|
|
// return -1;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// // ---------- 初始化 GStreamer ----------
|
|
|
|
|
|
// RTMPManager::init();
|
|
|
|
|
|
|
|
|
|
|
|
// // ---------- 自动推流(8 路录像守护) ----------
|
|
|
|
|
|
// LOG_INFO("[MAIN] Starting all record streams...");
|
|
|
|
|
|
// RTMPManager::start_all();
|
|
|
|
|
|
|
|
|
|
|
|
// // 启动 MQTT 线程
|
|
|
|
|
|
// std::thread mqtt_thread(
|
|
|
|
|
|
// []
|
|
|
|
|
|
// {
|
|
|
|
|
|
// try
|
|
|
|
|
|
// {
|
|
|
|
|
|
// LOG_INFO("[MAIN] MQTT thread started.");
|
|
|
|
|
|
// mqtt_client_thread_func(); // 在回调里执行推流控制
|
|
|
|
|
|
// }
|
|
|
|
|
|
// catch (const std::exception& e)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// LOG_ERROR(std::string("[MAIN] MQTT thread crashed: ") + e.what());
|
|
|
|
|
|
// }
|
|
|
|
|
|
// LOG_INFO("[MAIN] MQTT thread exiting...");
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
|
|
// // 主循环,仅等待退出信号
|
|
|
|
|
|
// while (g_running.load(std::memory_order_relaxed)) std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
|
|
|
|
|
|
|
|
|
|
// // ---------- 退出清理 ----------
|
|
|
|
|
|
// LOG_INFO("[MAIN] Shutdown requested. Stopping RTMP streams...");
|
|
|
|
|
|
// RTMPManager::stop_all();
|
|
|
|
|
|
|
|
|
|
|
|
// if (mqtt_thread.joinable())
|
|
|
|
|
|
// {
|
|
|
|
|
|
// mqtt_thread.join();
|
|
|
|
|
|
// LOG_INFO("[MAIN] MQTT thread joined.");
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2025-10-17 15:29:38 +08:00
|
|
|
|
LOG_INFO("[MAIN] ===== Vehicle Video Service Exited Cleanly =====");
|
2025-09-08 14:55:07 +08:00
|
|
|
|
return 0;
|
2025-10-15 08:50:01 +08:00
|
|
|
|
}
|