v1.1 Update
major changes to the motion detection scheme and re-introduced multi-threading. this further sped up the motion detection to a point that it can now be called in line with the recording loop without loosing any extra camera footage due to heavy cpu usage. pixels are now read in blocks to further increase efficiency and to filter out movements of small objects. the footage clip size is now hard coded to 3 seconds instead of it being external adjustable. changed the way footage with motion is now stored. its now down to single level files with the current date. if footage of the same date already exists, new footage will be appended to it. the version number shall be updated going forward.
This commit is contained in:
parent
a36d4e93c0
commit
48e55b9721
66
README.md
66
README.md
|
@ -30,50 +30,49 @@ parameters supported and descriptions of each parameter.
|
||||||
#
|
#
|
||||||
recording_stream = rtsp://1.2.3.4:554/h264
|
recording_stream = rtsp://1.2.3.4:554/h264
|
||||||
# this is the url to the main stream of the IP camera that will be used
|
# this is the url to the main stream of the IP camera that will be used
|
||||||
# to record footage if it contains motion.
|
# to record footage.
|
||||||
#
|
|
||||||
detection_stream = rtsp://1.2.3.4:554/h264cif
|
|
||||||
# this is the low resolution secondary stream url of the IP camera the
|
|
||||||
# will be used to detect motion. it is never recorded. note: consider
|
|
||||||
# matching the fps of both streams for best results.
|
|
||||||
#
|
#
|
||||||
output_dir = /path/to/footage/directory
|
output_dir = /path/to/footage/directory
|
||||||
# this is the output directory that will be used to store recorded footage
|
# this is the output directory that will be used to store recorded footage
|
||||||
# from the camera. the file naming convention uses date codes. it creates
|
# from the camera. the file naming convention uses the current date. if
|
||||||
# a sub-folder for the date if it needs to and then stores the video file
|
# the file already exists, new footage is appended to it.
|
||||||
# using the time.
|
|
||||||
#
|
#
|
||||||
diff_verbose = N
|
buff_dir = /tmp/ramdisk/cam_name
|
||||||
# this is a boolean Y or N option that turns on/off the option to output
|
# this application records small clips of the footage from the camera and
|
||||||
# the pixel diff values that the application is reading from the camera in
|
# then stores them into this directory. any clips with motion detected in
|
||||||
# real time out into stdout. this is useful for determining the best value
|
# them are moved to output_dir, if no motion they are deleted. highly
|
||||||
# to use in pix_threshold, color_threshold or consec_threshold.
|
# recommend to use a ramdisk tempfs for this since this directory is used
|
||||||
|
# for lots of writes.
|
||||||
#
|
#
|
||||||
pix_threshold = 8
|
color_threshold = 8
|
||||||
# this application detects motion by loading back to back frames from the
|
|
||||||
# detection stream and then compares the color spectrum levels of each
|
|
||||||
# pixel of those frames. if the levels are significantly different, that
|
|
||||||
# will maybe considered motion. this threshold indicates how many pixels
|
|
||||||
# in the image needs to be different before triggering a potential motion
|
|
||||||
# event.
|
|
||||||
#
|
|
||||||
color_threshold = 190
|
|
||||||
# the color levels in each pixel of the detection stream can range from
|
# the color levels in each pixel of the detection stream can range from
|
||||||
# 0-255. in an ideal world the color differences in between frames should
|
# 0-255. in an ideal world the color differences in between frames should
|
||||||
# be 0 if there is no motion but must cameras can't do this. the threshold
|
# be 0 if there is no motion but must cameras can't do this. the threshold
|
||||||
# value here is used to filter if the pixels are truly different or if its
|
# value here is used to filter if the pixels are truly different or if its
|
||||||
# seeing color differences of small objects that are of no interest.
|
# seeing color differences of small objects that are of no interest.
|
||||||
#
|
#
|
||||||
consec_threshold = 10
|
block_threshold = 3456
|
||||||
# this setting is used to tell the application how many consecutive frames
|
# this application detects motion by loading frames from the camera and
|
||||||
# need to have pixel differences over the pix_threshold before triggering
|
# then compare the pixels of each back to back frame for any significant
|
||||||
# a motion event and then record to storage.
|
# differences between the pixels based on color_threshold. it loads the
|
||||||
|
# pixels of each frame in blocks. the size of the blocks are adjustable
|
||||||
|
# below. it counts how many pixels are different in the block and this is
|
||||||
|
# used to tell if the footage has motion if the different pixel count
|
||||||
|
# exceeds it.
|
||||||
|
#
|
||||||
|
block_x = 64
|
||||||
|
# this is the x coordinate size or horizontal size of a block of pixels
|
||||||
|
# that the application will use to detect the presents of motion.
|
||||||
|
#
|
||||||
|
block_y = 60
|
||||||
|
# this is the y coordinate size or vertical size of a block of pixels
|
||||||
|
# that the application will use to detect the presents of motion.
|
||||||
#
|
#
|
||||||
duration = 60
|
duration = 60
|
||||||
# this sets the internal timer used to reset to the detection loop and
|
# this sets the internal timer used to reset to the detection loop and
|
||||||
# then call post_cmd if it is defined. note: this time can be extended if
|
# then call post_cmd if it is defined. note: this time is extended if
|
||||||
# motion was detected. this will also reload the config file so changes
|
# motion was detected. this will also reload the config file so changes
|
||||||
# to the settings will be applied without restarting the application.
|
# to the settings will be applied without restarting the application.
|
||||||
#
|
#
|
||||||
post_cmd = move_the_ptz_camera.py
|
post_cmd = move_the_ptz_camera.py
|
||||||
# this an optional command to run after the internal timer duration has
|
# this an optional command to run after the internal timer duration has
|
||||||
|
@ -81,15 +80,6 @@ post_cmd = move_the_ptz_camera.py
|
||||||
# position of it's patrol pattern. note: the call to this command can be
|
# position of it's patrol pattern. note: the call to this command can be
|
||||||
# delayed if motion was detected.
|
# delayed if motion was detected.
|
||||||
#
|
#
|
||||||
detect_fps = 20
|
|
||||||
# this is how many frames to read from the detection stream per second.
|
|
||||||
# setting this any higher the camera's actual fps will just waste cpu
|
|
||||||
# cycles but setting it too low makes detecting motion inaccurate.
|
|
||||||
#
|
|
||||||
secs_post_motion = 10
|
|
||||||
# this is the minimum amount of seconds to capture after motion was
|
|
||||||
# detected.
|
|
||||||
#
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Build Setup ###
|
### Build Setup ###
|
||||||
|
|
339
src/main.cpp
339
src/main.cpp
|
@ -6,6 +6,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <opencv4/opencv2/opencv.hpp>
|
#include <opencv4/opencv2/opencv.hpp>
|
||||||
#include <opencv4/opencv2/videoio.hpp>
|
#include <opencv4/opencv2/videoio.hpp>
|
||||||
|
@ -13,24 +16,27 @@
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
#define BUF_SZ 3
|
||||||
|
|
||||||
struct shared_t
|
struct shared_t
|
||||||
{
|
{
|
||||||
VideoCapture camera;
|
string recordUrl;
|
||||||
Mat baseImg;
|
string outDir;
|
||||||
string detectUrl;
|
string postCmd;
|
||||||
string recordUrl;
|
string conf;
|
||||||
string diffVerb;
|
string buffDir;
|
||||||
string outDir;
|
string concatTxtTmp;
|
||||||
string postCmd;
|
string concatShTmp;
|
||||||
string conf;
|
string createShTmp;
|
||||||
int detectFps;
|
mutex thrMutex;
|
||||||
int colorThresh;
|
bool init;
|
||||||
int secs;
|
int tmpId;
|
||||||
int consec;
|
int colorThresh;
|
||||||
int consecThresh;
|
int secs;
|
||||||
int pixThresh;
|
int blockThresh;
|
||||||
int postMoIncr;
|
int blockX;
|
||||||
int retCode;
|
int blockY;
|
||||||
|
int retCode;
|
||||||
|
|
||||||
} sharedRes;
|
} sharedRes;
|
||||||
|
|
||||||
|
@ -74,6 +80,26 @@ bool createDirTree(const string &full_path)
|
||||||
return ret;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string genDstFile(const string &dirOut, const string &ext)
|
string genDstFile(const string &dirOut, const string &ext)
|
||||||
{
|
{
|
||||||
time_t rawtime;
|
time_t rawtime;
|
||||||
|
@ -82,27 +108,22 @@ string genDstFile(const string &dirOut, const string &ext)
|
||||||
|
|
||||||
auto timeinfo = localtime(&rawtime);
|
auto timeinfo = localtime(&rawtime);
|
||||||
|
|
||||||
char dirName[20];
|
char dateC[20];
|
||||||
char fileName[20];
|
|
||||||
|
|
||||||
strftime(dirName, 20, "%Y%m%d", timeinfo);
|
strftime(dateC, 20, "%Y-%m-%d", timeinfo);
|
||||||
strftime(fileName, 20, "%H%M%S", timeinfo);
|
|
||||||
|
|
||||||
createDirTree(cleanDir(dirOut) + string("/") + string(dirName));
|
createDirTree(cleanDir(dirOut));
|
||||||
|
|
||||||
return cleanDir(dirOut) + string("/") + string(dirName) + string("/") + string(fileName) + ext;
|
return cleanDir(dirOut) + string("/") + string(dateC) + ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wrOut(shared_t *share)
|
string genTmpFile(const string &dirOut, const string &ext, shared_t *share)
|
||||||
{
|
{
|
||||||
share->baseImg.release();
|
createDirTree(cleanDir(dirOut));
|
||||||
|
|
||||||
share->consec = 0;
|
share->tmpId += 1;
|
||||||
|
|
||||||
auto dstPath = genDstFile(share->outDir, ".mp4");
|
return cleanDir(dirOut) + string("/") + to_string(share->tmpId) + ext;
|
||||||
auto cmd = "ffmpeg -i " + share->recordUrl + " -y -vcodec copy -t " + to_string(share->postMoIncr) + " " + dstPath;
|
|
||||||
|
|
||||||
system(cmd.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat toGray(const Mat &src)
|
Mat toGray(const Mat &src)
|
||||||
|
@ -129,7 +150,7 @@ bool pixDiff(const uchar &pixA, const uchar &pixB, shared_t *share)
|
||||||
return diff != 0;
|
return diff != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int secDiff(Mat imgA, Mat imgB, int rows, int cols, int rowOffs, int colOffs, shared_t *share)
|
void secDiff(Mat imgA, Mat imgB, int rows, int cols, int rowOffs, int colOffs, bool *mod, shared_t *share)
|
||||||
{
|
{
|
||||||
auto pnts = 0;
|
auto pnts = 0;
|
||||||
|
|
||||||
|
@ -140,50 +161,45 @@ int secDiff(Mat imgA, Mat imgB, int rows, int cols, int rowOffs, int colOffs, sh
|
||||||
auto pixA = imgA.at<uchar>(Point(x, y));
|
auto pixA = imgA.at<uchar>(Point(x, y));
|
||||||
auto pixB = imgB.at<uchar>(Point(x, y));
|
auto pixB = imgB.at<uchar>(Point(x, y));
|
||||||
|
|
||||||
|
//cout << "pnts: " << pnts << endl;
|
||||||
|
|
||||||
if (pixDiff(pixA, pixB, share))
|
if (pixDiff(pixA, pixB, share))
|
||||||
{
|
{
|
||||||
pnts += 1;
|
pnts += 1;
|
||||||
|
|
||||||
|
if (pnts >= share->blockThresh)
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(share->thrMutex);
|
||||||
|
|
||||||
|
*mod = true; return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pnts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool imgDiff(Mat curImg, shared_t *share)
|
bool imgDiff(Mat prev, Mat next, shared_t *share)
|
||||||
{
|
{
|
||||||
if (share->baseImg.empty())
|
auto numOfXBlocks = prev.cols / share->blockX;
|
||||||
|
auto numOfYBlocks = prev.rows / share->blockY;
|
||||||
|
auto moInBlock = false;
|
||||||
|
|
||||||
|
vector<thread> threads;
|
||||||
|
|
||||||
|
for (auto x = 0; (x < numOfXBlocks) && !moInBlock; x += share->blockX)
|
||||||
{
|
{
|
||||||
share->baseImg = toGray(curImg);
|
for (auto y = 0; (y < numOfYBlocks) && !moInBlock; y += share->blockY)
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
curImg = toGray(curImg);
|
|
||||||
|
|
||||||
auto pnts = secDiff(share->baseImg, curImg, curImg.rows, curImg.cols, 0, 0, share);
|
|
||||||
|
|
||||||
if (share->diffVerb == "Y")
|
|
||||||
{
|
{
|
||||||
cout << "diff: " << pnts << endl;
|
threads.push_back(thread(secDiff, prev, next, share->blockY, share->blockX, y, x, &moInBlock, share));
|
||||||
}
|
|
||||||
|
|
||||||
share->baseImg = curImg.clone();
|
|
||||||
|
|
||||||
if (pnts >= share->pixThresh)
|
|
||||||
{
|
|
||||||
share->consec += 1;
|
|
||||||
|
|
||||||
return share->consec >= share->consecThresh;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
share->consec = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto &&thr : threads)
|
||||||
|
{
|
||||||
|
thr.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
return moInBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
|
string parseForParam(const string &arg, int argc, char** argv, bool argOnly)
|
||||||
|
@ -218,7 +234,7 @@ void rdLine(const string ¶m, const string &line, string *value)
|
||||||
{
|
{
|
||||||
*value = line.substr(param.size());
|
*value = line.substr(param.size());
|
||||||
|
|
||||||
cout << param << *value << endl;
|
//cout << param << *value << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,40 +244,34 @@ void rdLine(const string ¶m, const string &line, int *value)
|
||||||
{
|
{
|
||||||
*value = strtol(line.substr(param.size()).c_str(), NULL, 10);
|
*value = strtol(line.substr(param.size()).c_str(), NULL, 10);
|
||||||
|
|
||||||
cout << param << *value << endl;
|
//cout << param << *value << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rdConf(shared_t *share)
|
bool rdConf(shared_t *share)
|
||||||
{
|
{
|
||||||
auto ret = false;
|
|
||||||
|
|
||||||
share->retCode = ENOENT;
|
|
||||||
|
|
||||||
ifstream varFile(share->conf.c_str());
|
ifstream varFile(share->conf.c_str());
|
||||||
|
|
||||||
if (!varFile.is_open())
|
if (!varFile.is_open())
|
||||||
{
|
{
|
||||||
cerr << "err: failed to open the config file: " << share->conf << " for reading. please check file permissions or if it exists." << endl;
|
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
|
else
|
||||||
{
|
{
|
||||||
string line;
|
string line;
|
||||||
|
|
||||||
share->recordUrl.clear();
|
share->recordUrl.clear();
|
||||||
share->detectUrl.clear();
|
|
||||||
share->outDir.clear();
|
share->outDir.clear();
|
||||||
share->postCmd.clear();
|
share->postCmd.clear();
|
||||||
share->diffVerb.clear();
|
share->buffDir.clear();
|
||||||
share->baseImg.release();
|
|
||||||
|
|
||||||
share->pixThresh = 8;
|
share->colorThresh = 5;
|
||||||
share->consecThresh = 10;
|
|
||||||
share->colorThresh = 60;
|
|
||||||
share->secs = 60;
|
share->secs = 60;
|
||||||
share->detectFps = 20;
|
share->blockX = 32;
|
||||||
share->postMoIncr = 5;
|
share->blockY = 32;
|
||||||
share->consec = 0;
|
share->blockThresh = 900;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -270,70 +280,147 @@ bool rdConf(shared_t *share)
|
||||||
if (line.rfind("#", 0) != 0)
|
if (line.rfind("#", 0) != 0)
|
||||||
{
|
{
|
||||||
rdLine("recording_stream = ", line, &share->recordUrl);
|
rdLine("recording_stream = ", line, &share->recordUrl);
|
||||||
rdLine("detection_stream = ", line, &share->detectUrl);
|
|
||||||
rdLine("output_dir = ", line, &share->outDir);
|
rdLine("output_dir = ", line, &share->outDir);
|
||||||
rdLine("diff_verbose = ", line, &share->diffVerb);
|
|
||||||
rdLine("post_cmd = ", line, &share->postCmd);
|
rdLine("post_cmd = ", line, &share->postCmd);
|
||||||
rdLine("pix_threshold = ", line, &share->pixThresh);
|
|
||||||
rdLine("color_threshold = ", line, &share->colorThresh);
|
rdLine("color_threshold = ", line, &share->colorThresh);
|
||||||
rdLine("consec_threshold = ", line, &share->consecThresh);
|
|
||||||
rdLine("duration = ", line, &share->secs);
|
rdLine("duration = ", line, &share->secs);
|
||||||
rdLine("secs_post_motion = ", line, &share->postMoIncr);
|
rdLine("buff_dir = ", line, &share->buffDir);
|
||||||
rdLine("detect_fps = ", line, &share->detectFps);
|
rdLine("block_x = ", line, &share->blockX);
|
||||||
|
rdLine("block_y = ", line, &share->blockY);
|
||||||
|
rdLine("block_threshold = ", line, &share->blockThresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
} while(!line.empty());
|
} while(!line.empty());
|
||||||
|
|
||||||
ret = true;
|
if (share->init)
|
||||||
|
{
|
||||||
|
system(string("rm -r " + share->buffDir).c_str());
|
||||||
|
|
||||||
|
share->init = false;
|
||||||
|
}
|
||||||
|
|
||||||
share->retCode = 0;
|
share->retCode = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
varFile.close();
|
varFile.close();
|
||||||
|
|
||||||
return ret;
|
return share->retCode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void moDetect(shared_t *share)
|
bool capPair(Mat &prev, Mat &next, VideoCapture &capture, shared_t *share)
|
||||||
{
|
{
|
||||||
while (rdConf(share))
|
capture >> prev;
|
||||||
|
capture >> next;
|
||||||
|
|
||||||
|
return !prev.empty() && !next.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wrOut(const string &buffFile, const string &dstPath, shared_t *share)
|
||||||
|
{
|
||||||
|
ofstream file;
|
||||||
|
|
||||||
|
auto scriptFile = genTmpFile(share->buffDir, ".sh", share);
|
||||||
|
auto scriptData = string();
|
||||||
|
|
||||||
|
if (fileExists(dstPath))
|
||||||
{
|
{
|
||||||
for (auto i = 0; i < (share->secs * share->detectFps); ++i)
|
auto concatFile = genTmpFile(share->buffDir, ".txt", share);
|
||||||
|
auto existsFile = genTmpFile(share->outDir, ".ts", share);
|
||||||
|
auto concatData = share->concatTxtTmp;
|
||||||
|
|
||||||
|
scriptData = share->concatShTmp;
|
||||||
|
|
||||||
|
replaceAll(concatData, "%existsFile%", existsFile);
|
||||||
|
replaceAll(concatData, "%buffFile%", buffFile);
|
||||||
|
|
||||||
|
replaceAll(scriptData, "%existsFile%", existsFile);
|
||||||
|
replaceAll(scriptData, "%concatFile%", concatFile);
|
||||||
|
|
||||||
|
file.open(concatFile.c_str());
|
||||||
|
file << concatData;
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scriptData = share->createShTmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceAll(scriptData, "%buffFile%", buffFile);
|
||||||
|
replaceAll(scriptData, "%dstPath%", dstPath);
|
||||||
|
replaceAll(scriptData, "%scriptFile%", scriptFile);
|
||||||
|
|
||||||
|
file.open(scriptFile.c_str());
|
||||||
|
file << scriptData;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
system(string("sh " + scriptFile + " &").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool moDetect(const string &buffFile, shared_t *share)
|
||||||
|
{
|
||||||
|
auto mod = false;
|
||||||
|
|
||||||
|
VideoCapture capture(buffFile.c_str(), CAP_FFMPEG);
|
||||||
|
|
||||||
|
if (capture.isOpened())
|
||||||
|
{
|
||||||
|
Mat prev;
|
||||||
|
Mat next;
|
||||||
|
|
||||||
|
while (capPair(prev, next, capture, share))
|
||||||
{
|
{
|
||||||
Mat frame;
|
if (imgDiff(toGray(prev), toGray(next), share))
|
||||||
|
|
||||||
if (!share->camera.isOpened())
|
|
||||||
{
|
{
|
||||||
share->camera.open(share->detectUrl, CAP_FFMPEG);
|
mod = true; break;
|
||||||
}
|
|
||||||
|
|
||||||
share->camera >> frame;
|
|
||||||
|
|
||||||
if (frame.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.
|
|
||||||
share->camera.open(share->detectUrl, CAP_FFMPEG);
|
|
||||||
}
|
|
||||||
else if (imgDiff(frame, share))
|
|
||||||
{
|
|
||||||
wrOut(share); i = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usleep(1000000 / share->detectFps);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
system(share->postCmd.c_str());
|
if (mod)
|
||||||
|
{
|
||||||
|
auto dstPath = genDstFile(share->outDir, ".ts");
|
||||||
|
|
||||||
|
wrOut(buffFile, dstPath, share);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cerr << "err: Could not open buff file: " << buffFile << " for reading. check formatting/permissions." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mod)
|
||||||
|
{
|
||||||
|
system(string("rm " + buffFile + " &").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
void recLoop(shared_t *share)
|
||||||
|
{
|
||||||
|
while (rdConf(share))
|
||||||
|
{
|
||||||
|
auto mod = false;
|
||||||
|
|
||||||
|
for (auto i = 0; i < share->secs; i += BUF_SZ)
|
||||||
|
{
|
||||||
|
auto dstPath = genTmpFile(share->buffDir, ".ts", share);
|
||||||
|
auto cmd = "ffmpeg -hide_banner -loglevel error -i " + share->recordUrl + " -y -vcodec copy -t " + to_string(BUF_SZ) + " " + dstPath;
|
||||||
|
|
||||||
|
system(cmd.c_str());
|
||||||
|
|
||||||
|
mod = moDetect(dstPath, share);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mod)
|
||||||
|
{
|
||||||
|
system(share->postCmd.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showHelp()
|
void showHelp()
|
||||||
{
|
{
|
||||||
cout << "Motion Watch v1.0" << endl << endl;
|
cout << "Motion Watch v1.1" << endl << endl;
|
||||||
cout << "Usage: mow <argument>" << endl << endl;
|
cout << "Usage: mow <argument>" << endl << endl;
|
||||||
cout << "-h : display usage information about this application." << endl;
|
cout << "-h : display usage information about this application." << endl;
|
||||||
cout << "-c : path to the config file." << endl;
|
cout << "-c : path to the config file." << endl;
|
||||||
|
@ -349,11 +436,33 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
else if (sharedRes.conf.empty())
|
else if (sharedRes.conf.empty())
|
||||||
{
|
{
|
||||||
cerr << "err: a config file was not given in -c" << endl;
|
cerr << "err: A config file was not given in -c" << endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
moDetect(&sharedRes);
|
sharedRes.retCode = 0;
|
||||||
|
sharedRes.tmpId = 0;
|
||||||
|
sharedRes.init = true;
|
||||||
|
|
||||||
|
sharedRes.concatTxtTmp += "file '%existsFile%'\n";
|
||||||
|
sharedRes.concatTxtTmp += "file '%buffFile%'\n";
|
||||||
|
|
||||||
|
sharedRes.concatShTmp += "#!/bin/sh\n";
|
||||||
|
sharedRes.concatShTmp += "cp '%dstPath%' '%existsFile%'\n";
|
||||||
|
sharedRes.concatShTmp += "ffmpeg -hide_banner -loglevel error -y -f concat -safe 0 -i '%concatFile%' -c copy '%dstPath%'\n";
|
||||||
|
sharedRes.concatShTmp += "rm '%concatFile%'\n";
|
||||||
|
sharedRes.concatShTmp += "rm '%existsFile%'\n";
|
||||||
|
sharedRes.concatShTmp += "rm '%buffFile%'\n";
|
||||||
|
sharedRes.concatShTmp += "rm '%scriptFile%'\n";
|
||||||
|
|
||||||
|
sharedRes.createShTmp += "#!/bin/sh\n";
|
||||||
|
sharedRes.createShTmp += "cp '%buffFile%' '%dstPath%'\n";
|
||||||
|
sharedRes.createShTmp += "rm '%buffFile%'\n";
|
||||||
|
sharedRes.createShTmp += "rm '%scriptFile%'\n";
|
||||||
|
|
||||||
|
thread th1(recLoop, &sharedRes);
|
||||||
|
|
||||||
|
th1.join();
|
||||||
|
|
||||||
return sharedRes.retCode;
|
return sharedRes.retCode;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user