going back to basics. removed all threading code and opted for a multi
process architecture using fork(). previous code had a bad memory leak
and doesn't handle unexpected camera disconnects and for some reason it
also didn't recover gracefully in systemctl when it crashes. Hopefully
this new re-write fixes all of those numerous issues.

moDetect() will now try multiple times to grab buffer footage before
giving up and moving on.
This commit is contained in:
Maurice ONeal 2023-02-18 17:43:10 -05:00
parent 4dcd6c05a3
commit 13eaf75c8a
5 changed files with 125 additions and 194 deletions

View File

@ -355,13 +355,3 @@ string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
return parseForParam(arg, argc, argv, argOnly, notUsed);
}
void waitForDetThreads(shared_t *share)
{
for (auto &&thr : share->detThreads)
{
thr.join();
}
share->detThreads.clear();
}

View File

@ -21,8 +21,6 @@
#include <stdlib.h>
#include <errno.h>
#include <vector>
#include <thread>
#include <pthread.h>
#include <filesystem>
#include <mutex>
#include <sys/types.h>
@ -37,53 +35,47 @@ using namespace cv;
using namespace std;
using namespace std::filesystem;
#define APP_VER "1.6.t7"
#define APP_NAME "Motion Watch"
#define TRIM_REMOVE " \n\r\t\f\v."
#define APP_VER "1.6.t8"
#define APP_NAME "Motion Watch"
#define TRIM_REMOVE " \n\r\t\f\v."
#define PATH_ADDR 9
#define MAX_CAP_RETRY 3
struct shared_t
{
vector<thread> detThreads;
ofstream recLogFile;
ofstream detLogFile;
string conf;
string recLogPath;
string detLogPath;
string recordUrl;
string detectUrl;
string outDir;
string postCmd;
string buffDir;
string recSuffix;
string detSuffix;
string vidExt;
string vidCodec;
string camName;
string webBg;
string webTxt;
string webFont;
string webRoot;
bool init;
bool skipCmd;
bool updateRoot;
int cmdFinished;
int clipLen;
int frameGap;
int pixThresh;
int imgThresh;
int numOfClips;
int maxDays;
int maxClips;
int maxLogSize;
int retCode;
};
struct cmdRes_t
{
string cmd;
pthread_t thr;
int ret;
bool finished;
ofstream recLogFile;
ofstream detLogFile;
string conf;
string recLogPath;
string detLogPath;
string recordUrl;
string detectUrl;
string outDir;
string postCmd;
string buffDir;
string recSuffix;
string detSuffix;
string vidExt;
string vidCodec;
string camName;
string webBg;
string webTxt;
string webFont;
string webRoot;
bool init;
bool skipCmd;
bool updateRoot;
int detProcs;
int clipLen;
int frameGap;
int pixThresh;
int imgThresh;
int numOfClips;
int maxDays;
int maxClips;
int maxLogSize;
int index;
int retCode;
};
string leftTrim(const string &str, const string &toRemove);
@ -101,8 +93,6 @@ void enforceMaxDays(const string &dirPath, shared_t *share);
void enforceMaxClips(const string &dirPath, shared_t *share);
void rdLine(const string &param, const string &line, string *value);
void rdLine(const string &param, const string &line, int *value);
void statOut(shared_t *share);
void waitForDetThreads(shared_t *share);
bool rdConf(shared_t *share);
vector<string> lsFilesInDir(const string &path, const string &ext = string());
vector<string> lsDirsInDir(const string &path);

View File

@ -15,127 +15,40 @@
void detectMoInFile(const string &detPath, const string &recPath, shared_t *share)
{
detLog("detect_mo_in_file() -- start", share);
Mat thumbNail;
if (moDetect(detPath, thumbNail, share))
if (fork() == 0)
{
share->skipCmd = true;
detLog("detect_mo_in_file() -- start", share);
wrOut(recPath, thumbNail, share);
}
share->detProcs++;
if (exists(detPath)) remove(detPath);
if (exists(recPath)) remove(recPath);
detLog("detect_mo_in_file() -- finished", share);
}
static void *runCmd(void *arg)
{
auto args = static_cast<cmdRes_t*>(arg);
args->finished = false;
args->ret = 0;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
args->ret = pclose(popen(args->cmd.c_str(), "r"));
args->finished = true;
return NULL;
}
bool allCmdsFinished(const vector<cmdRes_t*> &resList)
{
for (auto &&res : resList)
{
if (!res->finished) return false;
}
return true;
}
void cancelAllCmds(const vector<cmdRes_t*> &resList)
{
for (auto &&res : resList)
{
if (!res->finished)
if (exists(detPath))
{
pthread_cancel(res->thr);
}
}
}
Mat thumbNail;
bool allCmdsDidNotFail(const vector<cmdRes_t*> &resList)
{
for (auto &&res : resList)
{
if (res->ret != 0) return false;
}
return true;
}
void cleanupRes(vector<cmdRes_t*> &resList)
{
for (auto &&res : resList)
{
delete res;
}
}
void runTOCmds(int timeOut, const vector<string> &cmds, vector<cmdRes_t*> &resList, bool abs = true)
{
resList.clear();
for (auto &&cmd : cmds)
{
auto res = new struct cmdRes_t;
res->cmd = cmd;
res->finished = false;
pthread_create(&res->thr, NULL, &runCmd, res);
resList.push_back(res);
}
if (abs)
{
sleep(timeOut);
if (!allCmdsFinished(resList))
{
sleep(2);
}
cancelAllCmds(resList);
}
else
{
for (auto i = 0; !allCmdsFinished(resList); ++i)
{
sleep(1);
if (i >= timeOut)
if (moDetect(detPath, thumbNail, share))
{
cancelAllCmds(resList); break;
share->skipCmd = true;
wrOut(recPath, thumbNail, share);
}
}
if (exists(detPath)) remove(detPath);
if (exists(recPath)) remove(recPath);
share->detProcs--;
detLog("detect_mo_in_file() -- finished", share);
}
}
void runTOCmd(int timeout, const string &cmd, bool abs = true)
void exeCmd(char *const argv[])
{
vector<string> cmds;
vector<cmdRes_t*> rets;
cmds.push_back(cmd);
runTOCmds(timeout, cmds, rets, abs);
cleanupRes(rets);
if (fork() == 0)
{
execvp("ffmpeg", argv);
_Exit(EXIT_FAILURE);
}
}
void recLoop(shared_t *share)
@ -169,47 +82,63 @@ void recLoop(shared_t *share)
recLog("camera specific webroot page updated: " + share->outDir + "/index.html", share);
for (auto i = 0; i < share->numOfClips; ++i)
{
auto detPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->detSuffix;
auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix;
auto strClipLen = to_string(share->clipLen);
vector<string> cmds;
vector<cmdRes_t*> rets;
char* argvRec[] = {(char*) "ffmpeg",
(char*) "-hide_banner",
(char*) "-i",
(char*) share->recordUrl.c_str(),
(char*) "-y",
(char*) "-vcodec",
(char*) share->vidCodec.c_str(),
(char*) "-t",
(char*) strClipLen.c_str(),
(char*) "--replace_me--",
NULL};
char* argvDet[] = {(char*) "ffmpeg",
(char*) "-hide_banner",
(char*) "-i",
(char*) share->detectUrl.c_str(),
(char*) "-y",
(char*) "-vcodec",
(char*) share->vidCodec.c_str(),
(char*) "-t",
(char*) strClipLen.c_str(),
(char*) "--replace_me--",
NULL};
for (auto i = 0; i < share->numOfClips; ++i, ++share->index)
{
auto detPath = cleanDir(share->buffDir) + "/" + to_string(share->index) + share->detSuffix;
auto recPath = cleanDir(share->buffDir) + "/" + to_string(share->index) + share->recSuffix;
if (share->recordUrl == share->detectUrl)
{
recPath = detPath;
}
else
{
cmds.push_back("ffmpeg -hide_banner -i " + share->recordUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + recPath);
}
cmds.push_back("ffmpeg -hide_banner -i " + share->detectUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + detPath);
argvRec[PATH_ADDR] = (char*) recPath.c_str();
argvDet[PATH_ADDR] = (char*) detPath.c_str();
recLog("fetching camera footage -- ", share);
runTOCmds(share->clipLen, cmds, rets);
for (auto &&ret : rets)
if (share->recordUrl == share->detectUrl)
{
recLog("cmd: " + ret->cmd + " return_code: " + to_string(ret->ret), share);
}
if (allCmdsDidNotFail(rets))
{
share->detThreads.push_back(thread(detectMoInFile, detPath, recPath, share));
exeCmd(argvDet);
}
else
{
recLog("one or both fetch cmds failed.", share);
exeCmd(argvDet);
exeCmd(argvRec);
}
cleanupRes(rets);
sleep(share->clipLen * 2);
detectMoInFile(detPath, recPath, share);
}
waitForDetThreads(share);
while (share->detProcs > 0) sleep(1);
if (!share->skipCmd)
{
@ -222,7 +151,8 @@ void recLoop(shared_t *share)
else
{
recLog("running post command: " + share->postCmd, share);
runTOCmd(14, share->postCmd, false);
system(string("timeout -k 1 14 " + share->postCmd).c_str());
}
}
else
@ -236,6 +166,9 @@ void recLoop(shared_t *share)
{
break;
}
destroy_at(argvDet);
destroy_at(argvRec);
}
}
@ -291,6 +224,8 @@ int main(int argc, char** argv)
else
{
sharedRes.retCode = 0;
sharedRes.detProcs = 0;
sharedRes.index = 0;
sharedRes.updateRoot = true;
sharedRes.skipCmd = false;
sharedRes.init = true;

View File

@ -95,16 +95,31 @@ void wrOut(const string &buffFile, const Mat &vidThumb, shared_t *share)
detLog("wr_out() -- finished()", share);
}
bool openVid(const string &buffFile, VideoCapture &cap)
{
auto ret = cap.open(buffFile.c_str(), CAP_FFMPEG);
for (auto i = 0; (i < 3) && !ret; ++i)
{
sleep(1);
ret = cap.open(buffFile.c_str(), CAP_FFMPEG);
}
return ret;
}
bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share)
{
detLog("mo_detect() -- start()", share);
detLog("buff_file: " + buffFile, share);
auto mod = false;
auto mod = false;
auto trys = 0;
VideoCapture capture(buffFile.c_str(), CAP_FFMPEG);
VideoCapture capture;
if (capture.isOpened())
if (openVid(buffFile, capture))
{
Mat prev;
Mat next;

View File

@ -17,6 +17,7 @@
#include "web.h"
#include "logger.h"
bool openVid(const string &buffFile, VideoCapture &cap);
bool imgDiff(const Mat &prev, const Mat &next, shared_t *share);
bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share);
void wrOut(const string &buffFile, const Mat &vidThumb, shared_t *share);