diff --git a/CMakeLists.txt b/CMakeLists.txt index c62ca87..27f6928 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) project( MotionWatch ) find_package( OpenCV REQUIRED ) -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread") include_directories( ${OpenCV_INCLUDE_DIRS} ) add_executable( mow src/main.cpp ) target_link_libraries( mow ${OpenCV_LIBS} ) diff --git a/src/main.cpp b/src/main.cpp index e7cc232..18d4818 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,17 +17,17 @@ using namespace std; struct shared_t { - string detectUrl; - string recordUrl; - string outDir; - string postMoCmd; - string postNoMoCmd; - string secsStr; - string camId; - bool ffRunning; - bool motion; - int secs; - int ignore; + vector buff; + vector writers; + string detectUrl; + string recordUrl; + string outDir; + string postMoCmd; + string postNoMoCmd; + string secsStr; + bool wrRunning; + bool ffRunning; + int secs; } sharedRes; @@ -73,51 +73,49 @@ bool createDirTree(const string &full_path) void vidCap(shared_t *share) { - time_t rawtime; - - time(&rawtime); - - auto timeinfo = localtime(&rawtime); - - char dirName[20]; - char fileName[20]; - - strftime(dirName, 20, "%Y%m%d", timeinfo); - strftime(fileName, 20, "%H%M%S.ts", timeinfo); - - auto tmpFile = string("/tmp/mow/") + share->camId + ".ts"; - auto ffmpegCmd = string("ffmpeg -hide_banner -loglevel error -i ") + share->recordUrl + string(" -t ") + share->secsStr + string(" -y -vcodec copy ") + tmpFile; - - createDirTree(string("/tmp/mow")); - system(ffmpegCmd.c_str()); - - share->ffRunning = false; - - if (share->motion) + if (share->buff.size() >= 30) { - if (!share->postMoCmd.empty()) - { - system(share->postMoCmd.c_str()); - } + share->wrRunning = true; + + time_t rawtime; + + time(&rawtime); + + auto timeinfo = localtime(&rawtime); + + char dirName[20]; + char fileName[20]; + + strftime(dirName, 20, "%Y%m%d", timeinfo); + strftime(fileName, 20, "%H%M%S.avi", timeinfo); createDirTree(cleanDir(share->outDir) + string("/") + string(dirName)); auto dstPath = cleanDir(share->outDir) + string("/") + string(dirName) + string("/") + string(fileName); + auto codec = VideoWriter::fourcc('M', 'J', 'P', 'G'); + auto fps = 25.0; - system(string("mv " + tmpFile + " " + dstPath).c_str()); - } - else - { - if (!share->postNoMoCmd.empty()) + VideoWriter writer; + + writer.open(dstPath, codec, fps, share->buff[0].size(), true); + + if (!writer.isOpened()) { - system(share->postNoMoCmd.c_str()); + cerr << "could not open the output video file for writing: " << dstPath; + } + else + { + for (; !share->buff.empty(); share->buff.erase(share->buff.begin())) + { + writer.write(share->buff[0]); + } } - system(string("rm " + tmpFile).c_str()); + share->wrRunning = false; } } -void detectDiff(const Mat &prev, const Mat &next, shared_t *share) +bool detectDiff(const Mat &prev, const Mat &next, shared_t *share) { // optical flow calculations are used to detect motion. // reference: https://docs.opencv.org/3.4/d4/dee/tutorial_optical_flow.html @@ -135,14 +133,14 @@ void detectDiff(const Mat &prev, const Mat &next, shared_t *share) goodFeaturesToTrack(prev, p0, 100, 0.3, 7, Mat(), 7, false, 0.04); calcOpticalFlowPyrLK(prev, next, p0, p1, status, err, Size(10, 10), 2, criteria); - for(uint i = 0; (i < p0.size()) && !share->motion; i++) + for(uint i = 0; i < p0.size(); i++) { // select good points if(status[i] == 1) { if (count == 5) { - share->motion = true; + return true; } else if (norm(p0[i] - p1[i]) > distance) { @@ -156,36 +154,72 @@ void detectDiff(const Mat &prev, const Mat &next, shared_t *share) } } } + + return false; +} + +void timer(shared_t *share) +{ + sleep(share->secs); + + share->ffRunning = false; + + if (!share->wrRunning) + { + new thread(vidCap, share); + } } void moDetect(shared_t *share) { - auto cap = VideoCapture(share->detectUrl, CAP_FFMPEG); + auto dCap = VideoCapture(share->detectUrl, CAP_FFMPEG); + auto rCap = VideoCapture(share->recordUrl, CAP_FFMPEG); + auto mod = false; - Mat firstFrame, currentFrame, frame; + Mat dPrevFrame, dNextFrame, dFrame, rFrame; - while (share->ffRunning && !share->motion) + while (share->ffRunning) { - cap >> frame; + dCap >> dFrame; + rCap >> rFrame; - if (frame.empty()) + if (dFrame.empty()) { // broken frames returned from the cameras i've tested this with would cause // the entire capture connection to drop, hence why this bit of code is here // to detect empty frames (signs of a dropped connection) and attempt // re-connect to the cammera. - cap.open(share->detectUrl, CAP_FFMPEG); + dCap.open(share->detectUrl, CAP_FFMPEG); } - else if (firstFrame.empty()) + else if (rFrame.empty()) { - cvtColor(frame, firstFrame, COLOR_BGR2GRAY); + rCap.open(share->recordUrl, CAP_FFMPEG); + } + else if (dPrevFrame.empty()) + { + cvtColor(dFrame, dPrevFrame, COLOR_BGR2GRAY); } else { - cvtColor(frame, currentFrame, COLOR_BGR2GRAY); - detectDiff(firstFrame, currentFrame, share); + cvtColor(dFrame, dNextFrame, COLOR_BGR2GRAY); + + if (detectDiff(dPrevFrame, dNextFrame, share)) + { + mod = true; + + share->buff.push_back(rFrame.clone()); + } } } + + if (mod) + { + system(share->postMoCmd.c_str()); + } + else + { + system(share->postNoMoCmd.c_str()); + } } string parseForParam(const string &arg, int argc, char** argv) @@ -217,7 +251,6 @@ int main(int argc, char** argv) auto outDir = parseForParam("-dir", argc, argv); auto moCmd = parseForParam("-mc", argc, argv); auto noMocmd = parseForParam("-nmc", argc, argv); - auto camId = parseForParam("-id", argc, argv); auto secs = strtol(secsStr.c_str(), NULL, 10); if (lowUrl.empty()) @@ -232,16 +265,14 @@ int main(int argc, char** argv) { cerr << "the output directory is empty." << endl; } - else if (camId.empty()) - { - cerr << "the camera id is empty." << endl; - } else if (secs == 0) { cerr << "the amount of seconds in -sec cannot be 0 or an invalid number was given." << endl; } else { + sharedRes.wrRunning = false; + while (true) { sharedRes.recordUrl = highUrl; @@ -251,12 +282,9 @@ int main(int argc, char** argv) sharedRes.secsStr = secsStr; sharedRes.secs = secs; sharedRes.outDir = outDir; - sharedRes.camId = camId; sharedRes.ffRunning = true; - sharedRes.motion = false; - sharedRes.ignore = 0; - thread th1(vidCap, &sharedRes); + thread th1(timer, &sharedRes); thread th2(moDetect, &sharedRes); // Wait for the threads to finish