From 13eaf75c8aa7f893acea30ef145922b164a865d4 Mon Sep 17 00:00:00 2001 From: Maurice ONeal Date: Sat, 18 Feb 2023 17:43:10 -0500 Subject: [PATCH] v1.6.t8 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. --- src/common.cpp | 10 --- src/common.h | 86 +++++++++----------- src/main.cpp | 201 ++++++++++++++++------------------------------ src/mo_detect.cpp | 21 ++++- src/mo_detect.h | 1 + 5 files changed, 125 insertions(+), 194 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 82ac2f6..5637ba7 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -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(); -} diff --git a/src/common.h b/src/common.h index fdf73eb..2c9310c 100644 --- a/src/common.h +++ b/src/common.h @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include #include #include @@ -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 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 ¶m, const string &line, string *value); void rdLine(const string ¶m, const string &line, int *value); -void statOut(shared_t *share); -void waitForDetThreads(shared_t *share); bool rdConf(shared_t *share); vector lsFilesInDir(const string &path, const string &ext = string()); vector lsDirsInDir(const string &path); diff --git a/src/main.cpp b/src/main.cpp index 0b10333..80f85ff 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(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 &resList) -{ - for (auto &&res : resList) - { - if (!res->finished) return false; - } - - return true; -} - -void cancelAllCmds(const vector &resList) -{ - for (auto &&res : resList) - { - if (!res->finished) + if (exists(detPath)) { - pthread_cancel(res->thr); - } - } -} + Mat thumbNail; -bool allCmdsDidNotFail(const vector &resList) -{ - for (auto &&res : resList) - { - if (res->ret != 0) return false; - } - - return true; -} - -void cleanupRes(vector &resList) -{ - for (auto &&res : resList) - { - delete res; - } -} - -void runTOCmds(int timeOut, const vector &cmds, vector &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 cmds; - vector 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 cmds; - vector 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; diff --git a/src/mo_detect.cpp b/src/mo_detect.cpp index 48a8ab1..fc9e145 100644 --- a/src/mo_detect.cpp +++ b/src/mo_detect.cpp @@ -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; diff --git a/src/mo_detect.h b/src/mo_detect.h index 894573c..71c4f08 100644 --- a/src/mo_detect.h +++ b/src/mo_detect.h @@ -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);