log
This commit is contained in:
parent
94c59f4ba8
commit
b0e66efe82
@ -5,6 +5,8 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -12,18 +14,17 @@ namespace fs = std::filesystem;
|
|||||||
//
|
//
|
||||||
RecordManager::RecordManager(const std::string& srs_record_cfg_path) : srs_record_cfg_path_(srs_record_cfg_path)
|
RecordManager::RecordManager(const std::string& srs_record_cfg_path) : srs_record_cfg_path_(srs_record_cfg_path)
|
||||||
{
|
{
|
||||||
std::cout << "[RecordManager] Loading SRS config: " << srs_record_cfg_path_ << "\n";
|
LOG_INFO("[RecordManager] Loading SRS config: " + srs_record_cfg_path_);
|
||||||
|
|
||||||
bool ok = loadSrsConfig();
|
bool ok = loadSrsConfig();
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
std::cout << "[RecordManager] ERROR loading SRS config\n";
|
LOG_ERROR("[RecordManager] Failed to load SRS config.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << "[RecordManager] SRS config loaded successfully.\n";
|
LOG_INFO("[RecordManager] SRS config loaded. record_dir=" + record_dir_ +
|
||||||
std::cout << " record_dir_ = " << record_dir_ << "\n";
|
", dvr_duration=" + std::to_string(dvr_duration_sec_) + "s");
|
||||||
std::cout << " dvr_duration_sec = " << dvr_duration_sec_ << "\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,51 +36,48 @@ bool RecordManager::loadSrsConfig()
|
|||||||
std::ifstream ifs(srs_record_cfg_path_);
|
std::ifstream ifs(srs_record_cfg_path_);
|
||||||
if (!ifs.is_open())
|
if (!ifs.is_open())
|
||||||
{
|
{
|
||||||
std::cerr << "[RecordManager] Failed to open SRS config: " << srs_record_cfg_path_ << "\n";
|
LOG_ERROR("[RecordManager] Cannot open SRS config file: " + srs_record_cfg_path_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
std::string dvr_path;
|
std::string dvr_path;
|
||||||
|
|
||||||
while (std::getline(ifs, line))
|
while (std::getline(ifs, line))
|
||||||
{
|
{
|
||||||
// 去掉前后空白
|
|
||||||
line.erase(0, line.find_first_not_of(" \t"));
|
line.erase(0, line.find_first_not_of(" \t"));
|
||||||
if (line.empty() || line[0] == '#') continue;
|
if (line.empty() || line[0] == '#') continue;
|
||||||
|
|
||||||
|
// 解析 dvr_path
|
||||||
if (line.find("dvr_path") != std::string::npos)
|
if (line.find("dvr_path") != std::string::npos)
|
||||||
{
|
{
|
||||||
auto pos = line.find('/');
|
auto pos = line.find('/');
|
||||||
auto semicolon = line.find(';');
|
auto semicolon = line.find(';');
|
||||||
if (pos != std::string::npos && semicolon != std::string::npos)
|
if (pos != std::string::npos && semicolon != std::string::npos)
|
||||||
{
|
|
||||||
dvr_path = line.substr(pos, semicolon - pos);
|
dvr_path = line.substr(pos, semicolon - pos);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析 dvr_duration
|
||||||
else if (line.find("dvr_duration") != std::string::npos)
|
else if (line.find("dvr_duration") != std::string::npos)
|
||||||
{
|
{
|
||||||
int sec = 0;
|
int sec = 0;
|
||||||
if (sscanf(line.c_str(), "dvr_duration %d", &sec) == 1)
|
if (sscanf(line.c_str(), "dvr_duration %d", &sec) == 1) dvr_duration_sec_ = sec;
|
||||||
{
|
|
||||||
dvr_duration_sec_ = sec;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dvr_path.empty())
|
if (dvr_path.empty())
|
||||||
{
|
{
|
||||||
// 例子:/sata/record/[stream]/[2006]-[01]-[02]/...
|
LOG_ERROR("[RecordManager] dvr_path not found in config.");
|
||||||
// → record_dir_ = /sata/record/
|
return false;
|
||||||
auto pos = dvr_path.find("[stream]");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
record_dir_ = dvr_path.substr(0, pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record_dir_.empty())
|
// /sata/record/[stream]/[2006]-[01]-[02]/...
|
||||||
|
auto pos = dvr_path.find("[stream]");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
record_dir_ = dvr_path.substr(0, pos);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
std::cerr << "[RecordManager] ERROR: cannot parse dvr_path.\n";
|
LOG_ERROR("[RecordManager] Cannot extract record_dir from dvr_path: " + dvr_path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +91,11 @@ void RecordManager::scanAll()
|
|||||||
{
|
{
|
||||||
index_.clear();
|
index_.clear();
|
||||||
|
|
||||||
if (!fs::exists(record_dir_)) return;
|
if (!fs::exists(record_dir_))
|
||||||
|
{
|
||||||
|
LOG_WARN("[RecordManager] record_dir not exist: " + record_dir_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto const& entry : fs::recursive_directory_iterator(record_dir_))
|
for (auto const& entry : fs::recursive_directory_iterator(record_dir_))
|
||||||
{
|
{
|
||||||
@ -108,11 +110,12 @@ void RecordManager::scanAll()
|
|||||||
index_[info.stream].push_back(info);
|
index_[info.stream].push_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 各 stream 排序
|
|
||||||
for (auto& kv : index_)
|
for (auto& kv : index_)
|
||||||
{
|
{
|
||||||
sortStream(kv.first);
|
sortStream(kv.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_INFO("[RecordManager] scanAll completed. streams=" + std::to_string(index_.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -121,7 +124,6 @@ void RecordManager::scanAll()
|
|||||||
void RecordManager::sortStream(const std::string& stream)
|
void RecordManager::sortStream(const std::string& stream)
|
||||||
{
|
{
|
||||||
auto& files = index_[stream];
|
auto& files = index_[stream];
|
||||||
|
|
||||||
std::sort(files.begin(), files.end(),
|
std::sort(files.begin(), files.end(),
|
||||||
[](const RecordFileInfo& a, const RecordFileInfo& b) { return a.start_ms < b.start_ms; });
|
[](const RecordFileInfo& a, const RecordFileInfo& b) { return a.start_ms < b.start_ms; });
|
||||||
}
|
}
|
||||||
@ -129,9 +131,6 @@ void RecordManager::sortStream(const std::string& stream)
|
|||||||
//
|
//
|
||||||
// 解析路径得到文件时间戳
|
// 解析路径得到文件时间戳
|
||||||
//
|
//
|
||||||
// 格式:
|
|
||||||
/// sata/record/AHD1_main/2025-11-13/10/10-23-17.mp4
|
|
||||||
//
|
|
||||||
RecordFileInfo RecordManager::parseFile(const fs::path& p)
|
RecordFileInfo RecordManager::parseFile(const fs::path& p)
|
||||||
{
|
{
|
||||||
RecordFileInfo info;
|
RecordFileInfo info;
|
||||||
@ -139,9 +138,8 @@ RecordFileInfo RecordManager::parseFile(const fs::path& p)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 目录结构倒推
|
|
||||||
auto filename = p.filename().string(); // 10-23-17.mp4
|
auto filename = p.filename().string(); // 10-23-17.mp4
|
||||||
auto date_dir = p.parent_path().parent_path().filename().string(); // 2025-11-13
|
auto date_dir = p.parent_path().parent_path().filename().string(); // yyyy-mm-dd
|
||||||
auto stream_dir = p.parent_path().parent_path().parent_path().filename().string();
|
auto stream_dir = p.parent_path().parent_path().parent_path().filename().string();
|
||||||
|
|
||||||
info.stream = stream_dir;
|
info.stream = stream_dir;
|
||||||
@ -161,20 +159,17 @@ RecordFileInfo RecordManager::parseFile(const fs::path& p)
|
|||||||
int64_t start = std::mktime(&tm) * 1000LL;
|
int64_t start = std::mktime(&tm) * 1000LL;
|
||||||
info.start_ms = start;
|
info.start_ms = start;
|
||||||
|
|
||||||
// ====== 加入 mp4v2 读取真实 duration ======
|
// 读取 mp4 时长
|
||||||
MP4FileHandle hFile = MP4Read(info.path.c_str());
|
MP4FileHandle hFile = MP4Read(info.path.c_str());
|
||||||
if (hFile != MP4_INVALID_FILE_HANDLE)
|
if (hFile != MP4_INVALID_FILE_HANDLE)
|
||||||
{
|
{
|
||||||
double durSec = MP4GetDuration(hFile) / (double)MP4GetTimeScale(hFile);
|
double durSec = MP4GetDuration(hFile) / (double)MP4GetTimeScale(hFile);
|
||||||
MP4Close(hFile);
|
MP4Close(hFile);
|
||||||
|
info.end_ms = start + (int64_t)(durSec * 1000.0);
|
||||||
int64_t dur_ms = (int64_t)(durSec * 1000.0);
|
|
||||||
info.end_ms = start + dur_ms;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 出错 fallback
|
info.end_ms = start + 60000; // fallback
|
||||||
info.end_ms = start + 60000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
@ -187,38 +182,32 @@ RecordFileInfo RecordManager::parseFile(const fs::path& p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// 查询某段时间的录像段
|
// 查询录像段
|
||||||
//
|
//
|
||||||
std::vector<RecordSegment> RecordManager::querySegments(const std::string& stream, int64_t start_ms, int64_t end_ms)
|
std::vector<RecordSegment> RecordManager::querySegments(const std::string& stream, int64_t start_ms, int64_t end_ms)
|
||||||
{
|
{
|
||||||
std::vector<RecordSegment> result;
|
std::vector<RecordSegment> result;
|
||||||
|
|
||||||
if (!index_.count(stream)) return result;
|
if (!index_.count(stream)) return result;
|
||||||
|
|
||||||
const auto& files = index_[stream];
|
const auto& files = index_[stream];
|
||||||
|
|
||||||
std::vector<RecordFileInfo> hits;
|
std::vector<RecordFileInfo> hits;
|
||||||
|
|
||||||
// 先找到落在时间区间内的文件
|
|
||||||
for (const auto& f : files)
|
for (const auto& f : files)
|
||||||
{
|
{
|
||||||
bool overlap = !(f.end_ms <= start_ms || f.start_ms >= end_ms);
|
bool overlap = !(f.end_ms <= start_ms || f.start_ms >= end_ms);
|
||||||
|
|
||||||
if (overlap) hits.push_back(f);
|
if (overlap) hits.push_back(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hits.empty()) return result;
|
if (hits.empty()) return result;
|
||||||
|
|
||||||
// 合并连续文件为段
|
|
||||||
std::sort(hits.begin(), hits.end(), [](auto& a, auto& b) { return a.start_ms < b.start_ms; });
|
std::sort(hits.begin(), hits.end(), [](auto& a, auto& b) { return a.start_ms < b.start_ms; });
|
||||||
|
|
||||||
int idx = 1;
|
int idx = 1;
|
||||||
RecordSegment seg{};
|
RecordSegment seg{};
|
||||||
seg.index = idx;
|
seg.index = idx;
|
||||||
seg.start_ms = hits[0].start_ms;
|
seg.start_ms = hits[0].start_ms;
|
||||||
seg.files.push_back(hits[0]);
|
|
||||||
seg.end_ms = hits[0].end_ms;
|
seg.end_ms = hits[0].end_ms;
|
||||||
seg.segment_id = hits[0].stream + "_" + std::to_string(seg.start_ms) + "_" + std::to_string(seg.end_ms);
|
seg.files.push_back(hits[0]);
|
||||||
|
|
||||||
for (size_t i = 1; i < hits.size(); ++i)
|
for (size_t i = 1; i < hits.size(); ++i)
|
||||||
{
|
{
|
||||||
@ -234,36 +223,29 @@ std::vector<RecordSegment> RecordManager::querySegments(const std::string& strea
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 关闭上一段
|
seg.segment_id = stream + "_" + std::to_string(seg.start_ms) + "_" + std::to_string(seg.end_ms);
|
||||||
seg.segment_id =
|
|
||||||
seg.files[0].stream + "_" + std::to_string(seg.start_ms) + "_" + std::to_string(seg.end_ms);
|
|
||||||
result.push_back(seg);
|
result.push_back(seg);
|
||||||
|
|
||||||
// 开新段
|
|
||||||
idx++;
|
idx++;
|
||||||
seg = RecordSegment{};
|
seg = RecordSegment{};
|
||||||
seg.index = idx;
|
seg.index = idx;
|
||||||
seg.files.push_back(cur);
|
|
||||||
seg.start_ms = cur.start_ms;
|
seg.start_ms = cur.start_ms;
|
||||||
seg.end_ms = cur.end_ms;
|
seg.end_ms = cur.end_ms;
|
||||||
|
seg.files.push_back(cur);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最后一段补进去
|
seg.segment_id = stream + "_" + std::to_string(seg.start_ms) + "_" + std::to_string(seg.end_ms);
|
||||||
seg.segment_id = seg.files[0].stream + "_" + std::to_string(seg.start_ms) + "_" + std::to_string(seg.end_ms);
|
|
||||||
result.push_back(seg);
|
result.push_back(seg);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// 根据 segmentId 反查 segment
|
// 根据 segmentId 获取 segment
|
||||||
//
|
//
|
||||||
RecordSegment RecordManager::getSegment(const std::string& segmentId)
|
RecordSegment RecordManager::getSegment(const std::string& segmentId)
|
||||||
{
|
{
|
||||||
// segmentId 格式:
|
|
||||||
// AHD1_main_1731465600000_1731467400000
|
|
||||||
|
|
||||||
auto pos1 = segmentId.find('_');
|
auto pos1 = segmentId.find('_');
|
||||||
auto pos2 = segmentId.find('_', pos1 + 1);
|
auto pos2 = segmentId.find('_', pos1 + 1);
|
||||||
if (pos1 == std::string::npos || pos2 == std::string::npos) return {};
|
if (pos1 == std::string::npos || pos2 == std::string::npos) return {};
|
||||||
@ -273,23 +255,21 @@ RecordSegment RecordManager::getSegment(const std::string& segmentId)
|
|||||||
int64_t end_ms = std::stoll(segmentId.substr(pos2 + 1));
|
int64_t end_ms = std::stoll(segmentId.substr(pos2 + 1));
|
||||||
|
|
||||||
auto segs = querySegments(stream, start_ms, end_ms);
|
auto segs = querySegments(stream, start_ms, end_ms);
|
||||||
|
|
||||||
for (auto& s : segs)
|
for (auto& s : segs)
|
||||||
{
|
{
|
||||||
if (s.start_ms == start_ms && s.end_ms == end_ms) return s;
|
if (s.start_ms == start_ms && s.end_ms == end_ms) return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t RecordManager::toMsTimestamp(const std::string& s)
|
int64_t RecordManager::toMsTimestamp(const std::string& s)
|
||||||
{
|
{
|
||||||
std::tm tm = {};
|
std::tm tm{};
|
||||||
char* ret = strptime(s.c_str(), "%Y-%m-%d %H:%M:%S", &tm);
|
char* ret = strptime(s.c_str(), "%Y-%m-%d %H:%M:%S", &tm);
|
||||||
if (!ret) return -1;
|
if (!ret) return -1;
|
||||||
|
|
||||||
time_t t = mktime(&tm); // local time (UTC+8)
|
time_t t = mktime(&tm);
|
||||||
return static_cast<int64_t>(t) * 1000;
|
return (int64_t)t * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RecordManager::toReadable(int64_t ms)
|
std::string RecordManager::toReadable(int64_t ms)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user