From 83b206c06cc6089a597275db762f8d88ed51ba68 Mon Sep 17 00:00:00 2001 From: Zii Date: Sun, 26 Mar 2023 10:32:56 -0400 Subject: [PATCH] v2.0.t13 Fixed the crashing issue by adding tcp timeout args to ffmpeg and having the app handle empty frames from a disconnected camera better. Reformed the directory structure by having live, logs and events in seperate directories. schLoop() no longer exists, postCmd is now handled by detectMoInStream() to ensure motion detection is not done while the command is running. --- src/common.cpp | 17 +++++---- src/common.h | 5 ++- src/logger.cpp | 12 +++--- src/logger.h | 2 +- src/main.cpp | 94 ++++++++++++++++++----------------------------- src/mo_detect.cpp | 71 +++++++++++++++++++++++------------ src/mo_detect.h | 4 +- src/web.cpp | 54 ++++++++++++--------------- 8 files changed, 127 insertions(+), 132 deletions(-) mode change 100755 => 100644 src/main.cpp diff --git a/src/common.cpp b/src/common.cpp index 0546e74..fee254f 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -134,18 +134,19 @@ void cleanupStream(const string &plsPath) void enforceMaxEvents(shared_t *share) { - auto names = lsFilesInDir(".", ".mp4"); + auto names = lsFilesInDir("events", ".mp4"); while (names.size() > share->maxEvents) { - // removes the playlist file extension. - auto nameOnly = names[0].substr(0, names[0].size() - 4); - auto imgFile = nameOnly + ".jpg"; - auto webFile = nameOnly + ".html"; + // removes the video file extension (.mp4). + auto nameOnly = "events/" + names[0].substr(0, names[0].size() - 4); + auto mp4File = nameOnly + string(".mp4"); + auto imgFile = nameOnly + string(".jpg"); + auto webFile = nameOnly + string(".html"); - if (exists(names[0])) remove(names[0]); - if (exists(imgFile)) remove(imgFile); - if (exists(webFile)) remove(webFile); + if (exists(mp4File)) remove(mp4File); + if (exists(imgFile)) remove(imgFile); + if (exists(webFile)) remove(webFile); names.erase(names.begin()); } diff --git a/src/common.h b/src/common.h index 0a382b4..c51abbb 100644 --- a/src/common.h +++ b/src/common.h @@ -38,7 +38,7 @@ using namespace std; using namespace std::filesystem; using namespace std::chrono; -#define APP_VER "2.0.t12" +#define APP_VER "2.0.t13" #define APP_NAME "Motion Watch" #define REC_LOG_NAME "rec_log_lines.html" #define DET_LOG_NAME "det_log_lines.html" @@ -67,8 +67,9 @@ struct shared_t string webTxt; string webFont; string webRoot; + mutex detMutex; bool skipCmd; - bool postCmdRunning; + int procTime; int schSec; int frameGap; int pixThresh; diff --git a/src/logger.cpp b/src/logger.cpp index de68b2a..996690d 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -38,7 +38,7 @@ void enforceMaxLogSize(const string &filePath, shared_t *share) } } -void dumpLogs(const char *fileName, const string &lines) +void dumpLogs(const string &fileName, const string &lines) { if (!lines.empty()) { @@ -46,11 +46,11 @@ void dumpLogs(const char *fileName, const string &lines) if (exists(fileName)) { - outFile.open(fileName, ofstream::app); + outFile.open(fileName.c_str(), ofstream::app); } else { - outFile.open(fileName); + outFile.open(fileName.c_str()); } outFile << lines; @@ -116,7 +116,7 @@ void initLogFrontPage(const string &filePath, const string &logLinesFile) void initLogFrontPages(shared_t *share) { - initLogFrontPage("recording_log.html", REC_LOG_NAME); - initLogFrontPage("detection_log.html", DET_LOG_NAME); - initLogFrontPage("upkeep_log.html", UPK_LOG_NAME); + initLogFrontPage("logs/recording_log.html", REC_LOG_NAME); + initLogFrontPage("logs/detection_log.html", DET_LOG_NAME); + initLogFrontPage("logs/upkeep_log.html", UPK_LOG_NAME); } diff --git a/src/logger.h b/src/logger.h index 28c60f4..df7c82a 100644 --- a/src/logger.h +++ b/src/logger.h @@ -18,7 +18,7 @@ void recLog(const string &line, shared_t *share); void detLog(const string &line, shared_t *share); void upkLog(const string &line, shared_t *share); -void dumpLogs(const char *fileName, const string &lines); +void dumpLogs(const string &fileName, const string &lines); void enforceMaxLogSize(const string &filePath, shared_t *share); void initLogFrontPages(shared_t *share); diff --git a/src/main.cpp b/src/main.cpp old mode 100755 new mode 100644 index 055a27a..c4b8d72 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,24 +27,26 @@ void eventLoop(shared_t *share) { while (share->retCode == 0) { - for (auto it = share->recList.begin(); !share->recList.empty();) + while (!share->recList.empty()) { + auto it = share->recList.begin(); auto evName = it->first; auto event = it->second; auto timeDiff = genEpoch() - event.createTime; - // wait at least 61 seconds before processing the event in + // wait at least 62 seconds before processing the event in // queue. - if ((timeDiff > 0) && (timeDiff > 60)) + if ((timeDiff > 0) && (timeDiff > 62)) { try { + createDirTree("events"); wrOutVod(event, share); genHTMLvod(evName); - if (!exists(evName + ".jpg")) + if (!exists("events/" + evName + ".jpg")) { - imwrite(string(evName + ".jpg").c_str(), event.thumbnail); + imwrite(string("events/" + evName + ".jpg").c_str(), event.thumbnail); } } catch (filesystem_error &ex) @@ -54,50 +56,29 @@ void eventLoop(shared_t *share) share->recList.erase(it); } + + sleep(5); } sleep(5); } } -void schLoop(shared_t *share) -{ - if (!share->postCmd.empty()) - { - while (share->retCode == 0) - { - sleep(share->schSec); - - if (!share->skipCmd) - { - share->postCmdRunning = true; - - detLog("no motion detected, running post command: " + share->postCmd, share); - system(share->postCmd.c_str()); - - share->postCmdRunning = false; - } - else - { - share->skipCmd = false; - - detLog("motion detected, skipping the post command.", share); - } - } - } -} - void upkeep(shared_t *share) { while (share->retCode == 0) { - enforceMaxLogSize(REC_LOG_NAME, share); - enforceMaxLogSize(DET_LOG_NAME, share); - enforceMaxLogSize(UPK_LOG_NAME, share); + createDirTree("live"); + createDirTree("events"); + createDirTree("logs"); - dumpLogs(REC_LOG_NAME, share->recLog); - dumpLogs(DET_LOG_NAME, share->detLog); - dumpLogs(UPK_LOG_NAME, share->upkLog); + enforceMaxLogSize(string("logs/") + REC_LOG_NAME, share); + enforceMaxLogSize(string("logs/") + DET_LOG_NAME, share); + enforceMaxLogSize(string("logs/") + UPK_LOG_NAME, share); + + dumpLogs(string("logs/") + REC_LOG_NAME, share->recLog); + dumpLogs(string("logs/") + DET_LOG_NAME, share->detLog); + dumpLogs(string("logs/") + UPK_LOG_NAME, share->upkLog); share->recLog.clear(); share->detLog.clear(); @@ -105,7 +86,6 @@ void upkeep(shared_t *share) initLogFrontPages(share); enforceMaxEvents(share); - cleanupEmptyDirs("VIDEO_TS"); genHTMLul(".", share->camName, share); @@ -134,14 +114,19 @@ void recLoop(shared_t *share) { while (share->retCode == 0) { - auto cmd = "ffmpeg -hide_banner -i " + + if (exists("live")) + { + remove_all("live"); + } + + auto cmd = "ffmpeg -hide_banner -rtsp_transport tcp -timeout 3000000 -i " + share->recordUrl + " -strftime 1" + " -strftime_mkdir 1" + - " -hls_segment_filename 'VIDEO_TS/live/%Y/%j/%H/%M%S.ts'" + + " -hls_segment_filename 'live/%Y-%j-%H-%M-%S.ts'" + + " -hls_flags delete_segments" + " -y -vcodec copy" + - " -f hls -hls_time 6 -hls_list_size " + - to_string((share->maxDays * 86400) / 6) + // 86400 seconds in a day. + " -f hls -hls_time 10 -hls_list_size 400" + " stream.m3u8"; recLog("ffmpeg_run: " + cmd, share); @@ -155,7 +140,7 @@ void recLoop(shared_t *share) recLog("err: ffmpeg returned non zero, indicating failure. please check stderr output.", share); } - sleep(60); + sleep(10); } } @@ -183,28 +168,21 @@ int main(int argc, char** argv) } else { - sharedRes.retCode = 0; - sharedRes.skipCmd = false; - sharedRes.postCmdRunning = false; + sharedRes.retCode = 0; + sharedRes.procTime = 0; + sharedRes.skipCmd = false; rdConf(&sharedRes); - if (exists("VIDEO_TS/live")) - { - remove_all("VIDEO_TS/live"); - } - auto thr1 = thread(recLoop, &sharedRes); auto thr2 = thread(upkeep, &sharedRes); - //auto thr3 = thread(detectMo, &sharedRes); - //auto thr4 = thread(eventLoop, &sharedRes); - auto thr5 = thread(schLoop, &sharedRes); + auto thr3 = thread(detectMo, &sharedRes); + auto thr4 = thread(eventLoop, &sharedRes); thr1.join(); thr2.join(); - //thr3.join(); - //thr4.join(); - thr5.join(); + thr3.join(); + thr4.join(); return sharedRes.retCode; } diff --git a/src/mo_detect.cpp b/src/mo_detect.cpp index bb56cd7..13d4cd7 100644 --- a/src/mo_detect.cpp +++ b/src/mo_detect.cpp @@ -12,17 +12,31 @@ #include "mo_detect.h" -void detectMoInStream(const string &bufPath, shared_t *share) +void detectMoInStream(const string &streamFile, shared_t *share) { - ifstream fileIn(bufPath); + if (share->procTime >= share->schSec) + { + if (!share->skipCmd) + { + detLog("no motion detected, running post command: " + share->postCmd, share); + system(share->postCmd.c_str()); + } + else + { + share->skipCmd = false; + share->procTime = 0; + + detLog("motion detected, skipping the post command.", share); + } + } + + ifstream fileIn(streamFile); string tsPath; Mat thumbnail; - auto clipPathFilter = genTimeStr("VIDEO_TS/live/%Y/%j/%H/"); - for (string line; getline(fileIn, line); ) { - if (line.starts_with(clipPathFilter)) + if (line.starts_with("live/")) { tsPath = line; } @@ -53,31 +67,35 @@ void detectMoInStream(const string &bufPath, shared_t *share) share->skipCmd = true; } + + share->procTime += 10; } } -bool imgDiff(Mat &prev, Mat &next, shared_t *share) +bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share) { - auto ret = false; + Mat prevGray; + Mat nextGray; - cvtColor(prev, prev, COLOR_BGR2GRAY); - cvtColor(next, next, COLOR_BGR2GRAY); + cvtColor(prev, prevGray, COLOR_BGR2GRAY); + cvtColor(next, nextGray, COLOR_BGR2GRAY); Mat diff; - absdiff(prev, next, diff); + absdiff(prevGray, nextGray, diff); threshold(diff, diff, share->pixThresh, 255, THRESH_BINARY); - auto diffScore = countNonZero(diff); + score = countNonZero(diff); - detLog("diff_score: " + to_string(diffScore), share); + detLog("diff_score: " + to_string(score) + " tresh: " + to_string(share->imgThresh), share); - return diffScore >= share->imgThresh; + return score >= share->imgThresh; } bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share) { - auto mod = false; + auto maxScore = 0; + auto score = 0; detLog("stream_clip: " + buffFile, share); @@ -85,8 +103,8 @@ bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share) if (capture.isOpened()) { - Mat prev; - Mat next; + Mat prev; + Mat next; detLog("capture open successful.", share); @@ -100,13 +118,18 @@ bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share) { capture.retrieve(next); - if (!share->postCmdRunning) - { - if (imgDiff(prev, next, share)) - { - resize(next, vidThumb, Size(720, 480), INTER_LINEAR); + lock_guard guard(share->detMutex); - mod = true; break; + if (!next.empty()) + { + if (imgDiff(prev, next, score, share)) + { + if (score > maxScore) + { + maxScore = score; + + resize(next, vidThumb, Size(720, 480), INTER_LINEAR); + } } } @@ -124,7 +147,7 @@ bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share) capture.release(); - return mod; + return maxScore > 0; } void wrOutVod(const pls_t &event, shared_t *share) @@ -140,6 +163,6 @@ void wrOutVod(const pls_t &event, shared_t *share) file.close(); - system(string("ffmpeg -f concat -safe 0 -i " + concat + " -c copy " + event.evName + ".mp4").c_str()); + system(string("ffmpeg -f concat -safe 0 -i " + concat + " -c copy events/" + event.evName + ".mp4").c_str()); remove(concat); } diff --git a/src/mo_detect.h b/src/mo_detect.h index d3998a4..f148db2 100644 --- a/src/mo_detect.h +++ b/src/mo_detect.h @@ -16,9 +16,9 @@ #include "common.h" #include "logger.h" -bool imgDiff(Mat &prev, Mat &next, shared_t *share); +bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share); bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share); -void detectMoInStream(const string &bufPath, shared_t *share); +void detectMoInStream(const string &streamFile, shared_t *share); void wrOutVod(const pls_t &pls, shared_t *share); #endif // MO_DETECT_H diff --git a/src/web.cpp b/src/web.cpp index 53f9f72..41a582f 100644 --- a/src/web.cpp +++ b/src/web.cpp @@ -15,8 +15,8 @@ void genHTMLul(const string &outputDir, const string &title, shared_t *share) { vector logNames; - vector regNames = lsFilesInDir(outputDir); - vector dirNames = lsDirsInDir(outputDir); + vector eveNames; + vector dirNames; string htmlText = "\n"; @@ -31,20 +31,23 @@ void genHTMLul(const string &outputDir, const string &title, shared_t *share) htmlText += "\n"; htmlText += "

" + title + "

\n"; - if (!dirNames.empty() && !exists(outputDir + "/VIDEO_TS")) + if (exists(outputDir + "/live")) { + eveNames = lsFilesInDir(outputDir + "/events", ".html"); + logNames = lsFilesInDir(outputDir + "/logs", "_log.html"); + + htmlText += "

Logs

\n"; htmlText += "
    \n"; - for (auto &&dirName : dirNames) + for (auto &&logName : logNames) { - htmlText += "
  • " + dirName + "
  • \n"; + // name.substr(0, name.size() - 9) removes _log.html + auto name = logName.substr(0, logName.size() - 9); + + htmlText += "
  • " + name + "
  • \n"; } htmlText += "
\n"; - } - - if (exists(outputDir + "/VIDEO_TS")) - { htmlText += "

Live

\n"; htmlText += "
    \n"; htmlText += "
  • " + share->camName + ":live" + "
  • \n"; @@ -52,35 +55,24 @@ void genHTMLul(const string &outputDir, const string &title, shared_t *share) htmlText += "

    Motion Events

    \n"; genHTMLstream("stream"); - } - for (auto &®Name : regNames) - { - if (regName.ends_with("_log.html")) - { - logNames.push_back(regName); - } - else if (regName.ends_with(".html") && - !regName.ends_with("index.html") && - !regName.ends_with("stream.html") && - !regName.ends_with("_log_lines.html")) + for (auto &&eveName : eveNames) { // regName.substr(0, regName.size() - 5) removes .html - auto name = regName.substr(0, regName.size() - 5); + auto name = eveName.substr(0, eveName.size() - 5); - htmlText += "\n"; + htmlText += "\n"; } } - - if (!logNames.empty()) + else { - htmlText += "

    Logs

    \n"; + dirNames = lsDirsInDir(outputDir); + htmlText += "
    \n"; @@ -119,7 +111,7 @@ void genHTMLstream(const string &name) htmlText += " var hls = new Hls({\n"; htmlText += " debug: true,\n"; htmlText += " });\n"; - htmlText += " hls.loadSource('stream.m3u8');\n"; + htmlText += " hls.loadSource('" + name + ".m3u8');\n"; htmlText += " hls.attachMedia(video);\n"; htmlText += " hls.on(Hls.Events.MEDIA_ATTACHED, function () {\n"; htmlText += " video.muted = true;\n"; @@ -127,7 +119,7 @@ void genHTMLstream(const string &name) htmlText += " });\n"; htmlText += " }\n"; htmlText += " else if (video.canPlayType('application/vnd.apple.mpegurl')) {\n"; - htmlText += " video.src = 'stream.m3u8';\n"; + htmlText += " video.src = '" + name + ".m3u8';\n"; htmlText += " video.addEventListener('canplay', function () {\n"; htmlText += " video.play();\n"; htmlText += " });\n"; @@ -162,7 +154,7 @@ void genHTMLvod(const string &name) htmlText += "\n"; htmlText += ""; - ofstream file(string(name + ".html").c_str()); + ofstream file(string("events/" + name + ".html").c_str()); file << htmlText << endl;