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.
This commit is contained in:
parent
4dcd6c05a3
commit
13eaf75c8a
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
20
src/common.h
20
src/common.h
|
@ -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 ¶m, const string &line, string *value);
|
void rdLine(const string ¶m, const string &line, string *value);
|
||||||
void rdLine(const string ¶m, const string &line, int *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);
|
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);
|
||||||
|
|
183
src/main.cpp
183
src/main.cpp
|
@ -14,9 +14,15 @@
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
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)
|
void exeCmd(char *const argv[])
|
||||||
{
|
{
|
||||||
for (auto &&res : resList)
|
if (fork() == 0)
|
||||||
{
|
{
|
||||||
if (!res->finished) return false;
|
execvp("ffmpeg", argv);
|
||||||
|
_Exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user