c153fdcd21
Somewhere in the code is causing an infinite loop, root cause still undermined. added more logging statements to help me find misbehavior. imgDiff() will now handle empty frames on the parameters more gracefully. enforceMaxClips() will no longer assume all video clips are accompanied by html and jpg files but will now instead "delete if exists."
283 lines
7.3 KiB
C++
283 lines
7.3 KiB
C++
// 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;
|
|
|
|
if (exists(path))
|
|
{
|
|
for (auto &entry : directory_iterator(path))
|
|
{
|
|
if (entry.is_regular_file())
|
|
{
|
|
auto name = entry.path().filename().string();
|
|
|
|
if (ext.empty() || name.ends_with(ext))
|
|
{
|
|
names.push_back(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sort(names.begin(), names.end());
|
|
|
|
return names;
|
|
}
|
|
|
|
vector<string> lsDirsInDir(const string &path)
|
|
{
|
|
vector<string> names;
|
|
|
|
if (exists(path))
|
|
{
|
|
for (auto &entry : directory_iterator(path))
|
|
{
|
|
if (entry.is_directory())
|
|
{
|
|
names.push_back(entry.path().filename().string());
|
|
}
|
|
}
|
|
}
|
|
|
|
sort(names.begin(), names.end());
|
|
|
|
return names;
|
|
}
|
|
|
|
void enforceMaxDays(const string &dirPath, shared_t *share)
|
|
{
|
|
auto names = lsDirsInDir(dirPath);
|
|
|
|
while (names.size() > (share->maxDays - 1))
|
|
{
|
|
remove_all(string(cleanDir(dirPath) + "/" + names[0]).c_str());
|
|
|
|
names.erase(names.begin());
|
|
}
|
|
}
|
|
|
|
void enforceMaxClips(const string &dirPath, shared_t *share)
|
|
{
|
|
auto names = lsFilesInDir(dirPath, "." + share->vidExt);
|
|
|
|
while (names.size() > share->maxClips)
|
|
{
|
|
// 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);
|
|
|
|
names.erase(names.begin());
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
bool rdConf(shared_t *share)
|
|
{
|
|
ifstream varFile(share->conf.c_str());
|
|
|
|
if (!varFile.is_open())
|
|
{
|
|
share->retCode = ENOENT;
|
|
|
|
cerr << "err: Failed to open the config file: " << share->conf << " for reading. please check file permissions or if it exists." << endl;
|
|
}
|
|
else
|
|
{
|
|
string line;
|
|
|
|
share->recordUrl.clear();
|
|
share->postCmd.clear();
|
|
share->buffDir.clear();
|
|
share->recLogPath.clear();
|
|
share->detLogPath.clear();
|
|
share->recLogFile.close();
|
|
share->detLogFile.close();
|
|
|
|
share->retCode = 0;
|
|
share->frameGap = 10;
|
|
share->pixThresh = 150;
|
|
share->imgThresh = 1024;
|
|
share->secs = 60;
|
|
share->maxDays = 15;
|
|
share->maxClips = 30;
|
|
share->maxLogSize = 50000;
|
|
share->camName = path(share->conf.c_str()).filename();
|
|
share->webRoot = "/var/www/html";
|
|
share->vidExt = "mp4";
|
|
share->recLoopWait = false;
|
|
share->skipCmd = false;
|
|
share->webBg = "#485564";
|
|
share->webTxt = "#dee5ee";
|
|
share->webFont = "courier";
|
|
|
|
do
|
|
{
|
|
getline(varFile, line);
|
|
|
|
if (line.rfind("#", 0) != 0)
|
|
{
|
|
rdLine("cam_name = ", line, &share->camName);
|
|
rdLine("recording_stream = ", line, &share->recordUrl);
|
|
rdLine("web_root = ", line, &share->webRoot);
|
|
rdLine("web_text = ", line, &share->webTxt);
|
|
rdLine("web_bg = ", line, &share->webBg);
|
|
rdLine("web_font = ", line, &share->webFont);
|
|
rdLine("post_cmd = ", line, &share->postCmd);
|
|
rdLine("duration = ", line, &share->secs);
|
|
rdLine("buff_dir = ", line, &share->buffDir);
|
|
rdLine("frame_gap = ", line, &share->frameGap);
|
|
rdLine("pix_thresh = ", line, &share->pixThresh);
|
|
rdLine("img_thresh = ", line, &share->imgThresh);
|
|
rdLine("max_days = ", line, &share->maxDays);
|
|
rdLine("max_clips = ", line, &share->maxClips);
|
|
rdLine("max_log_size = ", line, &share->maxLogSize);
|
|
rdLine("vid_container = ", line, &share->vidExt);
|
|
}
|
|
|
|
} while(!line.empty());
|
|
|
|
share->outDir = cleanDir(share->webRoot) + "/" + share->camName;
|
|
share->recLogPath = share->outDir + "/rec_log_lines.html";
|
|
share->detLogPath = share->outDir + "/det_log_lines.html";
|
|
|
|
if (share->init)
|
|
{
|
|
if (exists(share->buffDir))
|
|
{
|
|
remove_all(share->buffDir);
|
|
}
|
|
|
|
share->init = false;
|
|
}
|
|
|
|
createDirTree(cleanDir(share->buffDir));
|
|
createDirTree(share->outDir);
|
|
}
|
|
|
|
varFile.close();
|
|
|
|
return share->retCode == 0;
|
|
}
|
|
|
|
string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
|
|
{
|
|
for (int i = 0; i < argc; ++i)
|
|
{
|
|
auto argInParams = string(argv[i]);
|
|
|
|
if (arg.compare(argInParams) == 0)
|
|
{
|
|
if (!argOnly)
|
|
{
|
|
// check ahead, make sure i + 1 won't cause out-of-range exception
|
|
if ((i + 1) <= (argc - 1))
|
|
{
|
|
return string(argv[i + 1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return string("true");
|
|
}
|
|
}
|
|
}
|
|
|
|
return string();
|
|
}
|