310 lines
7.1 KiB
C++
310 lines
7.1 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;
|
||
|
}
|
||
|
|
||
|
bool fileExists(const string& name)
|
||
|
{
|
||
|
return access(name.c_str(), F_OK) != -1;
|
||
|
}
|
||
|
|
||
|
void replaceAll(string &str, const string &from, const string &to)
|
||
|
{
|
||
|
if(from.empty())
|
||
|
return;
|
||
|
|
||
|
size_t startPos = 0;
|
||
|
|
||
|
while((startPos = str.find(from, startPos)) != string::npos)
|
||
|
{
|
||
|
str.replace(startPos, from.length(), to);
|
||
|
|
||
|
startPos += to.length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vector<string> lsFilesInDir(const string &path, const string &ext)
|
||
|
{
|
||
|
DIR *dir;
|
||
|
struct dirent *ent;
|
||
|
vector<string> names;
|
||
|
|
||
|
if ((dir = opendir(path.c_str())) != NULL)
|
||
|
{
|
||
|
while ((ent = readdir(dir)) != NULL)
|
||
|
{
|
||
|
auto name = string(ent->d_name);
|
||
|
|
||
|
if ((name.size() >= 4) && (ent->d_type & DT_REG))
|
||
|
{
|
||
|
if (name.substr(name.size() - 4) == ext)
|
||
|
{
|
||
|
names.push_back(name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
closedir(dir);
|
||
|
}
|
||
|
|
||
|
sort(names.begin(), names.end());
|
||
|
|
||
|
return names;
|
||
|
}
|
||
|
|
||
|
void enforceMaxDays(shared_t *share)
|
||
|
{
|
||
|
auto names = lsFilesInDir(share->outDir, ".m3u");
|
||
|
|
||
|
while (names.size() > share->maxDays)
|
||
|
{
|
||
|
auto name = names[0];
|
||
|
auto plsFile = cleanDir(share->outDir) + "/" + name;
|
||
|
auto vidFold = cleanDir(share->outDir) + "/." + name.substr(0, name.size() - 4);
|
||
|
|
||
|
remove(plsFile.c_str());
|
||
|
remove_all(vidFold.c_str());
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
string genTmpFile(const string &dirOut, const string &ext, shared_t *share)
|
||
|
{
|
||
|
createDirTree(cleanDir(dirOut));
|
||
|
|
||
|
if (share->tmpId == 9999999)
|
||
|
{
|
||
|
share->tmpId = 0;
|
||
|
}
|
||
|
|
||
|
return cleanDir(dirOut) + string("/") + to_string(share->tmpId++) + ext;
|
||
|
}
|
||
|
|
||
|
Mat toGray(const Mat &src)
|
||
|
{
|
||
|
Mat ret;
|
||
|
|
||
|
cvtColor(src, ret, COLOR_BGR2GRAY);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void rdLine(const string ¶m, const string &line, string *value)
|
||
|
{
|
||
|
if (line.rfind(param.c_str(), 0) == 0)
|
||
|
{
|
||
|
*value = line.substr(param.size());
|
||
|
|
||
|
//cout << param << *value << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
|
||
|
//cout << param << *value << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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->outDir.clear();
|
||
|
share->postCmd.clear();
|
||
|
share->buffDir.clear();
|
||
|
|
||
|
share->colorThresh = 5;
|
||
|
share->secs = 60;
|
||
|
share->blockX = 32;
|
||
|
share->blockY = 32;
|
||
|
share->blockThresh = 900;
|
||
|
share->maxDays = 5;
|
||
|
share->vidExt = "mp4";
|
||
|
share->recLoopWait = false;
|
||
|
share->skipCmd = false;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
getline(varFile, line);
|
||
|
|
||
|
if (line.rfind("#", 0) != 0)
|
||
|
{
|
||
|
rdLine("recording_stream = ", line, &share->recordUrl);
|
||
|
rdLine("output_dir = ", line, &share->outDir);
|
||
|
rdLine("post_cmd = ", line, &share->postCmd);
|
||
|
rdLine("color_threshold = ", line, &share->colorThresh);
|
||
|
rdLine("duration = ", line, &share->secs);
|
||
|
rdLine("buff_dir = ", line, &share->buffDir);
|
||
|
rdLine("block_x = ", line, &share->blockX);
|
||
|
rdLine("block_y = ", line, &share->blockY);
|
||
|
rdLine("block_threshold = ", line, &share->blockThresh);
|
||
|
rdLine("max_days = ", line, &share->maxDays);
|
||
|
rdLine("vid_container = ", line, &share->vidExt);
|
||
|
}
|
||
|
|
||
|
} while(!line.empty());
|
||
|
|
||
|
if (share->init)
|
||
|
{
|
||
|
remove_all(share->buffDir.c_str());
|
||
|
|
||
|
share->init = false;
|
||
|
}
|
||
|
|
||
|
new thread(enforceMaxDays, share);
|
||
|
|
||
|
share->retCode = 0;
|
||
|
}
|
||
|
|
||
|
varFile.close();
|
||
|
|
||
|
return share->retCode == 0;
|
||
|
}
|
||
|
|
||
|
bool capPair(Mat &prev, Mat &next, VideoCapture &capture, shared_t *share)
|
||
|
{
|
||
|
capture >> prev;
|
||
|
capture >> next;
|
||
|
|
||
|
return !prev.empty() && !next.empty();
|
||
|
}
|
||
|
|
||
|
void wrOut(const string &buffFile, shared_t *share)
|
||
|
{
|
||
|
auto clnDir = cleanDir(share->outDir);
|
||
|
auto vidOut = genDstFile(clnDir + "/." + genTimeStr("%Y-%m-%d"), "%H%M%S", "." + share->vidExt);
|
||
|
auto m3uOut = vidOut.substr(clnDir.size() + 1);
|
||
|
auto lisOut = genDstFile(share->outDir, "%Y-%m-%d", ".m3u");
|
||
|
|
||
|
copy_file(buffFile.c_str(), vidOut.c_str());
|
||
|
remove(buffFile.c_str());
|
||
|
|
||
|
ofstream file;
|
||
|
|
||
|
if (fileExists(lisOut))
|
||
|
{
|
||
|
file.open(lisOut.c_str(), ios_base::app);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
file.open(lisOut.c_str());
|
||
|
}
|
||
|
|
||
|
file << m3uOut << endl;
|
||
|
|
||
|
file.close();
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
}
|