forked from dsutanto/bytetrack-counter-cpp
Save video under batch counting date; fix retention timezone
This commit is contained in:
1 parent
a94c3dff87
commit
37f98f243c
9 files changed
+37
-19
No files matched your search
@@ -53,3 +53,17 @@ touch /dev/shm/bytetrack-counter/.reset
|
||||
- `.reset` trigger file
|
||||
|
||||
Directory auto-created at startup.
|
||||
|
||||
## Video recordings
|
||||
|
||||
- Recordings are written to `SHM_DIR` as `batch_pending_*.mp4`, confirmed when ayam detected, and moved via `fork()` (non-blocking) to `OUTPUT_DIR/<batch counting_date>/batch_N_HHMMSS.mp4`.
|
||||
- The day folder uses the **batch counting date** (today before `DAILY_CUTOFF_TIME`, tomorrow after), forwarded from `BatchStore` through the batch-closed callback — matching the SQLite `counting_date`.
|
||||
- Retention cleanup compares day folders with `std::localtime` to match folder naming.
|
||||
|
||||
## Source override
|
||||
|
||||
```bash
|
||||
bytetrack-counter <config.env> --source /tmp/test.mp4 # local file (single pass)
|
||||
bytetrack-counter <config.env> --source rtsp://cam:554/x # live stream
|
||||
bytetrack-counter <config.env> -s path # or --source=path
|
||||
```
|
||||
@@ -23,13 +23,13 @@ env: .env KEY=VALUE file — all config per config.env.example
|
||||
env: SHM_DIR → shared memory directory (/dev/shm/bytetrack-counter)
|
||||
env: LIVE_STREAM_FRAME_PATH → JPEG snapshot path
|
||||
env: RECORD_VIDEO, RECORD_DELAY_SECONDS, RECORD_END_DELAY, RECORD_RETENTION_DAYS
|
||||
cmd: bytetrack-counter <config.env>
|
||||
cmd: bytetrack-counter <config.env> [--source <rtsp://|http://|/path/to/file.mp4>]
|
||||
db: SQLite batches(id,counting_date,batch_number,camera_name,object_label,count,start_time,end_time)
|
||||
db: SQLite daily_summaries(id,counting_date,camera_name,object_label,total_count,total_batches)
|
||||
file: JSON state — batch_number,count,start_time,last_detection_time,counted_event_ids[]
|
||||
file: CSV crossing log — batch,frame,timestamp,chicken_id
|
||||
file: SHM_DIR/.reset → trigger full state reset
|
||||
file: SHM_DIR/batch_N_HHMMSS.mp4 → per-batch video recording (moved to OUTPUT_DIR/YYYY-MM-DD/)
|
||||
file: SHM_DIR/batch_N_HHMMSS.mp4 → per-batch video recording (moved to OUTPUT_DIR/<batch counting_date>/)
|
||||
sig: SIGTERM|SIGINT → shutdown_requested → graceful exit
|
||||
```
|
||||
|
||||
@@ -48,8 +48,8 @@ V9: live_stream_enabled → parent dir of LIVE_STREAM_FRAME_PATH created @ star
|
||||
V10: stale track (> TRACKED_PRUNE_SEC unseen) → prune from external tracking maps
|
||||
V11: all dirs (output_dir, shm_dir, live_stream parent) created @ startup
|
||||
V12: SHM_DIR/.reset → BatchStore::reset() — end active batch, clear memory, delete state_file, write blank live JPEG
|
||||
V13: video recording: motion → SPECULATING (≤RECORD_DELAY_SECONDS), ayam → CONFIRMED → talenan/batch-end → STOPPING (+RECORD_END_DELAY) → move to OUTPUT_DIR/YYYY-MM-DD/
|
||||
V14: video retention: day-dirs > RECORD_RETENTION_DAYS old → removed @ startup & @ each CUTOFF_TIME
|
||||
V13: video recording: motion → SPECULATING (≤RECORD_DELAY_SECONDS), ayam → CONFIRMED → talenan/batch-end → STOPPING (+RECORD_END_DELAY) → move to OUTPUT_DIR/<batch counting_date>/ via fork
|
||||
V14: video retention: day-dirs > RECORD_RETENTION_DAYS old → removed @ startup & @ each CUTOFF_TIME (localtime cutoff matches folder names)
|
||||
```
|
||||
|
||||
## §T — Tasks
|
||||
@@ -71,8 +71,9 @@ T12|x|CsvLogger for crossing events|I.file
|
||||
T13|x|Live JPEG snapshot to shared memory|V9
|
||||
T14|x|systemd service with ProtectSystem + ReadWritePaths|I.cmd
|
||||
T15|x|Graceful shutdown: SIGTERM → close batch → save state|I.sig
|
||||
T16|x|Video per-batch recording: motion-triggered → speculating in shm, confirmed when ayam detected ≤ RECORD_DELAY_SECONDS, stopped RECORD_END_DELAY after batch close, moved to OUTPUT_DIR/YYYY-MM-DD/ via fork, retention cleanup at startup & cutoff. Unconfirmed recordings deleted.|V13,V14
|
||||
T16|x|Video per-batch recording: motion-triggered → speculating in shm, confirmed when ayam detected ≤ RECORD_DELAY_SECONDS, stopped RECORD_END_DELAY after batch close, moved to OUTPUT_DIR/<batch counting_date>/ via fork, retention cleanup at startup & cutoff. Unconfirmed recordings deleted.|V13,V14
|
||||
T17|~?|Python DEBUG_TRACKING equivalent (per-frame score/track dump)|-
|
||||
T18|x|CLI --source/-s/--source= override of SOURCE (rtsp|http|local mp4; file = single pass)|I.cmd
|
||||
```
|
||||
|
||||
## §B — Bugs
|
||||
|
||||
Binary file not shown.
+2
-2
@@ -4,11 +4,11 @@
|
||||
|
||||
namespace version {
|
||||
|
||||
const std::string GIT_COMMIT = "eefff489";
|
||||
const std::string GIT_COMMIT = "a94c3dff";
|
||||
const std::string GIT_BRANCH = "main";
|
||||
const std::string GIT_TAG = "";
|
||||
const std::string BUILD_DATE = "2026-08-04";
|
||||
const std::string BUILD_TIME = "23:35:10";
|
||||
const std::string BUILD_TIME = "23:55:47";
|
||||
const int BUILD_YEAR = 2026;
|
||||
const bool BUILD_RKNN = 1;
|
||||
|
||||
|
||||
+4
-3
@@ -328,12 +328,13 @@ bool BatchStore::end_batch_locked(const std::string& closed_by) {
|
||||
|
||||
int count = state["count"].get<int>();
|
||||
int batch_number = state["batch_number"].get<int>();
|
||||
std::string counting_date = state.value("counting_date", "");
|
||||
|
||||
if (count < min_object_per_batch_ || duration_sec < min_duration_per_batch_) {
|
||||
current_state_ = nullptr;
|
||||
save_state();
|
||||
batch_timer_running_ = false;
|
||||
if (batch_closed_cb_) batch_closed_cb_(batch_number);
|
||||
if (batch_closed_cb_) batch_closed_cb_(batch_number, counting_date);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -348,7 +349,7 @@ bool BatchStore::end_batch_locked(const std::string& closed_by) {
|
||||
current_state_ = nullptr;
|
||||
save_state();
|
||||
batch_timer_running_ = false;
|
||||
if (batch_closed_cb_) batch_closed_cb_(batch_number);
|
||||
if (batch_closed_cb_) batch_closed_cb_(batch_number, counting_date);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -409,7 +410,7 @@ int BatchStore::display_total() {
|
||||
return get_closed_total_for_day(get_counting_date()) + current_batch_count();
|
||||
}
|
||||
|
||||
void BatchStore::set_batch_closed_callback(std::function<void(int)> cb) {
|
||||
void BatchStore::set_batch_closed_callback(std::function<void(int, const std::string&)> cb) {
|
||||
batch_closed_cb_ = std::move(cb);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ public:
|
||||
int current_batch_count() const;
|
||||
int display_total();
|
||||
|
||||
void set_batch_closed_callback(std::function<void(int)> cb);
|
||||
void set_batch_closed_callback(std::function<void(int, const std::string&)> cb);
|
||||
void reset();
|
||||
|
||||
bool check_cutoff_reset();
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
int min_duration_per_batch_;
|
||||
int carry_ids_ = 50;
|
||||
std::function<void(const std::string&)> logger_;
|
||||
std::function<void(int)> batch_closed_cb_;
|
||||
std::function<void(int, const std::string&)> batch_closed_cb_;
|
||||
|
||||
sqlite3* db_ = nullptr;
|
||||
std::mutex state_mutex_;
|
||||
|
||||
+2
-2
@@ -266,7 +266,7 @@ int main(int argc, char* argv[]) {
|
||||
g_config.record_end_delay_seconds,
|
||||
g_config.record_retention_days);
|
||||
auto* vw = video_writer.get();
|
||||
store.set_batch_closed_callback([vw](int bn) { vw->on_batch_closed(bn); });
|
||||
store.set_batch_closed_callback([vw](int bn, const std::string& date) { vw->on_batch_closed(bn, date); });
|
||||
}
|
||||
|
||||
int reconnect_count = 0;
|
||||
@@ -617,8 +617,8 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
cap.release();
|
||||
if (video_writer) video_writer->shutdown();
|
||||
store.shutdown();
|
||||
if (video_writer) video_writer->shutdown();
|
||||
|
||||
std::cout << "\n=== Batch Summary (SQLite) ===" << std::endl;
|
||||
std::cout << "Database: " << g_config.db_path << std::endl;
|
||||
|
||||
@@ -41,7 +41,7 @@ void VideoBatchWriter::cleanup_retention() {
|
||||
auto cutoff = std::chrono::system_clock::now() - std::chrono::hours(retention_days_ * 24);
|
||||
auto cutoff_t = std::chrono::system_clock::to_time_t(cutoff);
|
||||
std::ostringstream cutoff_ss;
|
||||
cutoff_ss << std::put_time(std::gmtime(&cutoff_t), "%Y-%m-%d");
|
||||
cutoff_ss << std::put_time(std::localtime(&cutoff_t), "%Y-%m-%d");
|
||||
std::string cutoff_str = cutoff_ss.str();
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(output_dir_)) {
|
||||
@@ -86,8 +86,9 @@ void VideoBatchWriter::note_ayam_detection() {
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBatchWriter::on_batch_closed(int batch_number) {
|
||||
void VideoBatchWriter::on_batch_closed(int batch_number, const std::string& counting_date) {
|
||||
current_batch_ = batch_number;
|
||||
current_date_ = counting_date;
|
||||
if (state_ == CONFIRMED) {
|
||||
state_ = STOPPING;
|
||||
state_start_ = std::chrono::steady_clock::now();
|
||||
@@ -175,7 +176,7 @@ void VideoBatchWriter::stop_and_move(int batch_number) {
|
||||
std::string src = current_path_;
|
||||
current_path_.clear();
|
||||
|
||||
std::string day_dir = output_dir_ + "/" + today_str();
|
||||
std::string day_dir = output_dir_ + "/" + (current_date_.empty() ? today_str() : current_date_);
|
||||
ensure_dir(day_dir);
|
||||
|
||||
std::ostringstream name;
|
||||
@@ -205,7 +206,7 @@ void VideoBatchWriter::shutdown() {
|
||||
current_path_.clear();
|
||||
|
||||
if (prev_state == CONFIRMED || prev_state == STOPPING) {
|
||||
std::string day_dir = output_dir_ + "/" + today_str();
|
||||
std::string day_dir = output_dir_ + "/" + (current_date_.empty() ? today_str() : current_date_);
|
||||
ensure_dir(day_dir);
|
||||
std::string dst = day_dir + "/" +
|
||||
fs::path(src).filename().string();
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
|
||||
void note_motion();
|
||||
void note_ayam_detection();
|
||||
void on_batch_closed(int batch_number);
|
||||
void on_batch_closed(int batch_number, const std::string& counting_date);
|
||||
void feed_frame(const cv::Mat& raw);
|
||||
void cleanup_retention();
|
||||
void shutdown();
|
||||
@@ -41,4 +41,5 @@ private:
|
||||
cv::VideoWriter writer_;
|
||||
std::string current_path_;
|
||||
int current_batch_ = 0;
|
||||
std::string current_date_;
|
||||
};
|
||||
Reference in new issue
Block a user