// 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" #include "logger.h" #include "web.h" void timer(shared_t *share) { while (share->retCode == 0) { sleep(1); share->postInd += 1; share->evInd += 1; } } void detectMo(shared_t *share) { while (share->retCode == 0) { sleep(2); detectMoInStream("stream.m3u8", share); } } void eventLoop(shared_t *share) { while (share->retCode == 0) { if (!share->recList.empty()) { auto event = share->recList[0]; try { recLog("attempting write out of event: " + event.evName, share); createDirTree("events"); if (wrOutVod(event, share)) { genHTMLvod(event.evName); imwrite(string("events/" + event.evName + ".jpg").c_str(), event.thumbnail); } } catch (filesystem_error &ex) { recLog(string("err: ") + ex.what(), share); } share->recList.erase(share->recList.begin()); } sleep(10); } } void upkeep(shared_t *share) { while (share->retCode == 0) { createDirTree("live"); createDirTree("events"); createDirTree("logs"); 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(); share->upkLog.clear(); initLogFrontPages(share); enforceMaxEvents(share); genHTMLul(".", share->camName, share); upkLog("camera specific webroot page updated: " + share->outDir + "/index.html", share); if (!exists("/tmp/mow-lock")) { system("touch /tmp/mow-lock"); genCSS(share); genHTMLul(share->webRoot, string(APP_NAME) + " " + string(APP_VER), share); remove("/tmp/mow-lock"); upkLog("webroot page updated: " + cleanDir(share->webRoot) + "/index.html", share); } else { upkLog("skipping update of the webroot page, it is busy.", share); } sleep(10); } } void rmLive() { if (exists("live")) { remove_all("live"); } } void recLoop(shared_t *share) { while (share->retCode == 0) { auto cmd = "ffmpeg -hide_banner -rtsp_transport tcp -timeout 3000000 -i " + share->recordUrl + " -strftime 1" + " -strftime_mkdir 1" + " -hls_segment_filename 'live/%Y-%j-%H-%M-%S.ts'" + " -hls_flags delete_segments" + " -y -vcodec copy" + " -f hls -hls_time 2 -hls_list_size 1000" + " stream.m3u8"; recLog("ffmpeg_run: " + cmd, share); rmLive(); auto retCode = system(cmd.c_str()); recLog("ffmpeg_retcode: " + to_string(retCode), share); if (retCode != 0) { recLog("err: ffmpeg returned non zero, indicating failure. please check stderr output.", share); } sleep(10); } } int main(int argc, char** argv) { struct shared_t sharedRes; sharedRes.conf = parseForParam("-c", argc, argv, false); if (parseForParam("-h", argc, argv, true) == "true") { cout << "Motion Watch " << APP_VER << endl << endl; cout << "Usage: mow " << endl << endl; cout << "-h : display usage information about this application." << endl; cout << "-c : path to the config file." << endl; cout << "-v : display the current version." << endl << endl; } else if (parseForParam("-v", argc, argv, true) == "true") { cout << APP_VER << endl; } else if (sharedRes.conf.empty()) { cerr << "err: no config file(s) were given in -c" << endl; } else { sharedRes.retCode = 0; sharedRes.maxScore = 0; sharedRes.postInd = 0; sharedRes.evInd = 0; sharedRes.skipCmd = false; rdConf(&sharedRes); auto thr1 = thread(recLoop, &sharedRes); auto thr2 = thread(upkeep, &sharedRes); auto thr3 = thread(detectMo, &sharedRes); auto thr4 = thread(eventLoop, &sharedRes); auto thr5 = thread(timer, &sharedRes); thr1.join(); thr2.join(); thr3.join(); thr4.join(); thr5.join(); return sharedRes.retCode; } return EINVAL; }