Updated the documentation.

The test machine had a mystery crash that needs to be investigated. In
mean time, the timeout run code has been refactored and will not run
thread cancel unless is it absolutely needed at the individual thread
level (hopefully that fixes the crash issue).

post_cmd shall also now run via timeout. With that, no external commands
should cause this application to stall. Timeout protection should
prevent that.
This commit is contained in:
Maurice ONeal 2023-02-07 23:19:41 -05:00
parent 23e0ae935e
commit f4f1f62d25
3 changed files with 132 additions and 45 deletions

View File

@ -38,6 +38,14 @@ recording_stream = rtsp://1.2.3.4:554/h264
# this is the url to the main stream of the IP camera that will be used
# to record footage.
#
detect_stream = rtsp://1.2.3.4:554/h264cif
# this is an optional detection stream that will be used to detect motion.
# most cameras have the option to broadcast multiple streams of the same
# footage. it's recommend to use this parameter with a smaller, lower bit
# rate stream to reduce CPU usage. recording_stream will still be used to
# record higher quality footage. if not defined, recording_stream will be
# used for both motion detection and recording.
#
web_root = /var/www/html
# this is the output directory that will be used to store recorded footage
# from the cameras as well as the web interface for the application.

View File

@ -36,7 +36,7 @@ using namespace cv;
using namespace std;
using namespace std::filesystem;
#define APP_VER "1.6.t3"
#define APP_VER "1.6.t4"
#define APP_NAME "Motion Watch"
#define TRIM_REMOVE " \n\r\t\f\v."
@ -55,8 +55,6 @@ struct shared_t
string buffDir;
string recSuffix;
string detSuffix;
string recCmd;
string detCmd;
string vidExt;
string vidCodec;
string camName;
@ -78,6 +76,14 @@ struct shared_t
int retCode;
};
struct cmdRes_t
{
string cmd;
pthread_t thr;
int ret;
bool finished;
};
string leftTrim(const string &str, const string &toRemove);
string rightTrim(const string &str, const string &toRemove);
string trim(const string &str, const string &toRemove);

View File

@ -32,29 +32,109 @@ void detectMoInFile(const string &detPath, const string &recPath, shared_t *shar
detLog("detect_mo_in_file() -- finished", share);
}
static void *runDetCmd(void *arg)
static void *runCmd(void *arg)
{
auto share = reinterpret_cast<shared_t*>(arg);
auto args = static_cast<cmdRes_t*>(arg);
recLog("ffmpeg_det_run: " + share->detCmd, share);
pclose(popen(share->detCmd.c_str(), "r"));
args->finished = false;
args->ret = 0;
share->cmdFinished++; return NULL;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
args->ret = pclose(popen(args->cmd.c_str(), "r"));
args->finished = true;
return NULL;
}
static void *runRecCmd(void *arg)
bool allCmdsFinished(const vector<cmdRes_t*> &resList)
{
auto share = reinterpret_cast<shared_t*>(arg);
if (share->recordUrl != share->detectUrl)
for (auto &&res : resList)
{
// recording command is not allowed to run if both
// streams are the same.
recLog("ffmpeg_rec_run: " + share->recCmd, share);
pclose(popen(share->recCmd.c_str(), "r"));
if (!res->finished) return false;
}
share->cmdFinished++; return NULL;
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;
res->ret = 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)
@ -95,45 +175,38 @@ void recLoop(shared_t *share)
auto detPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->detSuffix;
auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix;
vector<string> cmds;
vector<cmdRes_t*> rets;
if (share->recordUrl == share->detectUrl)
{
recPath = detPath;
}
share->recCmd = "ffmpeg -hide_banner -i " + share->recordUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + recPath;
share->detCmd = "ffmpeg -hide_banner -i " + share->detectUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + detPath;
share->cmdFinished = 0;
pthread_t detThr;
pthread_t recThr;
auto detOk = pthread_create(&detThr, NULL, &runDetCmd, share);
auto recOk = pthread_create(&recThr, NULL, &runRecCmd, share);
if (detOk == 0 && recOk == 0)
else
{
sleep(share->clipLen);
cmds.push_back("ffmpeg -hide_banner -i " + share->recordUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + recPath);
}
if (share->cmdFinished < 2)
{
// 2 second grace period.
sleep(2);
}
cmds.push_back("ffmpeg -hide_banner -i " + share->detectUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + detPath);
if (share->cmdFinished < 2)
{
// force close ffmpeg cmds after failing to finish
// after the grace period.
pthread_cancel(detThr);
pthread_cancel(recThr);
}
recLog("fetching camera footage -- ", share);
runTOCmds(share->clipLen, cmds, rets);
if (allCmdsDidNotFail(rets))
{
new thread(detectMoInFile, detPath, recPath, share);
}
else
{
recLog("could not start one or both ffmpeg cmds in seperate threads. detOk = " + to_string(detOk) + " recOk = " + to_string(recOk), share);
recLog("one or both fetch cmds failed.", share);
}
recLog("fetch results:", share);
for (auto &&ret : rets)
{
recLog("cmd: " + ret->cmd + " return_code: " + to_string(ret->ret), share);
}
}
@ -148,7 +221,7 @@ void recLoop(shared_t *share)
else
{
recLog("running post command: " + share->postCmd, share);
system(share->postCmd.c_str());
runTOCmd(14, share->postCmd, false);
}
}
else