#include "record_manager.hpp" #include #include #include namespace fs = std::filesystem; RecordManager::RecordManager(const std::string& base_dir) : base_dir_(base_dir) {} // // 扫描所有文件 // void RecordManager::scanAll() { index_.clear(); if (!fs::exists(base_dir_)) return; for (auto const& entry : fs::recursive_directory_iterator(base_dir_)) { if (!entry.is_regular_file()) continue; auto p = entry.path(); if (p.extension() != ".mp4") continue; auto info = parseFile(p); if (info.start_ms <= 0) continue; index_[info.stream].push_back(info); } // 各 stream 排序 for (auto& kv : index_) { sortStream(kv.first); } } // // 排序函数:按 start_ms 升序 // void RecordManager::sortStream(const std::string& stream) { auto& files = index_[stream]; std::sort(files.begin(), files.end(), [](const RecordFileInfo& a, const RecordFileInfo& b) { return a.start_ms < b.start_ms; }); } // // 解析路径得到文件时间戳 // // 格式: /// sata/record/AHD1_main/2025-11-13/10/10-23-17.mp4 // RecordFileInfo RecordManager::parseFile(const fs::path& p) { RecordFileInfo info; info.path = p.string(); try { // 目录结构倒推 auto filename = p.filename().string(); // 10-23-17.mp4 auto hour_dir = p.parent_path().filename().string(); // 10 auto date_dir = p.parent_path().parent_path().filename().string(); // 2025-11-13 auto stream_dir = p.parent_path().parent_path().parent_path().filename().string(); info.stream = stream_dir; // 日期解析 int y = 0, mon = 0, d = 0; sscanf(date_dir.c_str(), "%d-%d-%d", &y, &mon, &d); // 文件名解析 H-M-S int h = 0, m = 0, s = 0; sscanf(filename.c_str(), "%d-%d-%d", &h, &m, &s); std::tm t{}; t.tm_year = y - 1900; t.tm_mon = mon - 1; t.tm_mday = d; t.tm_hour = h; t.tm_min = m; t.tm_sec = s; int64_t epoch = std::mktime(&t) * 1000LL; info.start_ms = epoch; info.end_ms = epoch + 60 * 1000; // 默认 1 分钟 } catch (...) { info.start_ms = -1; info.end_ms = -1; } return info; } // // 查询某段时间的录像段 // std::vector RecordManager::querySegments(const std::string& stream, int64_t start_ms, int64_t end_ms) { std::vector result; if (!index_.count(stream)) return result; const auto& files = index_[stream]; std::vector hits; // 先找到落在时间区间内的文件 for (const auto& f : files) { bool overlap = !(f.end_ms <= start_ms || f.start_ms >= end_ms); if (overlap) hits.push_back(f); } if (hits.empty()) return result; // 合并连续文件为段 std::sort(hits.begin(), hits.end(), [](auto& a, auto& b) { return a.start_ms < b.start_ms; }); int idx = 1; RecordSegment seg{}; seg.index = idx; seg.start_ms = hits[0].start_ms; seg.files.push_back(hits[0]); 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); for (size_t i = 1; i < hits.size(); ++i) { auto& prev = hits[i - 1]; auto& cur = hits[i]; bool continuous = (cur.start_ms <= prev.end_ms + 1500); // 允许 1.5 秒误差 if (continuous) { seg.files.push_back(cur); seg.end_ms = cur.end_ms; } else { // 关闭上一段 seg.segment_id = seg.files[0].stream + "_" + std::to_string(seg.start_ms) + "_" + std::to_string(seg.end_ms); result.push_back(seg); // 开新段 idx++; seg = RecordSegment{}; seg.index = idx; seg.files.push_back(cur); seg.start_ms = cur.start_ms; seg.end_ms = cur.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); return result; } // // 根据 segmentId 反查 segment // RecordSegment RecordManager::getSegment(const std::string& segmentId) { // segmentId 格式: // AHD1_main_1731465600000_1731467400000 auto pos1 = segmentId.find('_'); auto pos2 = segmentId.find('_', pos1 + 1); if (pos1 == std::string::npos || pos2 == std::string::npos) return {}; std::string stream = segmentId.substr(0, pos1); int64_t start_ms = std::stoll(segmentId.substr(pos1 + 1, pos2 - pos1 - 1)); int64_t end_ms = std::stoll(segmentId.substr(pos2 + 1)); auto segs = querySegments(stream, start_ms, end_ms); for (auto& s : segs) { if (s.start_ms == start_ms && s.end_ms == end_ms) return s; } return {}; } void RecordManager::dumpIndex() const { std::cout << "\n=== dumpIndex() ===\n"; for (const auto& kv : index_) { const auto& stream = kv.first; const auto& files = kv.second; std::cout << "\n>>> Stream = " << stream << ", file_count = " << files.size() << "\n"; for (const auto& f : files) { std::cout << " - " << f.path << "\n start_ms=" << f.start_ms << ", end_ms=" << f.end_ms << "\n"; } } }