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;
}
string genEventName()
{
return genTimeStr("%Y-%j-%H-%M");
}
void rdLine(const string &param, 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";

View File

@ -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<string> srcPaths;
@ -49,33 +49,39 @@ struct pls_t
struct shared_t
{
map<string, pls_t> 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<evt_t> 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);

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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