diff --git a/src/common.cpp b/src/common.cpp index 8291956..efa92b7 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -12,6 +12,44 @@ #include "common.h" +string leftTrim(const string &str, const string &toRemove) +{ + auto start = str.find_first_not_of(toRemove); + + if (start != string::npos) + { + return str.substr(start); + } + else + { + return str; + } +} + +string rightTrim(const string &str, const string &toRemove) +{ + auto end = str.find_last_not_of(toRemove); + + if (end != string::npos) + { + return str.substr(0, end + 1); + } + else + { + return str; + } +} + +string trim(const string &str, const string &toRemove) +{ + return rightTrim(leftTrim(str, toRemove), toRemove); +} + +string trim(const string &str) +{ + return trim(str, TRIM_REMOVE); +} + string cleanDir(const string &path) { if (path[path.size() - 1] == '/') @@ -189,6 +227,7 @@ bool rdConf(const string &filePath, shared_t *share) { rdLine("cam_name = ", line, &share->camName); rdLine("recording_stream = ", line, &share->recordUrl); + rdLine("detect_stream = ", line, &share->detectUrl); rdLine("web_root = ", line, &share->webRoot); rdLine("web_text = ", line, &share->webTxt); rdLine("web_bg = ", line, &share->webBg); @@ -216,6 +255,7 @@ bool rdConf(const string &filePath, shared_t *share) bool rdConf(shared_t *share) { share->recordUrl.clear(); + share->detectUrl.clear(); share->postCmd.clear(); share->buffDir.clear(); share->camName.clear(); @@ -241,6 +281,8 @@ bool rdConf(shared_t *share) share->webBg = "#485564"; share->webTxt = "#dee5ee"; share->webFont = "courier"; + share->detSuffix = ".det."; + share->recSuffix = ".rec."; auto ret = false; @@ -256,10 +298,18 @@ bool rdConf(shared_t *share) share->camName = path(share->conf.back()).filename(); } + if (share->detectUrl.empty()) + { + share->detectUrl = share->recordUrl; + } + share->outDir = cleanDir(share->webRoot) + "/" + share->camName; share->buffDir = cleanDir(share->buffDir) + "/" + share->camName; share->recLogPath = share->outDir + "/rec_log_lines.html"; share->detLogPath = share->outDir + "/det_log_lines.html"; + share->vidExt = trim(share->vidExt); + share->detSuffix += share->vidExt; + share->recSuffix += share->vidExt; if (share->init) { diff --git a/src/common.h b/src/common.h index c375b5f..9fbd04d 100644 --- a/src/common.h +++ b/src/common.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -35,8 +36,9 @@ using namespace cv; using namespace std; using namespace std::filesystem; -#define APP_VER "1.6.t2" -#define APP_NAME "Motion Watch" +#define APP_VER "1.6.t3" +#define APP_NAME "Motion Watch" +#define TRIM_REMOVE " \n\r\t\f\v." struct shared_t { @@ -47,9 +49,14 @@ struct shared_t string recLogPath; string detLogPath; string recordUrl; + string detectUrl; string outDir; string postCmd; string buffDir; + string recSuffix; + string detSuffix; + string recCmd; + string detCmd; string vidExt; string vidCodec; string camName; @@ -59,6 +66,7 @@ struct shared_t string webRoot; bool init; bool skipCmd; + int cmdFinished; int clipLen; int frameGap; int pixThresh; @@ -70,6 +78,10 @@ struct shared_t int retCode; }; +string leftTrim(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); string genDstFile(const string &dirOut, const char *fmt, const string &ext); string genTimeStr(const char *fmt); string cleanDir(const string &path); diff --git a/src/main.cpp b/src/main.cpp index 8aec3d4..00985bd 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,26 +13,50 @@ #include "mo_detect.h" #include "logger.h" -void detectMoInFile(const string &bufPath, shared_t *share) +void detectMoInFile(const string &detPath, const string &recPath, shared_t *share) { detLog("detect_mo_in_file() -- start", share); Mat thumbNail; - if (moDetect(bufPath, thumbNail, share)) + if (moDetect(detPath, thumbNail, share)) { share->skipCmd = true; - wrOut(bufPath, thumbNail, share); - } - else if (exists(bufPath)) - { - remove(bufPath); + wrOut(recPath, thumbNail, share); } + if (exists(detPath)) remove(detPath); + if (exists(recPath)) remove(recPath); + detLog("detect_mo_in_file() -- finished", share); } +static void *runDetCmd(void *arg) +{ + auto share = reinterpret_cast(arg); + + recLog("ffmpeg_det_run: " + share->detCmd, share); + pclose(popen(share->detCmd.c_str(), "r")); + + share->cmdFinished++; return NULL; +} + +static void *runRecCmd(void *arg) +{ + auto share = reinterpret_cast(arg); + + if (share->recordUrl != share->detectUrl) + { + // 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")); + } + + share->cmdFinished++; return NULL; +} + void recLoop(shared_t *share) { while (rdConf(share)) @@ -68,38 +92,51 @@ void recLoop(shared_t *share) for (auto i = 0; i < share->numOfClips; ++i) { - auto bufPath = cleanDir(share->buffDir) + "/" + to_string(i) + "." + share->vidExt; - auto cmd = "timeout -k 1 " + to_string(share->clipLen + 2) + " "; + auto detPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->detSuffix; + auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix; - cmd += "ffmpeg -hide_banner -i " + share->recordUrl + " -y -vcodec " + share->vidCodec + " -movflags faststart -t " + to_string(share->clipLen) + " " + bufPath; - - recLog("ffmpeg_run: " + cmd, share); - - auto retCode = system(cmd.c_str()); - - recLog("ffmpeg_retcode: " + to_string(retCode), share); - - if (retCode == 0) + if (share->recordUrl == share->detectUrl) { - recLog("detect_mo_in_file() -- started in a seperate thread.", share); + recPath = detPath; + } - share->detThreads.push_back(thread(detectMoInFile, bufPath, share)); + 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); + + if (share->cmdFinished < 2) + { + // 2 second grace period. + sleep(2); + } + + if (share->cmdFinished < 2) + { + // force close ffmpeg cmds after failing to finish + // after the grace period. + pthread_cancel(detThr); + pthread_cancel(recThr); + } + + new thread(detectMoInFile, detPath, recPath, share); } else { - recLog("ffmpeg returned non zero, indicating failure. please check stderr output.", share); - - if (exists(bufPath)) - { - remove(bufPath); - } - - sleep(share->clipLen); + recLog("could not start one or both ffmpeg cmds in seperate threads. detOk = " + to_string(detOk) + " recOk = " + to_string(recOk), share); } } - waitForDetThreads(share); - if (!share->skipCmd) { recLog("no motion detected", share);