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;