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.
This commit is contained in:
Maurice ONeal 2023-04-20 14:52:59 -04:00
parent bafd2bf727
commit b4ca30b0e1
5 changed files with 170 additions and 99 deletions

View File

@ -175,6 +175,11 @@ string genDstFile(const string &dirOut, const char *fmt, const string &ext)
return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext; return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext;
} }
string genEventName()
{
return genTimeStr("%Y-%j-%H-%M");
}
void rdLine(const string &param, const string &line, string *value) void rdLine(const string &param, const string &line, string *value)
{ {
if (line.rfind(param.c_str(), 0) == 0) 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_text = ", line, &share->webTxt);
rdLine("web_bg = ", line, &share->webBg); rdLine("web_bg = ", line, &share->webBg);
rdLine("web_font = ", line, &share->webFont); 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("post_cmd = ", line, &share->postCmd);
rdLine("pix_thresh = ", line, &share->pixThresh); rdLine("pix_thresh = ", line, &share->pixThresh);
rdLine("img_thresh = ", line, &share->imgThresh); rdLine("img_thresh = ", line, &share->imgThresh);
rdLine("frame_gap = ", line, &share->frameGap);
rdLine("max_events = ", line, &share->maxEvents); rdLine("max_events = ", line, &share->maxEvents);
rdLine("max_log_size = ", line, &share->maxLogSize); rdLine("max_log_size = ", line, &share->maxLogSize);
} }
@ -241,9 +248,11 @@ bool rdConf(shared_t *share)
share->pixThresh = 50; share->pixThresh = 50;
share->imgThresh = 800; share->imgThresh = 800;
share->maxEvents = 40; share->maxEvents = 40;
share->maxLogSize = 50000; share->maxLogSize = 100000;
share->skipCmd = false; share->skipCmd = false;
share->schSec = 60; share->postSecs = 60;
share->evMaxSecs = 10;
share->frameGap = 5;
share->webRoot = "/var/www/html"; share->webRoot = "/var/www/html";
share->webBg = "#485564"; share->webBg = "#485564";
share->webTxt = "#dee5ee"; share->webTxt = "#dee5ee";

View File

@ -34,13 +34,13 @@ using namespace std;
using namespace std::filesystem; using namespace std::filesystem;
using namespace std::chrono; using namespace std::chrono;
#define APP_VER "2.1.t1" #define APP_VER "2.1.t2"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define REC_LOG_NAME "rec_log_lines.html" #define REC_LOG_NAME "rec_log_lines.html"
#define DET_LOG_NAME "det_log_lines.html" #define DET_LOG_NAME "det_log_lines.html"
#define UPK_LOG_NAME "upk_log_lines.html" #define UPK_LOG_NAME "upk_log_lines.html"
struct pls_t struct evt_t
{ {
string evName; string evName;
vector<string> srcPaths; vector<string> srcPaths;
@ -49,33 +49,39 @@ struct pls_t
struct shared_t struct shared_t
{ {
map<string, pls_t> recList; vector<evt_t> recList;
string curEventName; string conf;
string conf; string recLog;
string recLog; string detLog;
string detLog; string upkLog;
string upkLog; string recordUrl;
string recordUrl; string outDir;
string outDir; string postCmd;
string postCmd; string camName;
string camName; string webBg;
string webBg; string webTxt;
string webTxt; string webFont;
string webFont; string webRoot;
string webRoot; evt_t curEvent;
bool skipCmd; bool skipCmd;
int maxScore; int frameGap;
int procTime; int evMaxSecs;
int schSec; int postSecs;
int pixThresh; int maxScore;
int imgThresh; int procCnt;
int maxEvents; int hlsCnt;
int maxLogSize; int pixThresh;
int retCode; int imgThresh;
int maxEvents;
int maxLogSize;
int retCode;
int postInd;
int evInd;
}; };
string genVidNameFromLive(const string &tsPath); string genVidNameFromLive(const string &tsPath);
string genEventPath(const string &tsPath); string genEventPath(const string &tsPath);
string genEventName();
string genDstFile(const string &dirOut, const char *fmt, const string &ext); string genDstFile(const string &dirOut, const char *fmt, const string &ext);
string genTimeStr(const char *fmt); string genTimeStr(const char *fmt);
string cleanDir(const string &path); string cleanDir(const string &path);

View File

@ -14,6 +14,17 @@
#include "logger.h" #include "logger.h"
#include "web.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) void detectMo(shared_t *share)
{ {
while (share->retCode == 0) while (share->retCode == 0)
@ -27,27 +38,32 @@ void eventLoop(shared_t *share)
{ {
while (share->retCode == 0) while (share->retCode == 0)
{ {
while (share->recList.size() > 1) if (!share->recList.empty())
{ {
auto it = share->recList.begin(); auto event = share->recList[0];
auto evName = it->first;
auto event = it->second;
try try
{ {
createDirTree("events"); recLog("attempting write out of event: " + event.evName, share);
wrOutVod(event, share);
genHTMLvod(evName);
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) catch (filesystem_error &ex)
{ {
recLog(string("err: ") + ex.what(), share); recLog(string("err: ") + ex.what(), share);
} }
share->recList.erase(share->recList.begin());
} }
sleep(10); sleep(10);
@ -96,7 +112,7 @@ void upkeep(shared_t *share)
upkLog("skipping update of the webroot page, it is busy.", 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 else
{ {
sharedRes.retCode = 0; sharedRes.retCode = 0;
sharedRes.procTime = 0;
sharedRes.maxScore = 0; sharedRes.maxScore = 0;
sharedRes.postInd = 0;
sharedRes.evInd = 0;
sharedRes.skipCmd = false; sharedRes.skipCmd = false;
rdConf(&sharedRes); rdConf(&sharedRes);
@ -169,11 +186,13 @@ int main(int argc, char** argv)
auto thr2 = thread(upkeep, &sharedRes); auto thr2 = thread(upkeep, &sharedRes);
auto thr3 = thread(detectMo, &sharedRes); auto thr3 = thread(detectMo, &sharedRes);
auto thr4 = thread(eventLoop, &sharedRes); auto thr4 = thread(eventLoop, &sharedRes);
auto thr5 = thread(timer, &sharedRes);
thr1.join(); thr1.join();
thr2.join(); thr2.join();
thr3.join(); thr3.join();
thr4.join(); thr4.join();
thr5.join();
return sharedRes.retCode; return sharedRes.retCode;
} }

View File

@ -14,29 +14,55 @@
void detectMoInStream(const string &streamFile, shared_t *share) void detectMoInStream(const string &streamFile, shared_t *share)
{ {
if (share->procTime >= share->schSec) if (share->postInd >= share->postSecs)
{ {
share->curEventName.clear(); if (!share->postCmd.empty())
share->procTime = 0;
share->maxScore = 0;
if (!share->skipCmd)
{ {
detLog("no motion detected, running post command: " + share->postCmd, share); detLog("---POST_BREAK---", share);
system(share->postCmd.c_str());
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 else
{ {
share->skipCmd = false; detLog("no motion detected in all files. none queued for event generation.", share);
detLog("motion detected, skipping the post command.", share);
} }
share->curEvent.srcPaths.clear();
share->curEvent.evName.clear();
share->curEvent.thumbnail.release();
share->evInd = 0;
share->maxScore = 0;
} }
ifstream fileIn(streamFile); ifstream fileIn(streamFile);
string tsPath; string tsPath;
Mat thumbnail;
for (string line; getline(fileIn, line); ) 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 (!tsPath.empty())
{ {
if (moDetect(tsPath, thumbnail, share)) if (moDetect(tsPath, share))
{ {
if (share->recList.find(share->curEventName) != share->recList.end()) share->curEvent.srcPaths.push_back(tsPath);
{
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->skipCmd = true; 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; 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 score = 0;
auto mod = false;
detLog("stream_clip: " + buffFile, share); 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()) if (capture.isOpened())
{ {
Mat prev; Mat prev;
Mat next; Mat next;
detLog("capture open successful.", share); for (auto gap = 0; capture.grab(); ++gap)
while (capture.grab())
{ {
if (prev.empty()) if (prev.empty())
{ {
capture.retrieve(prev); capture.retrieve(prev);
} }
else else if (gap == (share->frameGap - 1))
{ {
capture.retrieve(next); capture.retrieve(next);
@ -132,45 +138,76 @@ bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share)
{ {
if (imgDiff(prev, next, score, share)) if (imgDiff(prev, next, score, share))
{ {
if (score > share->maxScore) mod = true;
if (share->maxScore <= score)
{ {
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(); next.release();
} }
else
sleep(1); {
capture.grab();
}
} }
} }
else else
{ {
detLog("capture open failure, check debug output.", share); detLog("err: failed to open: " + buffFile + " after 500 msecs. giving up.", share);
} }
capture.release(); 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"; auto concat = event.evName + ".tmp";
ofstream file(concat.c_str()); ofstream file(concat.c_str());
for (auto i = 0; i < event.srcPaths.size(); ++i) 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(); file.close();
system(string("ffmpeg -f concat -safe 0 -i " + concat + " -c copy events/" + event.evName + ".mp4").c_str()); if (cnt == 0)
remove(concat); {
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;
}
} }

View File

@ -17,8 +17,8 @@
#include "logger.h" #include "logger.h"
bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share); 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 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 #endif // MO_DETECT_H