v1.6.t4
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:
parent
23e0ae935e
commit
f4f1f62d25
|
@ -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
|
# this is the url to the main stream of the IP camera that will be used
|
||||||
# to record footage.
|
# 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
|
web_root = /var/www/html
|
||||||
# this is the output directory that will be used to store recorded footage
|
# 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.
|
# from the cameras as well as the web interface for the application.
|
||||||
|
|
12
src/common.h
12
src/common.h
|
@ -36,7 +36,7 @@ using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace std::filesystem;
|
using namespace std::filesystem;
|
||||||
|
|
||||||
#define APP_VER "1.6.t3"
|
#define APP_VER "1.6.t4"
|
||||||
#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."
|
||||||
|
|
||||||
|
@ -55,8 +55,6 @@ struct shared_t
|
||||||
string buffDir;
|
string buffDir;
|
||||||
string recSuffix;
|
string recSuffix;
|
||||||
string detSuffix;
|
string detSuffix;
|
||||||
string recCmd;
|
|
||||||
string detCmd;
|
|
||||||
string vidExt;
|
string vidExt;
|
||||||
string vidCodec;
|
string vidCodec;
|
||||||
string camName;
|
string camName;
|
||||||
|
@ -78,6 +76,14 @@ struct shared_t
|
||||||
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);
|
||||||
|
|
157
src/main.cpp
157
src/main.cpp
|
@ -32,29 +32,109 @@ void detectMoInFile(const string &detPath, const string &recPath, shared_t *shar
|
||||||
detLog("detect_mo_in_file() -- finished", share);
|
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);
|
args->finished = false;
|
||||||
pclose(popen(share->detCmd.c_str(), "r"));
|
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);
|
for (auto &&res : resList)
|
||||||
|
|
||||||
if (share->recordUrl != share->detectUrl)
|
|
||||||
{
|
{
|
||||||
// recording command is not allowed to run if both
|
if (!res->finished) return false;
|
||||||
// streams are the same.
|
|
||||||
recLog("ffmpeg_rec_run: " + share->recCmd, share);
|
|
||||||
pclose(popen(share->recCmd.c_str(), "r"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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 detPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->detSuffix;
|
||||||
auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix;
|
auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix;
|
||||||
|
|
||||||
|
vector<string> cmds;
|
||||||
|
vector<cmdRes_t*> rets;
|
||||||
|
|
||||||
if (share->recordUrl == share->detectUrl)
|
if (share->recordUrl == share->detectUrl)
|
||||||
{
|
{
|
||||||
recPath = detPath;
|
recPath = detPath;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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)
|
cmds.push_back("ffmpeg -hide_banner -i " + share->detectUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + detPath);
|
||||||
{
|
|
||||||
// 2 second grace period.
|
|
||||||
sleep(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (share->cmdFinished < 2)
|
recLog("fetching camera footage -- ", share);
|
||||||
{
|
|
||||||
// force close ffmpeg cmds after failing to finish
|
|
||||||
// after the grace period.
|
|
||||||
pthread_cancel(detThr);
|
|
||||||
pthread_cancel(recThr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
runTOCmds(share->clipLen, cmds, rets);
|
||||||
|
|
||||||
|
if (allCmdsDidNotFail(rets))
|
||||||
|
{
|
||||||
new thread(detectMoInFile, detPath, recPath, share);
|
new thread(detectMoInFile, detPath, recPath, share);
|
||||||
}
|
}
|
||||||
else
|
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
|
else
|
||||||
{
|
{
|
||||||
recLog("running post command: " + share->postCmd, share);
|
recLog("running post command: " + share->postCmd, share);
|
||||||
system(share->postCmd.c_str());
|
runTOCmd(14, share->postCmd, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue
Block a user