From b4ca30b0e1f739bb2906f14f83e818e72d71913f Mon Sep 17 00:00:00 2001 From: Maurice ONeal Date: Thu, 20 Apr 2023 14:52:59 -0400 Subject: [PATCH] v2.1.t2 the delay on the motion detection loop slowed it down too much to the point that it falls too far behind live. I removed the delay and re-introduced the frame gap so all frames in the video files need to be decoded. post command and event timers are now seperate but still tied to a single thread so they can still be synced. fixed an issued that cuased several thmubnails to not generate. added more log lines the aid with debugging. --- src/common.cpp | 15 ++++- src/common.h | 56 +++++++++-------- src/main.cpp | 41 +++++++++---- src/mo_detect.cpp | 153 ++++++++++++++++++++++++++++------------------ src/mo_detect.h | 4 +- 5 files changed, 170 insertions(+), 99 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 9948c13..1a530a6 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -175,6 +175,11 @@ string genDstFile(const string &dirOut, const char *fmt, const string &ext) return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext; } +string genEventName() +{ + return genTimeStr("%Y-%j-%H-%M"); +} + void rdLine(const string ¶m, const string &line, string *value) { if (line.rfind(param.c_str(), 0) == 0) @@ -217,10 +222,12 @@ bool rdConf(const string &filePath, shared_t *share) rdLine("web_text = ", line, &share->webTxt); rdLine("web_bg = ", line, &share->webBg); rdLine("web_font = ", line, &share->webFont); - rdLine("sch_sec = ", line, &share->schSec); + rdLine("max_event_secs = ", line, &share->evMaxSecs); + rdLine("post_secs = ", line, &share->postSecs); rdLine("post_cmd = ", line, &share->postCmd); rdLine("pix_thresh = ", line, &share->pixThresh); rdLine("img_thresh = ", line, &share->imgThresh); + rdLine("frame_gap = ", line, &share->frameGap); rdLine("max_events = ", line, &share->maxEvents); rdLine("max_log_size = ", line, &share->maxLogSize); } @@ -241,9 +248,11 @@ bool rdConf(shared_t *share) share->pixThresh = 50; share->imgThresh = 800; share->maxEvents = 40; - share->maxLogSize = 50000; + share->maxLogSize = 100000; share->skipCmd = false; - share->schSec = 60; + share->postSecs = 60; + share->evMaxSecs = 10; + share->frameGap = 5; share->webRoot = "/var/www/html"; share->webBg = "#485564"; share->webTxt = "#dee5ee"; diff --git a/src/common.h b/src/common.h index 46aae60..f9a8eb1 100644 --- a/src/common.h +++ b/src/common.h @@ -34,13 +34,13 @@ using namespace std; using namespace std::filesystem; using namespace std::chrono; -#define APP_VER "2.1.t1" +#define APP_VER "2.1.t2" #define APP_NAME "Motion Watch" #define REC_LOG_NAME "rec_log_lines.html" #define DET_LOG_NAME "det_log_lines.html" #define UPK_LOG_NAME "upk_log_lines.html" -struct pls_t +struct evt_t { string evName; vector srcPaths; @@ -49,33 +49,39 @@ struct pls_t struct shared_t { - map recList; - string curEventName; - string conf; - string recLog; - string detLog; - string upkLog; - string recordUrl; - string outDir; - string postCmd; - string camName; - string webBg; - string webTxt; - string webFont; - string webRoot; - bool skipCmd; - int maxScore; - int procTime; - int schSec; - int pixThresh; - int imgThresh; - int maxEvents; - int maxLogSize; - int retCode; + vector recList; + string conf; + string recLog; + string detLog; + string upkLog; + string recordUrl; + string outDir; + string postCmd; + string camName; + string webBg; + string webTxt; + string webFont; + string webRoot; + evt_t curEvent; + bool skipCmd; + int frameGap; + int evMaxSecs; + int postSecs; + int maxScore; + int procCnt; + int hlsCnt; + int pixThresh; + int imgThresh; + int maxEvents; + int maxLogSize; + int retCode; + int postInd; + int evInd; }; string genVidNameFromLive(const string &tsPath); string genEventPath(const string &tsPath); +string genEventName(); string genDstFile(const string &dirOut, const char *fmt, const string &ext); string genTimeStr(const char *fmt); string cleanDir(const string &path); diff --git a/src/main.cpp b/src/main.cpp index d3d58e7..89d2513 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,17 @@ #include "logger.h" #include "web.h" +void timer(shared_t *share) +{ + while (share->retCode == 0) + { + sleep(1); + + share->postInd += 1; + share->evInd += 1; + } +} + void detectMo(shared_t *share) { while (share->retCode == 0) @@ -27,27 +38,32 @@ void eventLoop(shared_t *share) { while (share->retCode == 0) { - while (share->recList.size() > 1) + if (!share->recList.empty()) { - auto it = share->recList.begin(); - auto evName = it->first; - auto event = it->second; + auto event = share->recList[0]; try { - createDirTree("events"); - wrOutVod(event, share); - genHTMLvod(evName); + recLog("attempting write out of event: " + event.evName, share); - if (!exists("events/" + evName + ".jpg")) + createDirTree("events"); + + if (wrOutVod(event, share)) { - imwrite(string("events/" + evName + ".jpg").c_str(), event.thumbnail); + genHTMLvod(event.evName); + + if (exists("events")) + { + imwrite(string("events/" + event.evName + ".jpg").c_str(), event.thumbnail); + } } } catch (filesystem_error &ex) { recLog(string("err: ") + ex.what(), share); } + + share->recList.erase(share->recList.begin()); } sleep(10); @@ -96,7 +112,7 @@ void upkeep(shared_t *share) upkLog("skipping update of the webroot page, it is busy.", share); } - sleep(60); + sleep(10); } } @@ -159,8 +175,9 @@ int main(int argc, char** argv) else { sharedRes.retCode = 0; - sharedRes.procTime = 0; sharedRes.maxScore = 0; + sharedRes.postInd = 0; + sharedRes.evInd = 0; sharedRes.skipCmd = false; rdConf(&sharedRes); @@ -169,11 +186,13 @@ int main(int argc, char** argv) auto thr2 = thread(upkeep, &sharedRes); auto thr3 = thread(detectMo, &sharedRes); auto thr4 = thread(eventLoop, &sharedRes); + auto thr5 = thread(timer, &sharedRes); thr1.join(); thr2.join(); thr3.join(); thr4.join(); + thr5.join(); return sharedRes.retCode; } diff --git a/src/mo_detect.cpp b/src/mo_detect.cpp index 6b2cb60..1a51833 100644 --- a/src/mo_detect.cpp +++ b/src/mo_detect.cpp @@ -14,29 +14,55 @@ void detectMoInStream(const string &streamFile, shared_t *share) { - if (share->procTime >= share->schSec) + if (share->postInd >= share->postSecs) { - share->curEventName.clear(); - - share->procTime = 0; - share->maxScore = 0; - - if (!share->skipCmd) + if (!share->postCmd.empty()) { - detLog("no motion detected, running post command: " + share->postCmd, share); - system(share->postCmd.c_str()); + detLog("---POST_BREAK---", share); + + if (!share->skipCmd) + { + detLog("no motion detected, running post command: " + share->postCmd, share); + system(share->postCmd.c_str()); + } + else + { + share->skipCmd = false; + + detLog("motion detected, skipping the post command.", share); + } + } + + share->postInd = 0; + } + + if (share->evInd >= share->evMaxSecs) + { + detLog("---EVENT_BREAK---", share); + + if (!share->curEvent.srcPaths.empty()) + { + share->curEvent.evName = genEventName(); + share->recList.push_back(share->curEvent); + + detLog("motion detected in " + to_string(share->curEvent.srcPaths.size()) + " file(s) in " + to_string(share->evMaxSecs) + " secs", share); + detLog("all video clips queued for event generation under event name: " + share->curEvent.evName, share); } else { - share->skipCmd = false; - - detLog("motion detected, skipping the post command.", share); + detLog("no motion detected in all files. none queued for event generation.", share); } + + share->curEvent.srcPaths.clear(); + share->curEvent.evName.clear(); + share->curEvent.thumbnail.release(); + + share->evInd = 0; + share->maxScore = 0; } ifstream fileIn(streamFile); string tsPath; - Mat thumbnail; for (string line; getline(fileIn, line); ) { @@ -46,40 +72,14 @@ void detectMoInStream(const string &streamFile, shared_t *share) } } - if (share->curEventName.empty()) - { - share->curEventName = genTimeStr("%Y-%j-%H-%M"); - } - if (!tsPath.empty()) { - if (moDetect(tsPath, thumbnail, share)) + if (moDetect(tsPath, share)) { - if (share->recList.find(share->curEventName) != share->recList.end()) - { - share->recList[share->curEventName].srcPaths.push_back(tsPath); - - if (!thumbnail.empty()) - { - share->recList[share->curEventName].thumbnail = thumbnail.clone(); - } - } - else - { - pls_t event; - - event.srcPaths.push_back(tsPath); - - event.thumbnail = thumbnail.clone(); - event.evName = share->curEventName; - - share->recList.insert(pair{event.evName, event}); - } + share->curEvent.srcPaths.push_back(tsPath); share->skipCmd = true; } - - share->procTime += 2; } } @@ -103,28 +103,34 @@ bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share) return score >= share->imgThresh; } -bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share) +bool moDetect(const string &buffFile, shared_t *share) { auto score = 0; + auto mod = false; detLog("stream_clip: " + buffFile, share); - VideoCapture capture(buffFile.c_str(), CAP_FFMPEG); + VideoCapture capture; + + if (!capture.open(buffFile.c_str(), CAP_FFMPEG)) + { + usleep(500); + + capture.open(buffFile.c_str(), CAP_FFMPEG); + } if (capture.isOpened()) { Mat prev; Mat next; - detLog("capture open successful.", share); - - while (capture.grab()) + for (auto gap = 0; capture.grab(); ++gap) { if (prev.empty()) { capture.retrieve(prev); } - else + else if (gap == (share->frameGap - 1)) { capture.retrieve(next); @@ -132,45 +138,76 @@ bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share) { if (imgDiff(prev, next, score, share)) { - if (score > share->maxScore) + mod = true; + + if (share->maxScore <= score) { share->maxScore = score; - resize(next, vidThumb, Size(720, 480), INTER_LINEAR); + resize(next, share->curEvent.thumbnail, Size(720, 480), INTER_LINEAR); } } } - prev.release(); + prev = next.clone(); + gap = 0; + next.release(); } - - sleep(1); + else + { + capture.grab(); + } } } else { - detLog("capture open failure, check debug output.", share); + detLog("err: failed to open: " + buffFile + " after 500 msecs. giving up.", share); } capture.release(); - return score > 0; + return mod; } -void wrOutVod(const pls_t &event, shared_t *share) +bool wrOutVod(const evt_t &event, shared_t *share) { + auto cnt = 0; auto concat = event.evName + ".tmp"; ofstream file(concat.c_str()); for (auto i = 0; i < event.srcPaths.size(); ++i) { - file << "file '" << event.srcPaths[i] << "''" << endl; + recLog("event_src: " + event.srcPaths[i], share); + + if (exists(event.srcPaths[i])) + { + file << "file '" << event.srcPaths[i] << "''" << endl; cnt++; + } } file.close(); - system(string("ffmpeg -f concat -safe 0 -i " + concat + " -c copy events/" + event.evName + ".mp4").c_str()); - remove(concat); + if (cnt == 0) + { + recLog("err: none of the event hls clips exists, canceling write out.", share); + + if (exists(concat)) remove(concat); + + return false; + } + else + { + auto ret = system(string("ffmpeg -f concat -safe 0 -i " + concat + " -c copy events/" + event.evName + ".mp4").c_str()); + + if (ret != 0) + { + recLog("err: ffmpeg concat failure, canceling write out.", share); + } + + if (exists(concat)) remove(concat); + + return ret == 0; + } } diff --git a/src/mo_detect.h b/src/mo_detect.h index f148db2..8191ef7 100644 --- a/src/mo_detect.h +++ b/src/mo_detect.h @@ -17,8 +17,8 @@ #include "logger.h" bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share); -bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share); +bool moDetect(const string &buffFile, shared_t *share); void detectMoInStream(const string &streamFile, shared_t *share); -void wrOutVod(const pls_t &pls, shared_t *share); +bool wrOutVod(const evt_t &pls, shared_t *share); #endif // MO_DETECT_H