2022-09-22 20:57:46 -04:00
|
|
|
// 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 "common.h"
|
|
|
|
|
|
|
|
string cleanDir(const string &path)
|
|
|
|
{
|
|
|
|
if (path[path.size() - 1] == '/')
|
|
|
|
{
|
|
|
|
return path.substr(0, path.size() - 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool createDir(const string &dir)
|
|
|
|
{
|
|
|
|
auto ret = mkdir(dir.c_str(), 0777);
|
|
|
|
|
|
|
|
if (ret == -1)
|
|
|
|
{
|
|
|
|
return errno == EEXIST;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool createDirTree(const string &full_path)
|
|
|
|
{
|
|
|
|
size_t pos = 0;
|
|
|
|
auto ret = true;
|
|
|
|
|
|
|
|
while (ret == true && pos != string::npos)
|
|
|
|
{
|
|
|
|
pos = full_path.find('/', pos + 1);
|
|
|
|
ret = createDir(full_path.substr(0, pos));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string> lsFilesInDir(const string &path, const string &ext)
|
|
|
|
{
|
|
|
|
vector<string> names;
|
|
|
|
|
2022-12-13 21:13:10 -05:00
|
|
|
if (exists(path))
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-13 21:13:10 -05:00
|
|
|
for (auto &entry : directory_iterator(path))
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-13 21:13:10 -05:00
|
|
|
if (entry.is_regular_file())
|
2022-12-11 12:33:56 -05:00
|
|
|
{
|
2022-12-13 21:13:10 -05:00
|
|
|
auto name = entry.path().filename().string();
|
|
|
|
|
|
|
|
if (ext.empty() || name.ends_with(ext))
|
|
|
|
{
|
|
|
|
names.push_back(name);
|
|
|
|
}
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort(names.begin(), names.end());
|
|
|
|
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
2022-12-04 15:13:39 -05:00
|
|
|
vector<string> lsDirsInDir(const string &path)
|
|
|
|
{
|
|
|
|
vector<string> names;
|
|
|
|
|
2022-12-13 21:13:10 -05:00
|
|
|
if (exists(path))
|
2022-12-04 15:13:39 -05:00
|
|
|
{
|
2022-12-13 21:13:10 -05:00
|
|
|
for (auto &entry : directory_iterator(path))
|
2022-12-04 15:13:39 -05:00
|
|
|
{
|
2022-12-13 21:13:10 -05:00
|
|
|
if (entry.is_directory())
|
|
|
|
{
|
|
|
|
names.push_back(entry.path().filename().string());
|
|
|
|
}
|
2022-12-04 15:13:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort(names.begin(), names.end());
|
|
|
|
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
void enforceMaxDays(const string &dirPath, shared_t *share)
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-04 15:13:39 -05:00
|
|
|
auto names = lsDirsInDir(dirPath);
|
2022-09-22 20:57:46 -04:00
|
|
|
|
2022-12-04 15:13:39 -05:00
|
|
|
while (names.size() > (share->maxDays - 1))
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-04 15:13:39 -05:00
|
|
|
remove_all(string(cleanDir(dirPath) + "/" + names[0]).c_str());
|
2022-09-22 20:57:46 -04:00
|
|
|
|
2022-12-04 15:13:39 -05:00
|
|
|
names.erase(names.begin());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void enforceMaxClips(const string &dirPath, shared_t *share)
|
|
|
|
{
|
2022-12-16 18:24:18 -05:00
|
|
|
auto names = lsFilesInDir(dirPath, "." + share->vidExt);
|
2022-12-13 21:13:10 -05:00
|
|
|
|
2022-12-16 18:24:18 -05:00
|
|
|
while (names.size() > share->maxClips)
|
2022-12-04 15:13:39 -05:00
|
|
|
{
|
2022-12-16 18:24:18 -05:00
|
|
|
// removes the video file extension.
|
|
|
|
auto nameOnly = names[0].substr(0, names[0].size() - (share->vidExt.size() + 1));
|
|
|
|
auto imgFile = cleanDir(dirPath) + "/" + nameOnly + ".jpg";
|
|
|
|
auto webFile = cleanDir(dirPath) + "/" + nameOnly + ".html";
|
|
|
|
|
|
|
|
remove(cleanDir(dirPath) + "/" + names[0]);
|
|
|
|
|
|
|
|
if (exists(imgFile)) remove(imgFile);
|
|
|
|
if (exists(webFile)) remove(webFile);
|
2022-09-22 20:57:46 -04:00
|
|
|
|
2022-12-04 15:13:39 -05:00
|
|
|
names.erase(names.begin());
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string genTimeStr(const char *fmt)
|
|
|
|
{
|
|
|
|
time_t rawtime;
|
|
|
|
|
|
|
|
time(&rawtime);
|
|
|
|
|
|
|
|
auto timeinfo = localtime(&rawtime);
|
|
|
|
|
|
|
|
char ret[50];
|
|
|
|
|
|
|
|
strftime(ret, 50, fmt, timeinfo);
|
|
|
|
|
|
|
|
return string(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
string genDstFile(const string &dirOut, const char *fmt, const string &ext)
|
|
|
|
{
|
|
|
|
createDirTree(cleanDir(dirOut));
|
|
|
|
|
|
|
|
return cleanDir(dirOut) + string("/") + genTimeStr(fmt) + ext;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rdLine(const string ¶m, const string &line, string *value)
|
|
|
|
{
|
|
|
|
if (line.rfind(param.c_str(), 0) == 0)
|
|
|
|
{
|
|
|
|
*value = line.substr(param.size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void rdLine(const string ¶m, const string &line, int *value)
|
|
|
|
{
|
|
|
|
if (line.rfind(param.c_str(), 0) == 0)
|
|
|
|
{
|
|
|
|
*value = strtol(line.substr(param.size()).c_str(), NULL, 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-24 13:48:51 -05:00
|
|
|
bool rdConf(const string &filePath, shared_t *share)
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-24 13:48:51 -05:00
|
|
|
ifstream varFile(filePath.c_str());
|
2022-09-22 20:57:46 -04:00
|
|
|
|
|
|
|
if (!varFile.is_open())
|
|
|
|
{
|
|
|
|
share->retCode = ENOENT;
|
|
|
|
|
2022-12-24 13:48:51 -05:00
|
|
|
cout << "wrn: config file: " << filePath << " does not exists or lack read permissions." << endl;
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string line;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
getline(varFile, line);
|
|
|
|
|
|
|
|
if (line.rfind("#", 0) != 0)
|
|
|
|
{
|
2022-12-04 15:13:39 -05:00
|
|
|
rdLine("cam_name = ", line, &share->camName);
|
2022-09-22 20:57:46 -04:00
|
|
|
rdLine("recording_stream = ", line, &share->recordUrl);
|
2022-12-11 10:25:22 -05:00
|
|
|
rdLine("web_root = ", line, &share->webRoot);
|
|
|
|
rdLine("web_text = ", line, &share->webTxt);
|
|
|
|
rdLine("web_bg = ", line, &share->webBg);
|
|
|
|
rdLine("web_font = ", line, &share->webFont);
|
2022-09-22 20:57:46 -04:00
|
|
|
rdLine("post_cmd = ", line, &share->postCmd);
|
2022-12-24 13:48:51 -05:00
|
|
|
rdLine("clip_len = ", line, &share->clipLen);
|
|
|
|
rdLine("num_of_clips = ", line, &share->numOfClips);
|
2022-09-22 20:57:46 -04:00
|
|
|
rdLine("buff_dir = ", line, &share->buffDir);
|
2022-12-04 15:13:39 -05:00
|
|
|
rdLine("frame_gap = ", line, &share->frameGap);
|
|
|
|
rdLine("pix_thresh = ", line, &share->pixThresh);
|
|
|
|
rdLine("img_thresh = ", line, &share->imgThresh);
|
2022-09-22 20:57:46 -04:00
|
|
|
rdLine("max_days = ", line, &share->maxDays);
|
2022-12-04 15:13:39 -05:00
|
|
|
rdLine("max_clips = ", line, &share->maxClips);
|
2022-12-13 20:27:32 -05:00
|
|
|
rdLine("max_log_size = ", line, &share->maxLogSize);
|
2022-09-22 20:57:46 -04:00
|
|
|
rdLine("vid_container = ", line, &share->vidExt);
|
2022-12-18 10:25:46 -05:00
|
|
|
rdLine("vid_codec = ", line, &share->vidCodec);
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} while(!line.empty());
|
2022-12-24 13:48:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return share->retCode == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rdConf(shared_t *share)
|
|
|
|
{
|
|
|
|
share->recordUrl.clear();
|
|
|
|
share->postCmd.clear();
|
|
|
|
share->buffDir.clear();
|
|
|
|
share->camName.clear();
|
|
|
|
share->recLogPath.clear();
|
|
|
|
share->detLogPath.clear();
|
|
|
|
share->recLogFile.close();
|
|
|
|
share->detLogFile.close();
|
|
|
|
|
|
|
|
share->retCode = 0;
|
|
|
|
share->frameGap = 20;
|
|
|
|
share->pixThresh = 150;
|
|
|
|
share->imgThresh = 80000;
|
|
|
|
share->clipLen = 20;
|
|
|
|
share->numOfClips = 3;
|
|
|
|
share->maxDays = 15;
|
|
|
|
share->maxClips = 90;
|
|
|
|
share->maxLogSize = 50000;
|
|
|
|
share->webRoot = "/var/www/html";
|
|
|
|
share->buffDir = "/tmp";
|
|
|
|
share->vidExt = "mp4";
|
|
|
|
share->vidCodec = "copy";
|
|
|
|
share->skipCmd = false;
|
|
|
|
share->webBg = "#485564";
|
|
|
|
share->webTxt = "#dee5ee";
|
|
|
|
share->webFont = "courier";
|
|
|
|
|
|
|
|
auto ret = false;
|
|
|
|
|
|
|
|
for (auto &&confPath: share->conf)
|
|
|
|
{
|
|
|
|
if (rdConf(confPath, share)) ret = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
if (share->camName.empty())
|
|
|
|
{
|
|
|
|
share->camName = path(share->conf.back()).filename();
|
|
|
|
}
|
2022-09-22 20:57:46 -04:00
|
|
|
|
2022-12-13 20:27:32 -05:00
|
|
|
share->outDir = cleanDir(share->webRoot) + "/" + share->camName;
|
2022-12-18 10:25:46 -05:00
|
|
|
share->buffDir = cleanDir(share->buffDir) + "/" + share->camName;
|
2022-12-13 20:27:32 -05:00
|
|
|
share->recLogPath = share->outDir + "/rec_log_lines.html";
|
|
|
|
share->detLogPath = share->outDir + "/det_log_lines.html";
|
2022-12-11 10:25:22 -05:00
|
|
|
|
2022-09-22 20:57:46 -04:00
|
|
|
if (share->init)
|
|
|
|
{
|
2022-12-13 21:13:10 -05:00
|
|
|
if (exists(share->buffDir))
|
|
|
|
{
|
|
|
|
remove_all(share->buffDir);
|
|
|
|
}
|
2022-12-16 18:24:18 -05:00
|
|
|
|
2022-09-22 20:57:46 -04:00
|
|
|
share->init = false;
|
|
|
|
}
|
2022-12-11 15:06:09 -05:00
|
|
|
|
|
|
|
createDirTree(cleanDir(share->buffDir));
|
|
|
|
createDirTree(share->outDir);
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
2022-12-24 13:48:51 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
cerr << "err: none of the expected config files could be read." << endl;
|
|
|
|
}
|
2022-09-22 20:57:46 -04:00
|
|
|
|
2022-12-24 13:48:51 -05:00
|
|
|
return ret;
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
|
2022-12-24 13:48:51 -05:00
|
|
|
string parseForParam(const string &arg, int argc, char** argv, bool argOnly, int &offs)
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-24 13:48:51 -05:00
|
|
|
auto ret = string();
|
|
|
|
|
|
|
|
for (; offs < argc; ++offs)
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-24 13:48:51 -05:00
|
|
|
auto argInParams = string(argv[offs]);
|
2022-09-22 20:57:46 -04:00
|
|
|
|
|
|
|
if (arg.compare(argInParams) == 0)
|
|
|
|
{
|
|
|
|
if (!argOnly)
|
|
|
|
{
|
2022-12-24 13:48:51 -05:00
|
|
|
offs++;
|
|
|
|
// check ahead, make sure offs + 1 won't cause out-of-range exception
|
|
|
|
if (offs <= (argc - 1))
|
2022-09-22 20:57:46 -04:00
|
|
|
{
|
2022-12-24 13:48:51 -05:00
|
|
|
ret = string(argv[offs]);
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-12-24 13:48:51 -05:00
|
|
|
ret = string("true");
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-24 13:48:51 -05:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
|
|
|
|
{
|
|
|
|
auto notUsed = 0;
|
|
|
|
|
|
|
|
return parseForParam(arg, argc, argv, argOnly, notUsed);
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string> parseForList(const string &arg, int argc, char** argv)
|
|
|
|
{
|
|
|
|
auto offs = 0;
|
|
|
|
auto ret = vector<string>();
|
|
|
|
string param;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
param = parseForParam(arg, argc, argv, false, offs);
|
|
|
|
|
|
|
|
if (!param.empty())
|
|
|
|
{
|
|
|
|
ret.push_back(param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!param.empty());
|
|
|
|
|
|
|
|
return ret;
|
2022-09-22 20:57:46 -04:00
|
|
|
}
|
2022-12-21 20:30:37 -05:00
|
|
|
|
|
|
|
void waitForDetThreads(shared_t *share)
|
|
|
|
{
|
|
|
|
for (auto &&thr : share->detThreads)
|
|
|
|
{
|
|
|
|
thr.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
share->detThreads.clear();
|
|
|
|
}
|