mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-27 15:45:55 -05:00
Populate MoonfireDatabase::CameraData::days.
Also expose it via GetCameraRow.
This commit is contained in:
parent
28fa458982
commit
1d55567384
@ -101,14 +101,18 @@ class MoonfireDbTest : public testing::Test {
|
|||||||
EXPECT_EQ("/main", row.main_rtsp_path);
|
EXPECT_EQ("/main", row.main_rtsp_path);
|
||||||
EXPECT_EQ("/sub", row.sub_rtsp_path);
|
EXPECT_EQ("/sub", row.sub_rtsp_path);
|
||||||
EXPECT_EQ(42, row.retain_bytes);
|
EXPECT_EQ(42, row.retain_bytes);
|
||||||
EXPECT_EQ(-1, row.min_start_time_90k);
|
EXPECT_EQ(std::numeric_limits<int64_t>::max(), row.min_start_time_90k);
|
||||||
EXPECT_EQ(-1, row.max_end_time_90k);
|
EXPECT_EQ(std::numeric_limits<int64_t>::min(), row.max_end_time_90k);
|
||||||
EXPECT_EQ(0, row.total_duration_90k);
|
EXPECT_EQ(0, row.total_duration_90k);
|
||||||
EXPECT_EQ(0, row.total_sample_file_bytes);
|
EXPECT_EQ(0, row.total_sample_file_bytes);
|
||||||
return IterationControl::kContinue;
|
return IterationControl::kContinue;
|
||||||
});
|
});
|
||||||
EXPECT_EQ(1, rows);
|
EXPECT_EQ(1, rows);
|
||||||
|
|
||||||
|
GetCameraRow row;
|
||||||
|
EXPECT_TRUE(mdb_->GetCamera(camera_uuid, &row));
|
||||||
|
EXPECT_THAT(row.days, testing::ElementsAre());
|
||||||
|
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
rows = 0;
|
rows = 0;
|
||||||
EXPECT_TRUE(mdb_->ListCameraRecordings(
|
EXPECT_TRUE(mdb_->ListCameraRecordings(
|
||||||
@ -150,6 +154,13 @@ class MoonfireDbTest : public testing::Test {
|
|||||||
});
|
});
|
||||||
EXPECT_EQ(1, rows);
|
EXPECT_EQ(1, rows);
|
||||||
|
|
||||||
|
std::map<std::string, int64_t> expected_days;
|
||||||
|
internal::AdjustDaysMap(recording.start_time_90k, recording.end_time_90k, 1,
|
||||||
|
&expected_days);
|
||||||
|
GetCameraRow row;
|
||||||
|
EXPECT_TRUE(mdb_->GetCamera(camera_uuid, &row));
|
||||||
|
EXPECT_THAT(row.days, testing::Eq(expected_days));
|
||||||
|
|
||||||
GetCameraRow camera_row;
|
GetCameraRow camera_row;
|
||||||
EXPECT_TRUE(mdb_->GetCamera(camera_uuid, &camera_row));
|
EXPECT_TRUE(mdb_->GetCamera(camera_uuid, &camera_row));
|
||||||
EXPECT_EQ(recording.start_time_90k, camera_row.min_start_time_90k);
|
EXPECT_EQ(recording.start_time_90k, camera_row.min_start_time_90k);
|
||||||
|
@ -73,10 +73,13 @@ void AdjustDaysMap(int64_t start_time_90k, int64_t end_time_90k, int sign,
|
|||||||
std::map<std::string, int64_t> *days) {
|
std::map<std::string, int64_t> *days) {
|
||||||
// There will always be at most two days adjusted, because
|
// There will always be at most two days adjusted, because
|
||||||
// kMaxRecordingDuration is less than a day (even during spring forward).
|
// kMaxRecordingDuration is less than a day (even during spring forward).
|
||||||
DCHECK_LT(start_time_90k, end_time_90k);
|
DCHECK_LE(start_time_90k, end_time_90k);
|
||||||
DCHECK_LE(end_time_90k - start_time_90k, kMaxRecordingDuration);
|
DCHECK_LE(end_time_90k - start_time_90k, kMaxRecordingDuration);
|
||||||
static_assert(kMaxRecordingDuration <= 23 * 60 * kTimeUnitsPerSecond,
|
static_assert(kMaxRecordingDuration <= 23 * 60 * kTimeUnitsPerSecond,
|
||||||
"max duration should be less than a (spring-forward) day");
|
"max duration should be less than a (spring-forward) day");
|
||||||
|
if (start_time_90k == end_time_90k) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Fill |buf| with the first day.
|
// Fill |buf| with the first day.
|
||||||
struct tm mytm;
|
struct tm mytm;
|
||||||
@ -150,8 +153,6 @@ bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
|||||||
{
|
{
|
||||||
DatabaseContext ctx(db_);
|
DatabaseContext ctx(db_);
|
||||||
|
|
||||||
// This query scans the entirety of the recording table's index.
|
|
||||||
// It is quite slow, so the results are cached.
|
|
||||||
auto list_cameras_run = ctx.UseOnce(
|
auto list_cameras_run = ctx.UseOnce(
|
||||||
R"(
|
R"(
|
||||||
select
|
select
|
||||||
@ -164,20 +165,9 @@ bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
|||||||
camera.password,
|
camera.password,
|
||||||
camera.main_rtsp_path,
|
camera.main_rtsp_path,
|
||||||
camera.sub_rtsp_path,
|
camera.sub_rtsp_path,
|
||||||
camera.retain_bytes,
|
camera.retain_bytes
|
||||||
min(recording.start_time_90k),
|
|
||||||
max(recording.start_time_90k + recording.duration_90k),
|
|
||||||
sum(recording.duration_90k),
|
|
||||||
sum(recording.sample_file_bytes)
|
|
||||||
from
|
from
|
||||||
camera
|
camera;
|
||||||
left join recording on (camera.id = recording.camera_id)
|
|
||||||
group by
|
|
||||||
camera.id,
|
|
||||||
camera.uuid,
|
|
||||||
camera.short_name,
|
|
||||||
camera.description,
|
|
||||||
camera.retain_bytes;
|
|
||||||
)");
|
)");
|
||||||
while (list_cameras_run.Step() == SQLITE_ROW) {
|
while (list_cameras_run.Step() == SQLITE_ROW) {
|
||||||
CameraData data;
|
CameraData data;
|
||||||
@ -197,14 +187,6 @@ bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
|||||||
data.main_rtsp_path = list_cameras_run.ColumnText(7).as_string();
|
data.main_rtsp_path = list_cameras_run.ColumnText(7).as_string();
|
||||||
data.sub_rtsp_path = list_cameras_run.ColumnText(8).as_string();
|
data.sub_rtsp_path = list_cameras_run.ColumnText(8).as_string();
|
||||||
data.retain_bytes = list_cameras_run.ColumnInt64(9);
|
data.retain_bytes = list_cameras_run.ColumnInt64(9);
|
||||||
data.min_start_time_90k = list_cameras_run.ColumnType(10) == SQLITE_NULL
|
|
||||||
? -1
|
|
||||||
: list_cameras_run.ColumnInt64(10);
|
|
||||||
data.max_end_time_90k = list_cameras_run.ColumnType(11) == SQLITE_NULL
|
|
||||||
? -1
|
|
||||||
: list_cameras_run.ColumnInt64(11);
|
|
||||||
data.total_duration_90k = list_cameras_run.ColumnInt64(12);
|
|
||||||
data.total_sample_file_bytes = list_cameras_run.ColumnInt64(13);
|
|
||||||
|
|
||||||
auto ret = cameras_by_uuid_.insert(std::make_pair(uuid, data));
|
auto ret = cameras_by_uuid_.insert(std::make_pair(uuid, data));
|
||||||
if (!ret.second) {
|
if (!ret.second) {
|
||||||
@ -220,6 +202,45 @@ bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
|||||||
if (list_cameras_run.status() != SQLITE_DONE) {
|
if (list_cameras_run.status() != SQLITE_DONE) {
|
||||||
*error_message = StrCat("Camera list query failed: ",
|
*error_message = StrCat("Camera list query failed: ",
|
||||||
list_cameras_run.error_message());
|
list_cameras_run.error_message());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This query scans the entirety of the recording table's index.
|
||||||
|
// It is quite slow, so the results are cached.
|
||||||
|
auto list_recordings_run = ctx.UseOnce(
|
||||||
|
R"(
|
||||||
|
select
|
||||||
|
recording.start_time_90k,
|
||||||
|
recording.duration_90k,
|
||||||
|
recording.sample_file_bytes,
|
||||||
|
recording.camera_id
|
||||||
|
from
|
||||||
|
recording
|
||||||
|
)");
|
||||||
|
while (list_recordings_run.Step() == SQLITE_ROW) {
|
||||||
|
int64_t start_time_90k = list_recordings_run.ColumnInt64(0);
|
||||||
|
int64_t duration_90k = list_recordings_run.ColumnInt64(1);
|
||||||
|
int64_t end_time_90k = start_time_90k + duration_90k;
|
||||||
|
int64_t sample_file_bytes = list_recordings_run.ColumnInt64(2);
|
||||||
|
int64_t camera_id = list_recordings_run.ColumnInt64(3);
|
||||||
|
auto it = cameras_by_id_.find(camera_id);
|
||||||
|
if (it == cameras_by_id_.end()) {
|
||||||
|
*error_message =
|
||||||
|
StrCat("Recording refers to unknown camera ", camera_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CameraData *data = it->second;
|
||||||
|
data->min_start_time_90k =
|
||||||
|
std::min(data->min_start_time_90k, start_time_90k);
|
||||||
|
data->max_end_time_90k = std::max(data->max_end_time_90k, end_time_90k);
|
||||||
|
data->total_sample_file_bytes += sample_file_bytes;
|
||||||
|
data->total_duration_90k += duration_90k;
|
||||||
|
internal::AdjustDaysMap(start_time_90k, end_time_90k, 1, &data->days);
|
||||||
|
}
|
||||||
|
if (list_cameras_run.status() != SQLITE_DONE) {
|
||||||
|
*error_message = StrCat("Recording list query failed: ",
|
||||||
|
list_recordings_run.error_message());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's simplest to just keep the video sample entries in RAM.
|
// It's simplest to just keep the video sample entries in RAM.
|
||||||
@ -358,6 +379,7 @@ bool MoonfireDatabase::Init(Database *db, std::string *error_message) {
|
|||||||
select
|
select
|
||||||
id,
|
id,
|
||||||
sample_file_uuid,
|
sample_file_uuid,
|
||||||
|
start_time_90k,
|
||||||
duration_90k,
|
duration_90k,
|
||||||
sample_file_bytes
|
sample_file_bytes
|
||||||
from
|
from
|
||||||
@ -453,6 +475,7 @@ bool MoonfireDatabase::GetCamera(Uuid camera_uuid, GetCameraRow *row) {
|
|||||||
row->max_end_time_90k = data.max_end_time_90k;
|
row->max_end_time_90k = data.max_end_time_90k;
|
||||||
row->total_duration_90k = data.total_duration_90k;
|
row->total_duration_90k = data.total_duration_90k;
|
||||||
row->total_sample_file_bytes = data.total_sample_file_bytes;
|
row->total_sample_file_bytes = data.total_sample_file_bytes;
|
||||||
|
row->days = data.days;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,6 +755,8 @@ bool MoonfireDatabase::InsertRecording(Recording *recording,
|
|||||||
camera_data->total_duration_90k +=
|
camera_data->total_duration_90k +=
|
||||||
recording->end_time_90k - recording->start_time_90k;
|
recording->end_time_90k - recording->start_time_90k;
|
||||||
camera_data->total_sample_file_bytes += recording->sample_file_bytes;
|
camera_data->total_sample_file_bytes += recording->sample_file_bytes;
|
||||||
|
internal::AdjustDaysMap(recording->start_time_90k, recording->end_time_90k, 1,
|
||||||
|
&camera_data->days);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,8 +783,9 @@ bool MoonfireDatabase::ListOldestSampleFiles(
|
|||||||
ToHex(run.ColumnBlob(1)));
|
ToHex(run.ColumnBlob(1)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
row.duration_90k = run.ColumnInt64(2);
|
row.start_time_90k = run.ColumnInt64(2);
|
||||||
row.sample_file_bytes = run.ColumnInt64(3);
|
row.duration_90k = run.ColumnInt64(3);
|
||||||
|
row.sample_file_bytes = run.ColumnInt64(4);
|
||||||
if (row_cb(row) == IterationControl::kBreak) {
|
if (row_cb(row) == IterationControl::kBreak) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -787,6 +813,7 @@ bool MoonfireDatabase::DeleteRecordings(
|
|||||||
int64_t deleted_sample_file_bytes = 0;
|
int64_t deleted_sample_file_bytes = 0;
|
||||||
int64_t min_start_time_90k = -1;
|
int64_t min_start_time_90k = -1;
|
||||||
int64_t max_end_time_90k = -1;
|
int64_t max_end_time_90k = -1;
|
||||||
|
std::map<std::string, int64_t> days;
|
||||||
CameraData *camera_data = nullptr;
|
CameraData *camera_data = nullptr;
|
||||||
};
|
};
|
||||||
std::map<int64_t, State> state_by_camera_id;
|
std::map<int64_t, State> state_by_camera_id;
|
||||||
@ -794,6 +821,9 @@ bool MoonfireDatabase::DeleteRecordings(
|
|||||||
State &state = state_by_camera_id[recording.camera_id];
|
State &state = state_by_camera_id[recording.camera_id];
|
||||||
state.deleted_duration_90k += recording.duration_90k;
|
state.deleted_duration_90k += recording.duration_90k;
|
||||||
state.deleted_sample_file_bytes += recording.sample_file_bytes;
|
state.deleted_sample_file_bytes += recording.sample_file_bytes;
|
||||||
|
internal::AdjustDaysMap(recording.start_time_90k,
|
||||||
|
recording.start_time_90k + recording.duration_90k,
|
||||||
|
1, &state.days);
|
||||||
|
|
||||||
auto delete_run = ctx.Borrow(&delete_recording_stmt_);
|
auto delete_run = ctx.Borrow(&delete_recording_stmt_);
|
||||||
delete_run.BindInt64(":recording_id", recording.recording_id);
|
delete_run.BindInt64(":recording_id", recording.recording_id);
|
||||||
@ -839,8 +869,8 @@ bool MoonfireDatabase::DeleteRecordings(
|
|||||||
state.min_start_time_90k = min_run.ColumnInt64(0);
|
state.min_start_time_90k = min_run.ColumnInt64(0);
|
||||||
} else if (min_run.Step() == SQLITE_DONE) {
|
} else if (min_run.Step() == SQLITE_DONE) {
|
||||||
// There are no recordings left.
|
// There are no recordings left.
|
||||||
state.min_start_time_90k = -1;
|
state.min_start_time_90k = std::numeric_limits<int64_t>::max();
|
||||||
state.max_end_time_90k = -1;
|
state.max_end_time_90k = std::numeric_limits<int64_t>::min();
|
||||||
continue; // skip additional query below to calculate max.
|
continue; // skip additional query below to calculate max.
|
||||||
} else {
|
} else {
|
||||||
ctx.RollbackTransaction();
|
ctx.RollbackTransaction();
|
||||||
@ -885,6 +915,9 @@ bool MoonfireDatabase::DeleteRecordings(
|
|||||||
state.deleted_sample_file_bytes;
|
state.deleted_sample_file_bytes;
|
||||||
state.camera_data->min_start_time_90k = state.min_start_time_90k;
|
state.camera_data->min_start_time_90k = state.min_start_time_90k;
|
||||||
state.camera_data->max_end_time_90k = state.max_end_time_90k;
|
state.camera_data->max_end_time_90k = state.max_end_time_90k;
|
||||||
|
for (const auto &day : state.days) {
|
||||||
|
AdjustDay(day.first, -day.second, &state.camera_data->days);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -105,8 +105,7 @@ struct GetCameraRow {
|
|||||||
int64_t max_end_time_90k = -1;
|
int64_t max_end_time_90k = -1;
|
||||||
int64_t total_duration_90k = -1;
|
int64_t total_duration_90k = -1;
|
||||||
int64_t total_sample_file_bytes = -1;
|
int64_t total_sample_file_bytes = -1;
|
||||||
|
std::map<std::string, int64_t> days; // YYYY-mm-dd -> duration_90k.
|
||||||
// TODO: std::vector<std::string> days; // keys: YYYY-mm-dd.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// For use with MoonfireDatabase::ListCameraRecordings.
|
// For use with MoonfireDatabase::ListCameraRecordings.
|
||||||
@ -129,6 +128,7 @@ struct ListOldestSampleFilesRow {
|
|||||||
int64_t camera_id = -1;
|
int64_t camera_id = -1;
|
||||||
int64_t recording_id = -1;
|
int64_t recording_id = -1;
|
||||||
Uuid sample_file_uuid;
|
Uuid sample_file_uuid;
|
||||||
|
int64_t start_time_90k = -1;
|
||||||
int64_t duration_90k = -1;
|
int64_t duration_90k = -1;
|
||||||
int64_t sample_file_bytes = -1;
|
int64_t sample_file_bytes = -1;
|
||||||
};
|
};
|
||||||
@ -221,15 +221,14 @@ class MoonfireDatabase {
|
|||||||
int64_t retain_bytes = -1;
|
int64_t retain_bytes = -1;
|
||||||
|
|
||||||
// Aggregates of all recordings associated with the camera.
|
// Aggregates of all recordings associated with the camera.
|
||||||
int64_t min_start_time_90k = -1;
|
int64_t min_start_time_90k = std::numeric_limits<int64_t>::max();
|
||||||
int64_t max_end_time_90k = -1;
|
int64_t max_end_time_90k = std::numeric_limits<int64_t>::min();
|
||||||
int64_t total_sample_file_bytes = -1;
|
int64_t total_sample_file_bytes = 0;
|
||||||
int64_t total_duration_90k = -1;
|
int64_t total_duration_90k = 0;
|
||||||
|
|
||||||
// A map of calendar days (in the local timezone, "YYYY-mm-DD") to the
|
// A map of calendar days (in the local timezone, "YYYY-mm-DD") to the
|
||||||
// total duration (in 90k units) of recorded data in the day. A day is
|
// total duration (in 90k units) of recorded data in the day. A day is
|
||||||
// present in the map ff the value is non-zero.
|
// present in the map ff the value is non-zero.
|
||||||
// TODO: actually fill this.
|
|
||||||
std::map<std::string, int64_t> days;
|
std::map<std::string, int64_t> days;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user