录像空间管理

This commit is contained in:
cxh 2025-11-26 09:13:11 +08:00
parent 4705890d04
commit 9cff02f30c
2 changed files with 158 additions and 0 deletions

View File

@ -60,6 +60,10 @@ class RecordManager
bool loadSrsConfig(); // 解析配置文件,填充 record_dir_ 和 dvr_duration_sec_ bool loadSrsConfig(); // 解析配置文件,填充 record_dir_ 和 dvr_duration_sec_
void removeExpiredDays();
void removeOldestHoursUntilSafe();
void cleanupStorage();
// === 文件列表索引 === // === 文件列表索引 ===
std::unordered_map<std::string, std::vector<RecordFileInfo>> index_; std::unordered_map<std::string, std::vector<RecordFileInfo>> index_;
std::mutex index_mutex_; std::mutex index_mutex_;
@ -76,6 +80,10 @@ class RecordManager
std::thread scan_thread_; std::thread scan_thread_;
std::atomic<bool> running_{false}; std::atomic<bool> running_{false};
int scan_interval_sec_ = 60; int scan_interval_sec_ = 60;
// ==== Storage management config ====
int retention_days_ = 14; // 保留最近 N 天
double usage_threshold_ = 0.90; // 超过 90% 就进行空间清理
}; };
extern std::shared_ptr<RecordManager> g_record_manager; extern std::shared_ptr<RecordManager> g_record_manager;

View File

@ -1,5 +1,7 @@
#include "record_manager.hpp" #include "record_manager.hpp"
#include <sys/statvfs.h>
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
#include <fstream> #include <fstream>
@ -123,6 +125,153 @@ bool RecordManager::loadSrsConfig()
return true; return true;
} }
void RecordManager::removeExpiredDays()
{
namespace fs = std::filesystem;
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
for (auto& streamEntry : fs::directory_iterator(record_dir_))
{
if (!streamEntry.is_directory()) continue;
for (auto& dayEntry : fs::directory_iterator(streamEntry))
{
if (!dayEntry.is_directory()) continue;
std::string dayName = dayEntry.path().filename().string(); // 2025-11-14
std::tm tm{};
if (sscanf(dayName.c_str(), "%d-%d-%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) continue;
tm.tm_year -= 1900;
tm.tm_mon -= 1;
time_t dayTime = mktime(&tm);
double daysOld = difftime(now, dayTime) / 86400.0;
if (daysOld > retention_days_)
{
LOG_WARN("[RecordManager] Removing expired day: " + dayEntry.path().string());
fs::remove_all(dayEntry.path());
}
}
}
}
void RecordManager::removeOldestHoursUntilSafe()
{
namespace fs = std::filesystem;
while (true)
{
// 1) 检查磁盘
struct statvfs vfs{};
if (statvfs(record_dir_.c_str(), &vfs) != 0) return;
double used = 1.0 - (double)vfs.f_bavail / (double)vfs.f_blocks;
if (used < usage_threshold_) break;
// 2) 找全盘最旧的 day
fs::path oldestDay;
time_t oldestTime = 0;
for (auto& streamDir : fs::directory_iterator(record_dir_))
{
if (!streamDir.is_directory()) continue;
for (auto& dayDir : fs::directory_iterator(streamDir))
{
if (!dayDir.is_directory()) continue;
std::string dayName = dayDir.path().filename().string();
std::tm tm{};
if (sscanf(dayName.c_str(), "%d-%d-%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3) continue;
tm.tm_year -= 1900;
tm.tm_mon -= 1;
time_t t = mktime(&tm);
if (oldestDay.empty() || t < oldestTime)
{
oldestTime = t;
oldestDay = dayDir.path();
}
}
}
if (oldestDay.empty())
{
LOG_WARN("[RecordManager] No day folder found for hour cleanup.");
break;
}
// ---- 找该 day 下最旧的 hour ----
fs::path oldestHour;
int oldestHourValue = -1;
for (auto& hourDir : fs::directory_iterator(oldestDay))
{
if (!hourDir.is_directory()) continue;
std::string hourName = hourDir.path().filename().string();
int h = -1;
if (sscanf(hourName.c_str(), "%d", &h) != 1) continue;
if (oldestHourValue == -1 || h < oldestHourValue)
{
oldestHourValue = h;
oldestHour = hourDir.path();
}
}
if (oldestHour.empty())
{
LOG_WARN("[RecordManager] Day empty, remove whole day: " + oldestDay.string());
fs::remove_all(oldestDay);
continue;
}
// ---- 删除最旧 hour ----
LOG_WARN("[RecordManager] Removing oldest hour: " + oldestHour.string());
fs::remove_all(oldestHour);
// 如果此 day 已空 → 删除 day 目录
if (fs::is_empty(oldestDay))
{
LOG_WARN("[RecordManager] Removing empty day: " + oldestDay.string());
fs::remove_all(oldestDay);
}
}
}
void RecordManager::cleanupStorage()
{
struct statvfs vfs{};
if (statvfs(record_dir_.c_str(), &vfs) != 0)
{
LOG_ERROR("[RecordManager] statvfs failed for " + record_dir_);
return;
}
double used = 1.0 - (double)vfs.f_bavail / (double)vfs.f_blocks;
// 1) 删除超期天
removeExpiredDays();
// 重新检查磁盘
if (statvfs(record_dir_.c_str(), &vfs) == 0)
{
used = 1.0 - (double)vfs.f_bavail / (double)vfs.f_blocks;
}
// 2) 使用率仍超 → 按小时清理
if (used >= usage_threshold_)
{
removeOldestHoursUntilSafe();
}
}
void RecordManager::startAutoScan(int interval_sec) void RecordManager::startAutoScan(int interval_sec)
{ {
scan_interval_sec_ = interval_sec; scan_interval_sec_ = interval_sec;
@ -141,6 +290,7 @@ void RecordManager::startAutoScan(int interval_sec)
auto t0 = std::chrono::steady_clock::now(); auto t0 = std::chrono::steady_clock::now();
this->scanAll(); this->scanAll();
this->cleanupStorage();
LOG_INFO("[RecordManager] scanAll() completed."); LOG_INFO("[RecordManager] scanAll() completed.");
// 休眠剩余时间(支持快速退出) // 休眠剩余时间(支持快速退出)