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); 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 <stdlib.h>
#include <errno.h> #include <errno.h>
#include <vector> #include <vector>
#include <thread>
#include <pthread.h>
#include <filesystem> #include <filesystem>
#include <mutex> #include <mutex>
#include <sys/types.h> #include <sys/types.h>
@ -37,13 +35,14 @@ using namespace cv;
using namespace std; using namespace std;
using namespace std::filesystem; using namespace std::filesystem;
#define APP_VER "1.6.t7" #define APP_VER "1.6.t8"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define TRIM_REMOVE " \n\r\t\f\v." #define TRIM_REMOVE " \n\r\t\f\v."
#define PATH_ADDR 9
#define MAX_CAP_RETRY 3
struct shared_t struct shared_t
{ {
vector<thread> detThreads;
ofstream recLogFile; ofstream recLogFile;
ofstream detLogFile; ofstream detLogFile;
string conf; string conf;
@ -66,7 +65,7 @@ struct shared_t
bool init; bool init;
bool skipCmd; bool skipCmd;
bool updateRoot; bool updateRoot;
int cmdFinished; int detProcs;
int clipLen; int clipLen;
int frameGap; int frameGap;
int pixThresh; int pixThresh;
@ -75,17 +74,10 @@ struct shared_t
int maxDays; int maxDays;
int maxClips; int maxClips;
int maxLogSize; int maxLogSize;
int index;
int retCode; int retCode;
}; };
struct cmdRes_t
{
string cmd;
pthread_t thr;
int ret;
bool finished;
};
string leftTrim(const string &str, const string &toRemove); string leftTrim(const string &str, const string &toRemove);
string rightTrim(const string &str, const string &toRemove); string rightTrim(const string &str, const string &toRemove);
string trim(const string &str, const string &toRemove); string trim(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 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, string *value);
void rdLine(const string &param, const string &line, int *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); bool rdConf(shared_t *share);
vector<string> lsFilesInDir(const string &path, const string &ext = string()); vector<string> lsFilesInDir(const string &path, const string &ext = string());
vector<string> lsDirsInDir(const string &path); vector<string> lsDirsInDir(const string &path);

View File

@ -15,8 +15,14 @@
void detectMoInFile(const string &detPath, const string &recPath, shared_t *share) void detectMoInFile(const string &detPath, const string &recPath, shared_t *share)
{ {
if (fork() == 0)
{
detLog("detect_mo_in_file() -- start", share); detLog("detect_mo_in_file() -- start", share);
share->detProcs++;
if (exists(detPath))
{
Mat thumbNail; Mat thumbNail;
if (moDetect(detPath, thumbNail, share)) if (moDetect(detPath, thumbNail, share))
@ -25,117 +31,24 @@ void detectMoInFile(const string &detPath, const string &recPath, shared_t *shar
wrOut(recPath, thumbNail, share); wrOut(recPath, thumbNail, share);
} }
}
if (exists(detPath)) remove(detPath); if (exists(detPath)) remove(detPath);
if (exists(recPath)) remove(recPath); if (exists(recPath)) remove(recPath);
share->detProcs--;
detLog("detect_mo_in_file() -- finished", share); 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)
{
pthread_cancel(res->thr);
}
} }
} }
bool allCmdsDidNotFail(const vector<cmdRes_t*> &resList) void exeCmd(char *const argv[])
{ {
for (auto &&res : resList) if (fork() == 0)
{ {
if (res->ret != 0) return false; execvp("ffmpeg", argv);
_Exit(EXIT_FAILURE);
} }
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)
{
cancelAllCmds(resList); break;
}
}
}
}
void runTOCmd(int timeout, const string &cmd, bool abs = true)
{
vector<string> cmds;
vector<cmdRes_t*> rets;
cmds.push_back(cmd);
runTOCmds(timeout, cmds, rets, abs);
cleanupRes(rets);
} }
void recLoop(shared_t *share) 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); recLog("camera specific webroot page updated: " + share->outDir + "/index.html", share);
for (auto i = 0; i < share->numOfClips; ++i) auto strClipLen = to_string(share->clipLen);
{
auto detPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->detSuffix;
auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix;
vector<string> cmds; char* argvRec[] = {(char*) "ffmpeg",
vector<cmdRes_t*> rets; (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) if (share->recordUrl == share->detectUrl)
{ {
recPath = detPath; 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); recLog("fetching camera footage -- ", share);
runTOCmds(share->clipLen, cmds, rets); if (share->recordUrl == share->detectUrl)
for (auto &&ret : rets)
{ {
recLog("cmd: " + ret->cmd + " return_code: " + to_string(ret->ret), share); exeCmd(argvDet);
}
if (allCmdsDidNotFail(rets))
{
share->detThreads.push_back(thread(detectMoInFile, detPath, recPath, share));
} }
else 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) if (!share->skipCmd)
{ {
@ -222,7 +151,8 @@ void recLoop(shared_t *share)
else else
{ {
recLog("running post command: " + share->postCmd, share); recLog("running post command: " + share->postCmd, share);
runTOCmd(14, share->postCmd, false);
system(string("timeout -k 1 14 " + share->postCmd).c_str());
} }
} }
else else
@ -236,6 +166,9 @@ void recLoop(shared_t *share)
{ {
break; break;
} }
destroy_at(argvDet);
destroy_at(argvRec);
} }
} }
@ -291,6 +224,8 @@ int main(int argc, char** argv)
else else
{ {
sharedRes.retCode = 0; sharedRes.retCode = 0;
sharedRes.detProcs = 0;
sharedRes.index = 0;
sharedRes.updateRoot = true; sharedRes.updateRoot = true;
sharedRes.skipCmd = false; sharedRes.skipCmd = false;
sharedRes.init = true; 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); 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) bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share)
{ {
detLog("mo_detect() -- start()", share); detLog("mo_detect() -- start()", share);
detLog("buff_file: " + buffFile, 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 prev;
Mat next; Mat next;

View File

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