// This file is part of Motion Watch. // Motion Watch is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Motion Watch is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. #include "mo_detect.h" void detectMoInStream(const string &streamFile, shared_t *share) { if (share->postInd >= share->postSecs) { if (!share->postCmd.empty()) { detLog("---POST_BREAK---", share); if (!share->skipCmd) { detLog("no motion detected, running post command: " + share->postCmd, share); system(share->postCmd.c_str()); } else { share->skipCmd = false; detLog("motion detected, skipping the post command.", share); } } share->postInd = 0; } if (share->evInd >= share->evMaxSecs) { detLog("---EVENT_BREAK---", share); if (!share->curEvent.srcPaths.empty()) { share->curEvent.evName = genEventName(share->maxScore); share->recList.push_back(share->curEvent); detLog("motion detected in " + to_string(share->curEvent.srcPaths.size()) + " file(s) in " + to_string(share->evMaxSecs) + " secs", share); detLog("all video clips queued for event generation under event name: " + share->curEvent.evName, share); } else { detLog("no motion detected in all files. none queued for event generation.", share); } share->curEvent.srcPaths.clear(); share->curEvent.evName.clear(); share->curEvent.thumbnail.release(); share->evInd = 0; share->maxScore = 0; } ifstream fileIn(streamFile); string tsPath; for (string line; getline(fileIn, line); ) { if (line.starts_with("live/")) { tsPath = line; } } if (!tsPath.empty()) { if (moDetect(tsPath, share)) { share->curEvent.srcPaths.push_back(tsPath); share->skipCmd = true; } } } bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share) { Mat prevGray; Mat nextGray; cvtColor(prev, prevGray, COLOR_BGR2GRAY); cvtColor(next, nextGray, COLOR_BGR2GRAY); Mat diff; absdiff(prevGray, nextGray, diff); threshold(diff, diff, share->pixThresh, 255, THRESH_BINARY); score = countNonZero(diff); detLog("diff_score: " + to_string(score) + " thresh: " + to_string(share->imgThresh), share); return score >= share->imgThresh; } bool moDetect(const string &buffFile, shared_t *share) { auto score = 0; auto mod = false; detLog("stream_clip: " + buffFile, share); VideoCapture capture; if (!capture.open(buffFile.c_str(), CAP_FFMPEG)) { usleep(500); capture.open(buffFile.c_str(), CAP_FFMPEG); } if (capture.isOpened()) { Mat prev; Mat next; int fps = capture.get(cv::CAP_PROP_FPS); for (auto gap = 0, frm = fps; capture.grab(); ++gap, ++frm) { if (frm == fps) sleep(1); frm = 1; if (prev.empty()) { capture.retrieve(prev); } else if (gap == (share->frameGap - 1)) { capture.retrieve(next); if (!next.empty()) { if (imgDiff(prev, next, score, share)) { mod = true; if (share->maxScore <= score) { share->maxScore = score; resize(next, share->curEvent.thumbnail, Size(720, 480), INTER_LINEAR); } } } prev = next.clone(); gap = 0; next.release(); } else { capture.grab(); } } } else { detLog("err: failed to open: " + buffFile + " after 500 msecs. giving up.", share); } capture.release(); return mod; } bool wrOutVod(const evt_t &event, shared_t *share) { auto cnt = 0; auto concat = event.evName + ".tmp"; ofstream file(concat.c_str()); for (auto i = 0; i < event.srcPaths.size(); ++i) { recLog("event_src: " + event.srcPaths[i], share); if (exists(event.srcPaths[i])) { file << "file '" << event.srcPaths[i] << "''" << endl; cnt++; } } file.close(); if (cnt == 0) { recLog("err: none of the event hls clips exists, canceling write out.", share); if (exists(concat)) remove(concat); return false; } else { auto ret = system(string("ffmpeg -f concat -safe 0 -i " + concat + " -c copy events/" + event.evName + ".mp4").c_str()); if (ret != 0) { recLog("err: ffmpeg concat failure, canceling write out.", share); } if (exists(concat)) remove(concat); return ret == 0; } }