v3.3.t4
-completely reformed the eventloop code to be more efficent and removed the use backward/forward facing functions. -added the live_secs config option that limits the amount of live footage the app will record to the buffer directory. -moved away from ffmpeg hls formatting. live footage will instead just be recorded in clips inside of the 'live' directory, the stream.m3u8 file will not longer be created. doing this removes the codec/container limitations that hls imposed. -changed up the default conf values that better suits a low spec machine like a resberrypi.
This commit is contained in:
parent
525c342c0f
commit
60a24c9d67
278
src/camera.cpp
278
src/camera.cpp
|
@ -20,15 +20,12 @@ int Camera::start(const QStringList &args)
|
||||||
{
|
{
|
||||||
auto thr1 = new QThread(nullptr);
|
auto thr1 = new QThread(nullptr);
|
||||||
auto thr2 = new QThread(nullptr);
|
auto thr2 = new QThread(nullptr);
|
||||||
auto thr3 = new QThread(nullptr);
|
|
||||||
|
|
||||||
new Upkeep(&shared, thr1, nullptr);
|
new EventLoop(&shared, thr1, nullptr);
|
||||||
new EventLoop(&shared, thr2, nullptr);
|
new DetectLoop(&shared, thr2, nullptr);
|
||||||
new DetectLoop(&shared, thr3, nullptr);
|
|
||||||
|
|
||||||
thr1->start();
|
thr1->start();
|
||||||
thr2->start();
|
thr2->start();
|
||||||
thr3->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shared.retCode;
|
return shared.retCode;
|
||||||
|
@ -75,103 +72,22 @@ bool Loop::exec()
|
||||||
return shared->retCode == 0;
|
return shared->retCode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Upkeep::Upkeep(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) {}
|
|
||||||
|
|
||||||
bool Upkeep::exec()
|
|
||||||
{
|
|
||||||
enforceMaxEvents(shared);
|
|
||||||
enforceMaxImages(shared);
|
|
||||||
enforceMaxVids(shared);
|
|
||||||
|
|
||||||
return Loop::exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
EventLoop::EventLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
|
EventLoop::EventLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
|
||||||
{
|
{
|
||||||
heartBeat = 2;
|
heartBeat = 2;
|
||||||
highScore = 0;
|
|
||||||
cycles = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventLoop::exec()
|
bool EventLoop::wrOutVod(const evt_t &event)
|
||||||
{
|
{
|
||||||
if (!vidList.isEmpty())
|
|
||||||
{
|
|
||||||
vidList.removeDuplicates();
|
|
||||||
|
|
||||||
if (vidList.size() > 1)
|
|
||||||
{
|
|
||||||
QTextStream(stdout) << "attempting write out of event: " << name << Qt::endl;
|
|
||||||
|
|
||||||
if (wrOutVod())
|
|
||||||
{
|
|
||||||
QProcess proc;
|
|
||||||
QStringList args;
|
|
||||||
|
|
||||||
args << "convert";
|
|
||||||
args << imgPath;
|
|
||||||
args << shared->recPath + "/" + name + shared->thumbExt;
|
|
||||||
|
|
||||||
proc.start("magick", args);
|
|
||||||
proc.waitForFinished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cycles = 0;
|
|
||||||
highScore = 0;
|
|
||||||
|
|
||||||
vidList.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<int> rmIndx;
|
|
||||||
|
|
||||||
for (auto i = 0; i < shared->recList.size(); ++i)
|
|
||||||
{
|
|
||||||
auto event = &shared->recList[i];
|
|
||||||
|
|
||||||
if (highScore < event->score)
|
|
||||||
{
|
|
||||||
name = event->timeStamp.toString(DATETIME_FMT);
|
|
||||||
imgPath = event->imgPath;
|
|
||||||
highScore = event->score;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->queAge >= (shared->evMaxSecs / heartBeat))
|
|
||||||
{
|
|
||||||
auto maxSecs = shared->evMaxSecs / 2;
|
|
||||||
// half the maxsecs value to get front-back half secs
|
|
||||||
|
|
||||||
auto backFiles = backwardFacingFiles(shared->buffPath + "/live", shared->streamExt, event->timeStamp, maxSecs);
|
|
||||||
auto frontFiles = forwardFacingFiles(shared->buffPath + "/live", shared->streamExt, event->timeStamp, maxSecs);
|
|
||||||
|
|
||||||
vidList.append(backFiles + frontFiles);
|
|
||||||
rmIndx.append(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
event->queAge += heartBeat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i : rmIndx)
|
|
||||||
{
|
|
||||||
shared->recList.removeAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Loop::exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EventLoop::wrOutVod()
|
|
||||||
{
|
|
||||||
auto cnt = 0;
|
|
||||||
auto concat = name + ".tmp";
|
|
||||||
auto ret = false;
|
auto ret = false;
|
||||||
|
auto cnt = 0;
|
||||||
|
auto concat = shared->buffPath + "/live/" + event.timeStamp + ".ctmp";
|
||||||
|
|
||||||
QFile file(concat);
|
QFile file(concat, this);
|
||||||
|
|
||||||
file.open(QFile::WriteOnly);
|
file.open(QFile::WriteOnly);
|
||||||
|
|
||||||
for (auto &&vid : vidList)
|
for (auto &&vid : event.vidList)
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << "event_src: " << vid << Qt::endl;
|
QTextStream(stdout) << "event_src: " << vid << Qt::endl;
|
||||||
|
|
||||||
|
@ -189,7 +105,7 @@ bool EventLoop::wrOutVod()
|
||||||
|
|
||||||
if (cnt == 0)
|
if (cnt == 0)
|
||||||
{
|
{
|
||||||
QTextStream(stderr) << "err: none of the event hls clips exists, canceling write out." << Qt::endl;
|
QTextStream(stderr) << "err: none of the event hls clips exists, cancelling write out." << Qt::endl;
|
||||||
|
|
||||||
QFile::remove(concat);
|
QFile::remove(concat);
|
||||||
}
|
}
|
||||||
|
@ -202,29 +118,59 @@ bool EventLoop::wrOutVod()
|
||||||
args << "-safe" << "0";
|
args << "-safe" << "0";
|
||||||
args << "-i" << concat;
|
args << "-i" << concat;
|
||||||
args << "-c" << "copy";
|
args << "-c" << "copy";
|
||||||
args << shared->recPath + "/" + name + shared->recExt;
|
args << shared->recPath + "/" + event.timeStamp + shared->recExt;
|
||||||
|
|
||||||
|
if (QProcess::execute("ffmpeg", args) == 0)
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
QProcess::execute("ffmpeg", args);
|
|
||||||
QFile::remove(concat);
|
QFile::remove(concat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EventLoop::exec()
|
||||||
|
{
|
||||||
|
enforceMaxEvents(shared);
|
||||||
|
enforceMaxImages(shared);
|
||||||
|
enforceMaxClips(shared);
|
||||||
|
|
||||||
|
if (!shared->recList.isEmpty())
|
||||||
|
{
|
||||||
|
auto event = shared->recList.takeFirst();
|
||||||
|
|
||||||
|
QTextStream(stdout) << "attempting write out of event: " << event.timeStamp << Qt::endl;
|
||||||
|
|
||||||
|
if (wrOutVod(event))
|
||||||
|
{
|
||||||
|
QStringList args;
|
||||||
|
|
||||||
|
args << "convert";
|
||||||
|
args << event.imgPath;
|
||||||
|
args << shared->recPath + "/" + event.timeStamp + shared->thumbExt;
|
||||||
|
|
||||||
|
QProcess::execute("magick", args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Loop::exec();
|
||||||
|
}
|
||||||
|
|
||||||
DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
|
DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
|
||||||
{
|
{
|
||||||
seed = 0;
|
pcTimer = 0;
|
||||||
pcTimer = 0;
|
heartBeat = 2;
|
||||||
heartBeat = 2;
|
|
||||||
delayCycles = 12; // this will be used to delay the
|
|
||||||
// actual start of DetectLoop by
|
|
||||||
// 24secs.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetectLoop::init()
|
void DetectLoop::init()
|
||||||
{
|
{
|
||||||
pcTimer = new QTimer(this);
|
pcTimer = new QTimer(this);
|
||||||
mod = false;
|
|
||||||
|
eventQue.queAge = 0;
|
||||||
|
eventQue.score = 0;
|
||||||
|
eventQue.inQue = false;
|
||||||
|
|
||||||
connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak);
|
connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak);
|
||||||
|
|
||||||
|
@ -244,15 +190,12 @@ void DetectLoop::pcBreak()
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << "---POST_BREAK---" << Qt::endl;
|
QTextStream(stdout) << "---POST_BREAK---" << Qt::endl;
|
||||||
|
|
||||||
if (mod)
|
if (eventQue.inQue)
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << "motion detected, skipping the post command." << Qt::endl;
|
QTextStream(stdout) << "motion detected, skipping the post command." << Qt::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (delayCycles == 0) delayCycles = 5;
|
|
||||||
else delayCycles += 5;
|
|
||||||
|
|
||||||
QTextStream(stdout) << "no motion detected, running post command: " << shared->postCmd << Qt::endl;
|
QTextStream(stdout) << "no motion detected, running post command: " << shared->postCmd << Qt::endl;
|
||||||
|
|
||||||
auto args = parseArgs(shared->postCmd.toUtf8(), -1);
|
auto args = parseArgs(shared->postCmd.toUtf8(), -1);
|
||||||
|
@ -267,8 +210,6 @@ void DetectLoop::pcBreak()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float DetectLoop::getFloatFromExe(const QByteArray &line)
|
float DetectLoop::getFloatFromExe(const QByteArray &line)
|
||||||
|
@ -288,7 +229,16 @@ float DetectLoop::getFloatFromExe(const QByteArray &line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strNum.toFloat();
|
auto ok = false;
|
||||||
|
auto res = strNum.toFloat(&ok);
|
||||||
|
|
||||||
|
if (!ok || strNum.isEmpty())
|
||||||
|
{
|
||||||
|
QTextStream(stderr) << "err: the image comp command returned unexpected output and couldn't be converted to float." << Qt::endl;
|
||||||
|
QTextStream(stderr) << " raw output: " << line << Qt::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList DetectLoop::buildArgs(const QString &prev, const QString &next)
|
QStringList DetectLoop::buildArgs(const QString &prev, const QString &next)
|
||||||
|
@ -308,6 +258,7 @@ QStringList DetectLoop::buildSnapArgs(const QString &vidSrc, const QString &imgP
|
||||||
{
|
{
|
||||||
QStringList ret;
|
QStringList ret;
|
||||||
|
|
||||||
|
ret.append("-hide_banner");
|
||||||
ret.append("-y");
|
ret.append("-y");
|
||||||
ret.append("-i");
|
ret.append("-i");
|
||||||
ret.append(vidSrc);
|
ret.append(vidSrc);
|
||||||
|
@ -320,88 +271,95 @@ QStringList DetectLoop::buildSnapArgs(const QString &vidSrc, const QString &imgP
|
||||||
|
|
||||||
bool DetectLoop::exec()
|
bool DetectLoop::exec()
|
||||||
{
|
{
|
||||||
if (delayCycles > 0)
|
if (eventQue.inQue)
|
||||||
{
|
{
|
||||||
delayCycles -= 1;
|
eventQue.queAge += heartBeat;
|
||||||
|
}
|
||||||
|
|
||||||
QTextStream(stdout) << "delay: detection cycle skipped. cycles left to be skipped: " << QString::number(delayCycles) << Qt::endl;
|
auto clips = lsFilesInDir(shared->buffPath + "/live", shared->streamExt);
|
||||||
|
|
||||||
|
if (clips.size() < 2)
|
||||||
|
{
|
||||||
|
QTextStream(stdout) << "warning: didn't pick up enough clips files from the video stream. number of files: " << QString::number(clips.size()) << Qt::endl;
|
||||||
|
QTextStream(stdout) << " will try again on the next loop." << Qt::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto imgAPath = "img/" + QString::number(seed++) + ".bmp";
|
auto vidAPath = shared->buffPath + "/live/" + clips[clips.size() - 2];
|
||||||
auto imgBPath = "img/" + QString::number(seed++) + ".bmp";
|
auto vidBPath = shared->buffPath + "/live/" + clips[clips.size() - 1];
|
||||||
auto curDT = QDateTime::currentDateTime();
|
auto imgAPath = shared->buffPath + "/img/" + QFileInfo(vidAPath).baseName() + ".bmp";
|
||||||
auto clips = backwardFacingFiles("live", shared->streamExt, curDT, 16);
|
auto imgBPath = shared->buffPath + "/img/" + QFileInfo(vidBPath).baseName() + ".bmp";
|
||||||
|
auto snapArgsA = buildSnapArgs(vidAPath, imgAPath);
|
||||||
|
auto snapArgsB = buildSnapArgs(vidBPath, imgBPath);
|
||||||
|
auto compArgs = buildArgs(imgAPath, imgBPath);
|
||||||
|
|
||||||
if (clips.size() < 2)
|
if (compArgs.isEmpty())
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << "warning: didn't pick up enough clips files from the video stream. number of files: " << QString::number(clips.size()) << Qt::endl;
|
QTextStream(stderr) << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd << Qt::endl;
|
||||||
QTextStream(stdout) << " will try again on the next loop." << Qt::endl;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto snapArgsA = buildSnapArgs(clips[clips.size() - 2], imgAPath);
|
QProcess::execute("ffmpeg", snapArgsA);
|
||||||
auto snapArgsB = buildSnapArgs(clips[clips.size() - 1], imgAPath);
|
QProcess::execute("ffmpeg", snapArgsB);
|
||||||
auto compArgs = buildArgs(imgAPath, imgBPath);
|
|
||||||
|
|
||||||
if (compArgs.isEmpty())
|
if (QFile::exists(imgAPath) && QFile::exists(imgBPath))
|
||||||
{
|
{
|
||||||
QTextStream(stderr) << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd << Qt::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QProcess snapFromVidA;
|
|
||||||
QProcess snapFromVidB;
|
|
||||||
QProcess extComp;
|
QProcess extComp;
|
||||||
|
|
||||||
snapFromVidA.start("ffmpeg", snapArgsA);
|
|
||||||
snapFromVidA.waitForFinished();
|
|
||||||
|
|
||||||
snapFromVidB.start("ffmpeg", snapArgsB);
|
|
||||||
snapFromVidB.waitForFinished();
|
|
||||||
|
|
||||||
extComp.start(compArgs[0], compArgs.mid(1));
|
extComp.start(compArgs[0], compArgs.mid(1));
|
||||||
extComp.waitForFinished();
|
extComp.waitForFinished();
|
||||||
|
|
||||||
float score = 0;
|
float score = 0;
|
||||||
auto ok = true;
|
|
||||||
|
|
||||||
if (shared->outputType == "stdout")
|
if (shared->outputType == "stdout")
|
||||||
{
|
{
|
||||||
score = getFloatFromExe(extComp.readAllStandardOutput());
|
score = getFloatFromExe(extComp.readAllStandardOutput());
|
||||||
}
|
}
|
||||||
else if (shared->outputType == "stderr")
|
else
|
||||||
{
|
{
|
||||||
score = getFloatFromExe(extComp.readAllStandardError());
|
score = getFloatFromExe(extComp.readAllStandardError());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok)
|
QTextStream(stdout) << compArgs.join(" ") << " --result: " << QString::number(score) << Qt::endl;
|
||||||
{
|
|
||||||
QTextStream(stderr) << "err: img_comp_out: " << shared->outputType << " is not valid. it must be 'stdout' or 'stderr'" << Qt::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QTextStream(stdout) << compArgs.join(" ") << " --result: " << QString::number(score) << Qt::endl;
|
|
||||||
|
|
||||||
if (score >= shared->imgThresh)
|
if (eventQue.inQue)
|
||||||
|
{
|
||||||
|
if (eventQue.score < score)
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << "--threshold_breached: " << QString::number(shared->imgThresh) << Qt::endl;
|
eventQue.score = score;
|
||||||
|
eventQue.imgPath = imgBPath;
|
||||||
evt_t event;
|
|
||||||
|
|
||||||
event.timeStamp = curDT;
|
|
||||||
event.score = score;
|
|
||||||
event.imgPath = imgBPath;
|
|
||||||
event.queAge = 0;
|
|
||||||
|
|
||||||
mod = true;
|
|
||||||
|
|
||||||
shared->recList.append(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eventQue.vidList.append(vidAPath);
|
||||||
|
eventQue.vidList.append(vidBPath);
|
||||||
|
|
||||||
|
if (eventQue.queAge >= shared->evMaxSecs)
|
||||||
|
{
|
||||||
|
eventQue.inQue = false;
|
||||||
|
|
||||||
|
shared->recList.append(eventQue);
|
||||||
|
|
||||||
|
eventQue.imgPath.clear();
|
||||||
|
eventQue.vidList.clear();
|
||||||
|
eventQue.timeStamp.clear();
|
||||||
|
|
||||||
|
eventQue.score = 0;
|
||||||
|
eventQue.queAge = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (score >= shared->imgThresh)
|
||||||
|
{
|
||||||
|
QTextStream(stdout) << "--threshold_breached: " << QString::number(shared->imgThresh) << Qt::endl;
|
||||||
|
|
||||||
|
eventQue.score = score;
|
||||||
|
eventQue.imgPath = imgBPath;
|
||||||
|
eventQue.inQue = true;
|
||||||
|
eventQue.queAge = 0;
|
||||||
|
eventQue.timeStamp = QDateTime::currentDateTime().toString(DATETIME_FMT);
|
||||||
|
|
||||||
|
eventQue.vidList.clear();
|
||||||
|
eventQue.vidList.append(vidAPath);
|
||||||
|
eventQue.vidList.append(vidBPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/camera.h
23
src/camera.h
|
@ -55,30 +55,13 @@ public:
|
||||||
virtual bool exec();
|
virtual bool exec();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Upkeep : public Loop
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit Upkeep(shared_t *shared, QThread *thr, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
bool exec();
|
|
||||||
};
|
|
||||||
|
|
||||||
class EventLoop : public Loop
|
class EventLoop : public Loop
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QStringList vidList;
|
bool wrOutVod(const evt_t &event);
|
||||||
QString imgPath;
|
|
||||||
QString name;
|
|
||||||
float highScore;
|
|
||||||
uint cycles;
|
|
||||||
|
|
||||||
bool wrOutVod();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -94,9 +77,7 @@ class DetectLoop : public Loop
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QTimer *pcTimer;
|
QTimer *pcTimer;
|
||||||
uint delayCycles;
|
evt_t eventQue;
|
||||||
bool mod;
|
|
||||||
quint64 seed;
|
|
||||||
|
|
||||||
void resetTimers();
|
void resetTimers();
|
||||||
float getFloatFromExe(const QByteArray &line);
|
float getFloatFromExe(const QByteArray &line);
|
||||||
|
|
|
@ -129,7 +129,7 @@ void enforceMaxImages(shared_t *share)
|
||||||
{
|
{
|
||||||
auto names = lsFilesInDir(share->buffPath + "/img", ".bmp");
|
auto names = lsFilesInDir(share->buffPath + "/img", ".bmp");
|
||||||
|
|
||||||
while (names.size() > MAX_IMAGES)
|
while (names.size() > (share->liveSecs / 2))
|
||||||
{
|
{
|
||||||
QFile::remove(share->buffPath + "/img/" + names[0]);
|
QFile::remove(share->buffPath + "/img/" + names[0]);
|
||||||
|
|
||||||
|
@ -137,11 +137,11 @@ void enforceMaxImages(shared_t *share)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void enforceMaxVids(shared_t *share)
|
void enforceMaxClips(shared_t *share)
|
||||||
{
|
{
|
||||||
auto names = lsFilesInDir(share->buffPath + "/live", share->streamExt);
|
auto names = lsFilesInDir(share->buffPath + "/live", share->streamExt);
|
||||||
|
|
||||||
while (names.size() > MAX_VIDEOS)
|
while (names.size() > (share->liveSecs / 2))
|
||||||
{
|
{
|
||||||
QFile::remove(share->buffPath + "/live/" + names[0]);
|
QFile::remove(share->buffPath + "/live/" + names[0]);
|
||||||
|
|
||||||
|
@ -217,8 +217,7 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
|
|
||||||
share->retCode = 0;
|
share->retCode = 0;
|
||||||
share->imgThresh = 8000;
|
share->imgThresh = 8000;
|
||||||
share->maxEvents = 100;
|
share->maxEvents = 30;
|
||||||
share->maxLogSize = 100000;
|
|
||||||
share->skipCmd = false;
|
share->skipCmd = false;
|
||||||
share->postSecs = 60;
|
share->postSecs = 60;
|
||||||
share->evMaxSecs = 30;
|
share->evMaxSecs = 30;
|
||||||
|
@ -226,12 +225,13 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->outputType = "stderr";
|
share->outputType = "stderr";
|
||||||
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
|
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
|
||||||
share->streamCodec = "copy";
|
share->streamCodec = "copy";
|
||||||
share->streamExt = ".ts";
|
share->streamExt = ".avi";
|
||||||
share->recExt = ".mp4";
|
share->recExt = ".avi";
|
||||||
share->thumbExt = ".jpg";
|
share->thumbExt = ".jpg";
|
||||||
share->imgThreads = thrCount;
|
share->imgThreads = thrCount;
|
||||||
share->recThreads = thrCount;
|
share->recThreads = thrCount;
|
||||||
share->recFps = 30;
|
share->recFps = 30;
|
||||||
|
share->liveSecs = 80;
|
||||||
share->recScale = "1280:720";
|
share->recScale = "1280:720";
|
||||||
share->imgScale = "320:240";
|
share->imgScale = "320:240";
|
||||||
share->servUser = APP_BIN;
|
share->servUser = APP_BIN;
|
||||||
|
@ -253,7 +253,6 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
rdLine("post_cmd = ", line, &share->postCmd);
|
rdLine("post_cmd = ", line, &share->postCmd);
|
||||||
rdLine("img_thresh = ", line, &share->imgThresh);
|
rdLine("img_thresh = ", line, &share->imgThresh);
|
||||||
rdLine("max_events = ", line, &share->maxEvents);
|
rdLine("max_events = ", line, &share->maxEvents);
|
||||||
rdLine("max_log_size = ", line, &share->maxLogSize);
|
|
||||||
rdLine("img_comp_out = ", line, &share->outputType);
|
rdLine("img_comp_out = ", line, &share->outputType);
|
||||||
rdLine("img_comp_cmd = ", line, &share->compCmd);
|
rdLine("img_comp_cmd = ", line, &share->compCmd);
|
||||||
rdLine("stream_codec = ", line, &share->streamCodec);
|
rdLine("stream_codec = ", line, &share->streamCodec);
|
||||||
|
@ -266,19 +265,25 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
rdLine("rec_scale = ", line, &share->recScale);
|
rdLine("rec_scale = ", line, &share->recScale);
|
||||||
rdLine("img_scale = ", line, &share->imgScale);
|
rdLine("img_scale = ", line, &share->imgScale);
|
||||||
rdLine("service_user = ", line, &share->servUser);
|
rdLine("service_user = ", line, &share->servUser);
|
||||||
|
rdLine("live_secs = ", line, &share->liveSecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
} while(!line.isEmpty());
|
} while(!line.isEmpty());
|
||||||
|
|
||||||
if (share->camName.isEmpty())
|
if (share->camName.isEmpty())
|
||||||
{
|
{
|
||||||
share->camName = QFileInfo(share->conf).fileName();
|
share->camName = QFileInfo(share->conf).baseName();
|
||||||
}
|
}
|
||||||
|
|
||||||
extCorrection(share->streamExt);
|
extCorrection(share->streamExt);
|
||||||
extCorrection(share->recExt);
|
extCorrection(share->recExt);
|
||||||
extCorrection(share->thumbExt);
|
extCorrection(share->thumbExt);
|
||||||
|
|
||||||
|
if (share->outputType != "stdout" && share->outputType != "stderr")
|
||||||
|
{
|
||||||
|
share->outputType = "stderr";
|
||||||
|
}
|
||||||
|
|
||||||
if (share->buffPath.isEmpty())
|
if (share->buffPath.isEmpty())
|
||||||
{
|
{
|
||||||
share->buffPath = "/var/buffer/" + share->camName;
|
share->buffPath = "/var/buffer/" + share->camName;
|
||||||
|
@ -297,6 +302,16 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->recPath = QDir::cleanPath(share->recPath);
|
share->recPath = QDir::cleanPath(share->recPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (share->liveSecs < 10)
|
||||||
|
{
|
||||||
|
share->liveSecs = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((share->liveSecs - 4) < share->evMaxSecs)
|
||||||
|
{
|
||||||
|
share->evMaxSecs = share->liveSecs - 4;
|
||||||
|
}
|
||||||
|
|
||||||
share->retCode = EACCES;
|
share->retCode = EACCES;
|
||||||
|
|
||||||
if (!mkPath(share->recPath))
|
if (!mkPath(share->recPath))
|
||||||
|
|
29
src/common.h
29
src/common.h
|
@ -29,42 +29,34 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define APP_VER "3.3.t3"
|
#define APP_VER "3.3.t4"
|
||||||
#define APP_NAME "Motion Watch"
|
#define APP_NAME "Motion Watch"
|
||||||
#define APP_BIN "mow"
|
#define APP_BIN "mow"
|
||||||
#define REC_LOG_NAME "rec.log"
|
|
||||||
#define DET_LOG_NAME "det.log"
|
|
||||||
#define UPK_LOG_NAME "upk.log"
|
|
||||||
#define DATETIME_FMT "yyyyMMddhhmmss"
|
#define DATETIME_FMT "yyyyMMddhhmmss"
|
||||||
#define STRFTIME_FMT "%Y%m%d%H%M%S"
|
#define STRFTIME_FMT "%Y%m%d%H%M%S"
|
||||||
#define MAX_IMAGES 1000
|
|
||||||
#define MAX_VIDEOS 1000
|
|
||||||
#define PREV_IMG "&prev&"
|
#define PREV_IMG "&prev&"
|
||||||
#define NEXT_IMG "&next&"
|
#define NEXT_IMG "&next&"
|
||||||
|
|
||||||
enum CmdExeType
|
enum CmdExeType
|
||||||
{
|
{
|
||||||
MAIN_LOOP,
|
MAIN_LOOP,
|
||||||
VID_LOOP,
|
VID_LOOP
|
||||||
IMG_LOOP
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct evt_t
|
struct evt_t
|
||||||
{
|
{
|
||||||
QDateTime timeStamp;
|
QList<QString> vidList;
|
||||||
QString imgPath;
|
QString timeStamp;
|
||||||
float score;
|
QString imgPath;
|
||||||
uint queAge;
|
float score;
|
||||||
|
bool inQue;
|
||||||
|
int queAge;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct shared_t
|
struct shared_t
|
||||||
{
|
{
|
||||||
QList<evt_t> recList;
|
QList<evt_t> recList;
|
||||||
QMutex recMutex;
|
|
||||||
QMutex logMutex;
|
|
||||||
QString conf;
|
QString conf;
|
||||||
QString recLog;
|
|
||||||
QString detLog;
|
|
||||||
QString recordUri;
|
QString recordUri;
|
||||||
QString buffPath;
|
QString buffPath;
|
||||||
QString postCmd;
|
QString postCmd;
|
||||||
|
@ -80,10 +72,10 @@ struct shared_t
|
||||||
QString imgScale;
|
QString imgScale;
|
||||||
QString servMainLoop;
|
QString servMainLoop;
|
||||||
QString servVidLoop;
|
QString servVidLoop;
|
||||||
QString servImgLoop;
|
|
||||||
QString servUser;
|
QString servUser;
|
||||||
bool singleTenant;
|
bool singleTenant;
|
||||||
bool skipCmd;
|
bool skipCmd;
|
||||||
|
int liveSecs;
|
||||||
int recFps;
|
int recFps;
|
||||||
int imgThreads;
|
int imgThreads;
|
||||||
int recThreads;
|
int recThreads;
|
||||||
|
@ -91,7 +83,6 @@ struct shared_t
|
||||||
int postSecs;
|
int postSecs;
|
||||||
int imgThresh;
|
int imgThresh;
|
||||||
int maxEvents;
|
int maxEvents;
|
||||||
int maxLogSize;
|
|
||||||
int retCode;
|
int retCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,7 +101,7 @@ void rdLine(const QString ¶m, const QString &line, int *value);
|
||||||
void rdLine(const QString ¶m, const QString &line, bool *value);
|
void rdLine(const QString ¶m, const QString &line, bool *value);
|
||||||
void enforceMaxEvents(shared_t *share);
|
void enforceMaxEvents(shared_t *share);
|
||||||
void enforceMaxImages(shared_t *share);
|
void enforceMaxImages(shared_t *share);
|
||||||
void enforceMaxVids(shared_t *share);
|
void enforceMaxClips(shared_t *share);
|
||||||
void extCorrection(QString &ext);
|
void extCorrection(QString &ext);
|
||||||
|
|
||||||
#endif // COMMON_H
|
#endif // COMMON_H
|
||||||
|
|
|
@ -23,6 +23,7 @@ void showHelp()
|
||||||
QTextStream(stdout) << "-i : all valid config files found in /etc/mow will be used to create" << Qt::endl;
|
QTextStream(stdout) << "-i : all valid config files found in /etc/mow will be used to create" << Qt::endl;
|
||||||
QTextStream(stdout) << " a full instance; full meaning main and vid loop systemd services" << Qt::endl;
|
QTextStream(stdout) << " a full instance; full meaning main and vid loop systemd services" << Qt::endl;
|
||||||
QTextStream(stdout) << " will be created for each config file." << Qt::endl;
|
QTextStream(stdout) << " will be created for each config file." << Qt::endl;
|
||||||
|
QTextStream(stdout) << "-d : this is the same as -i except it will not auto start the services." << Qt::endl;
|
||||||
QTextStream(stdout) << "-v : display the current version." << Qt::endl;
|
QTextStream(stdout) << "-v : display the current version." << Qt::endl;
|
||||||
QTextStream(stdout) << "-u : uninstall the entire app from your system, including all" << Qt::endl;
|
QTextStream(stdout) << "-u : uninstall the entire app from your system, including all" << Qt::endl;
|
||||||
QTextStream(stdout) << " systemd services related to it." << Qt::endl;
|
QTextStream(stdout) << " systemd services related to it." << Qt::endl;
|
||||||
|
|
|
@ -103,17 +103,21 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret += "taskset -c " + buildThreadCount(conf->recThreads) + " ";
|
ret += "ffmpeg -hide_banner -y -i '" + conf->recordUri + "' -strftime 1 -strftime_mkdir 1 ";
|
||||||
ret += "ffmpeg -hide_banner -i '" + conf->recordUri + "' -strftime 1 -strftime_mkdir 1 ";
|
|
||||||
|
|
||||||
if (conf->recordUri.contains("rtsp"))
|
if (conf->recordUri.contains("rtsp"))
|
||||||
{
|
{
|
||||||
ret += "-rtsp_transport tcp ";
|
ret += "-rtsp_transport tcp ";
|
||||||
}
|
}
|
||||||
|
|
||||||
ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " ";
|
if (conf->streamCodec != "copy")
|
||||||
ret += "-hls_segment_filename live/" + QString(STRFTIME_FMT) + conf->streamExt + " ";
|
{
|
||||||
ret += "-y -vcodec " + conf->streamCodec + " -f hls -hls_time 2 -hls_list_size 1000 -hls_flags append_list+omit_endlist -t 60 stream.m3u8";
|
ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "-vcodec " + conf->streamCodec + " ";
|
||||||
|
ret += "-reset_timestamps 1 -sc_threshold 0 -g 2 -force_key_frames \"expr:gte(t, n_forced * 2)\" -t 60 -segment_time 2 -f segment ";
|
||||||
|
ret += conf->buffPath + "/live/" + QString(STRFTIME_FMT) + conf->streamExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -150,8 +154,8 @@ int rmService(const QString &servName)
|
||||||
|
|
||||||
if (QFile::exists(path))
|
if (QFile::exists(path))
|
||||||
{
|
{
|
||||||
ret = QProcess::execute("systemctl", {"stop", servName});
|
if (ret == 0) ret = QProcess::execute("systemctl", {"stop", servName});
|
||||||
ret = QProcess::execute("systemctl", {"disable", servName});
|
if (ret == 0) ret = QProcess::execute("systemctl", {"disable", servName});
|
||||||
|
|
||||||
QFile::remove(path);
|
QFile::remove(path);
|
||||||
QFile::remove("/usr/bin/" + servName);
|
QFile::remove("/usr/bin/" + servName);
|
||||||
|
@ -168,6 +172,12 @@ int rmServiceByConf(const QString &confFile)
|
||||||
{
|
{
|
||||||
conf.retCode = rmService(conf.servMainLoop);
|
conf.retCode = rmService(conf.servMainLoop);
|
||||||
conf.retCode = rmService(conf.servVidLoop);
|
conf.retCode = rmService(conf.servVidLoop);
|
||||||
|
|
||||||
|
QDir live(conf.buffPath + "/live");
|
||||||
|
QDir img(conf.buffPath + "/img");
|
||||||
|
|
||||||
|
live.removeRecursively();
|
||||||
|
img.removeRecursively();
|
||||||
}
|
}
|
||||||
|
|
||||||
return conf.retCode;
|
return conf.retCode;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user