录像空间管理
This commit is contained in:
parent
4705890d04
commit
9cff02f30c
@ -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;
|
||||||
|
|||||||
@ -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.");
|
||||||
|
|
||||||
// 休眠剩余时间(支持快速退出)
|
// 休眠剩余时间(支持快速退出)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user