forked from dsutanto/bytetrack-counter-cpp
File sources record from frame 0; feed every frame (fix 10s offset + detection gaps)
This commit is contained in:
1 parent
32921ea3f3
commit
ea370b0ffd
7 files changed
+25
-13
No files matched your search
@@ -57,6 +57,9 @@ 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_<num>_<YYYYMMDD>_<HHMMSS>.mp4`.
|
||||
- **Live sources**: recording speculates (SPECULATING) and is discarded after `RECORD_DELAY_SECONDS` if no ayam confirms; only CONFIRMED/STOPPING recordings are saved.
|
||||
- **File sources** (`file_source_` flag): recording opens at frame 0, is **never discarded** by the SPECULATING timer, and any active recording is saved on batch close and on shutdown — so the output's first frame matches the source mp4's frame 0 (no `RECORD_DELAY` offset).
|
||||
- `feed_frame` is called **every frame** (outside the detections block), so recordings are continuous with no gaps on detection-less frames.
|
||||
- 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.
|
||||
- H.264 encode quality is set via `RECORD_CRF` (lower = better, default unset) and `RECORD_PRESET` — `start_recording()` writes `OPENCV_FFMPEG_WRITE_OPTIONS` (`crf;<n>|preset;<p>`) before `writer_.open()`.
|
||||
|
||||
@@ -49,10 +49,11 @@ 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/<batch counting_date>/ via fork
|
||||
V13: video recording: motion → SPECULATING (≤RECORD_DELAY_SECONDS, live only; file sources never discard) → CONFIRMED → talenan/batch-end → STOPPING (+RECORD_END_DELAY) → move to OUTPUT_DIR/<batch counting_date>/ via fork. feed_frame called every frame (no detection gaps)
|
||||
V14: video retention: day-dirs > RECORD_RETENTION_DAYS old → removed @ startup & @ each CUTOFF_TIME (localtime cutoff matches folder names)
|
||||
V15: RECORD_CRF (lower = better, -1 unset) & RECORD_PRESET → OPENCV_FFMPEG_WRITE_OPTIONS before writer_.open() (crf;<n>|preset;<p>)
|
||||
V16: pause_on_first_frame (env/--pause-on-first-frame, file source only; live ignored w/ warn): process frame 0 → idle-loop, re-write annotated frame to LIVE_STREAM_FRAME_PATH, wait for SHM_DIR/.continue → delete → resume
|
||||
V17: file sources (non-live) record from frame 0: SPECULATING never discarded, any active recording saved on batch close and shutdown → output first frame = source frame 0 (no RECORD_DELAY offset)
|
||||
```
|
||||
|
||||
## §T — Tasks
|
||||
@@ -74,7 +75,7 @@ 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/<batch counting_date>/ via fork, retention cleanup at startup & cutoff, H.264 quality via RECORD_CRF/RECORD_PRESET (OPENCV_FFMPEG_WRITE_OPTIONS). Unconfirmed recordings deleted.|V13,V14,V15
|
||||
T16|x|Video per-batch recording: motion-triggered → speculating in shm (live only, discarded after RECORD_DELAY_SECONDS unconfirmed), confirmed when ayam detected, stopped RECORD_END_DELAY after batch close, moved to OUTPUT_DIR/<batch counting_date>/ via fork, retention cleanup at startup & cutoff, H.264 quality via RECORD_CRF/RECORD_PRESET (OPENCV_FFMPEG_WRITE_OPTIONS). Every frame fed (no detection gaps). File sources: record from frame 0, never discard, save on close & shutdown.|V13,V14,V15,V17
|
||||
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
|
||||
T19|x|Pause-on-first-frame for file source: process frame 0, hold annotated frame as live view, wait for SHM_DIR/.continue, resume. Env PAUSE_ON_FIRST_FRAME + --pause-on-first-frame; stale .continue cleared @ startup; live source ignored w/ warning.|V16
|
||||
|
||||
Binary file not shown.
+2
-2
@@ -4,11 +4,11 @@
|
||||
|
||||
namespace version {
|
||||
|
||||
const std::string GIT_COMMIT = "139cff74";
|
||||
const std::string GIT_COMMIT = "32921ea3";
|
||||
const std::string GIT_BRANCH = "main";
|
||||
const std::string GIT_TAG = "";
|
||||
const std::string BUILD_DATE = "2026-08-10";
|
||||
const std::string BUILD_TIME = "10:11:04";
|
||||
const std::string BUILD_TIME = "10:51:48";
|
||||
const int BUILD_YEAR = 2026;
|
||||
const bool BUILD_RKNN = 1;
|
||||
|
||||
|
||||
+4
-3
@@ -282,7 +282,8 @@ int main(int argc, char* argv[]) {
|
||||
g_config.record_end_delay_seconds,
|
||||
g_config.record_retention_days,
|
||||
g_config.record_crf,
|
||||
g_config.record_preset);
|
||||
g_config.record_preset,
|
||||
!is_live);
|
||||
auto* vw = video_writer.get();
|
||||
store.set_batch_closed_callback([vw](int bn, const std::string& date) { vw->on_batch_closed(bn, date); });
|
||||
}
|
||||
@@ -377,6 +378,8 @@ int main(int argc, char* argv[]) {
|
||||
if (video_writer) video_writer->note_motion();
|
||||
}
|
||||
|
||||
if (video_writer) video_writer->feed_frame(frame);
|
||||
|
||||
std::vector<Detection> detections;
|
||||
float inf_ms = 0.0f;
|
||||
if (!skip_inference) {
|
||||
@@ -549,8 +552,6 @@ int main(int argc, char* argv[]) {
|
||||
ayam_tracked[tid] = {cx, mono};
|
||||
}
|
||||
|
||||
if (video_writer) video_writer->feed_frame(frame);
|
||||
|
||||
// Draw talenan
|
||||
for (int di = 0; di < nt; ++di) {
|
||||
auto it = talenan_tr.det_to_track.find(di);
|
||||
|
||||
+10
-5
@@ -18,11 +18,13 @@ VideoBatchWriter::VideoBatchWriter(const std::string& shm_dir,
|
||||
float end_delay_sec,
|
||||
int retention_days,
|
||||
int crf,
|
||||
const std::string& preset)
|
||||
const std::string& preset,
|
||||
bool file_source)
|
||||
: shm_dir_(shm_dir), output_dir_(output_dir),
|
||||
w_(w), h_(h), fps_(fps),
|
||||
delay_sec_(delay_sec), end_delay_sec_(end_delay_sec),
|
||||
retention_days_(retention_days), crf_(crf), preset_(preset)
|
||||
retention_days_(retention_days), crf_(crf), preset_(preset),
|
||||
file_source_(file_source)
|
||||
{
|
||||
ensure_dir(shm_dir_);
|
||||
ensure_dir(output_dir_);
|
||||
@@ -96,7 +98,8 @@ void VideoBatchWriter::note_ayam_detection() {
|
||||
void VideoBatchWriter::on_batch_closed(int batch_number, const std::string& counting_date) {
|
||||
current_batch_ = batch_number;
|
||||
current_date_ = counting_date;
|
||||
if (state_ == CONFIRMED) {
|
||||
bool save = file_source_ ? (state_ != IDLE) : (state_ == CONFIRMED);
|
||||
if (save) {
|
||||
state_ = STOPPING;
|
||||
state_start_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
@@ -105,7 +108,7 @@ void VideoBatchWriter::on_batch_closed(int batch_number, const std::string& coun
|
||||
void VideoBatchWriter::feed_frame(const cv::Mat& raw) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (state_ == SPECULATING) {
|
||||
if (state_ == SPECULATING && !file_source_) {
|
||||
float elapsed = std::chrono::duration<float>(now - state_start_).count();
|
||||
if (elapsed >= delay_sec_) {
|
||||
stop_and_delete();
|
||||
@@ -220,7 +223,9 @@ void VideoBatchWriter::shutdown() {
|
||||
std::string src = current_path_;
|
||||
current_path_.clear();
|
||||
|
||||
if (prev_state == CONFIRMED || prev_state == STOPPING) {
|
||||
bool keep = file_source_ ? (prev_state != IDLE)
|
||||
: (prev_state == CONFIRMED || prev_state == STOPPING);
|
||||
if (keep) {
|
||||
std::string day_dir = output_dir_ + "/" + (current_date_.empty() ? today_str() : current_date_);
|
||||
ensure_dir(day_dir);
|
||||
std::string dst = day_dir + "/" + build_mp4_name(current_batch_, current_date_);
|
||||
|
||||
@@ -12,7 +12,8 @@ public:
|
||||
float end_delay_sec,
|
||||
int retention_days,
|
||||
int crf,
|
||||
const std::string& preset);
|
||||
const std::string& preset,
|
||||
bool file_source);
|
||||
|
||||
~VideoBatchWriter();
|
||||
|
||||
@@ -39,6 +40,7 @@ private:
|
||||
int retention_days_;
|
||||
int crf_;
|
||||
std::string preset_;
|
||||
bool file_source_ = false;
|
||||
|
||||
State state_ = IDLE;
|
||||
std::chrono::steady_clock::time_point state_start_;
|
||||
|
||||
Reference in new issue
Block a user