diff --git a/README.md b/README.md index 31b61c4..b537f45 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/common.h b/src/common.h index 9fbd04d..9c05326 100644 --- a/src/common.h +++ b/src/common.h @@ -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); diff --git a/src/main.cpp b/src/main.cpp index 00985bd..b22254b 100755 --- a/src/main.cpp +++ b/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); } -static void *runDetCmd(void *arg) +static void *runCmd(void *arg) { - auto share = reinterpret_cast(arg); + auto args = static_cast(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 &resList) { - auto share = reinterpret_cast(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 &resList) +{ + for (auto &&res : resList) + { + if (!res->finished) + { + pthread_cancel(res->thr); + } + } +} + +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; + 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 cmds; + vector 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 cmds; + vector 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