-recording loop detected in "starving" state now triggers and auto restart of the recording loop. -preparing to release to main.
254 lines
6.4 KiB
C++
254 lines
6.4 KiB
C++
// This file is part of JustMotion.
|
|
|
|
// JustMotion 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.
|
|
|
|
// JustMotion 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 "detect_loop.h"
|
|
|
|
DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QFileSystemWatcher(parent)
|
|
{
|
|
pcTimer = 0;
|
|
shared = sharedRes;
|
|
|
|
connect(thr, &QThread::started, this, &DetectLoop::init);
|
|
connect(thr, &QThread::finished, this, &DetectLoop::deleteLater);
|
|
|
|
moveToThread(thr);
|
|
}
|
|
|
|
void DetectLoop::init()
|
|
{
|
|
pcTimer = new QTimer(this);
|
|
|
|
connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak);
|
|
connect(this, &QFileSystemWatcher::directoryChanged, this, &DetectLoop::updated);
|
|
|
|
pcTimer->start(shared->postSecs * 1000);
|
|
|
|
setupBuffDir(shared->buffPath);
|
|
addPath(shared->buffPath + "/live");
|
|
}
|
|
|
|
void DetectLoop::reset()
|
|
{
|
|
eventQue.inQue = false;
|
|
eventQue.score = 0;
|
|
eventQue.queAge = 0;
|
|
|
|
eventQue.imgPath.clear();
|
|
eventQue.vidList.clear();
|
|
eventQue.timeStamp.clear();
|
|
}
|
|
|
|
void DetectLoop::updated(const QString &path)
|
|
{
|
|
eTimer.start();
|
|
|
|
auto clips = lsFilesInDir(path, shared->streamExt);
|
|
auto index = clips.indexOf(vidBName);
|
|
|
|
if (clips.size() - (index + 1) < 3)
|
|
{
|
|
thread()->sleep(1);
|
|
}
|
|
else
|
|
{
|
|
vidAName = clips[clips.size() - 3];
|
|
vidBName = clips[clips.size() - 2];
|
|
|
|
vidAPath = shared->buffPath + "/live/" + vidAName;
|
|
vidBPath = shared->buffPath + "/live/" + vidBName;
|
|
|
|
exec(); thread()->sleep(1);
|
|
}
|
|
}
|
|
|
|
void DetectLoop::pcBreak()
|
|
{
|
|
prevClips.clear();
|
|
|
|
if (!shared->postCmd.isEmpty())
|
|
{
|
|
qInfo() << "---POST_BREAK---";
|
|
|
|
if (eventQue.inQue)
|
|
{
|
|
qInfo() << "motion detected, skipping the post command";
|
|
}
|
|
else
|
|
{
|
|
qInfo() << "no motion detected, running post command: " << shared->postCmd;
|
|
|
|
auto args = parseArgs(shared->postCmd.toUtf8(), -1);
|
|
|
|
if (args.isEmpty())
|
|
{
|
|
qCritical() << "err: did not parse an executable from the post command line.";
|
|
}
|
|
else
|
|
{
|
|
QProcess::execute(args[0], args.mid(1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float DetectLoop::getFloatFromExe(const QByteArray &line)
|
|
{
|
|
QString strLine(line);
|
|
QString strNum;
|
|
|
|
for (auto chr : strLine)
|
|
{
|
|
if (chr.isDigit() || (chr == '.'))
|
|
{
|
|
strNum.append(chr);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto ok = false;
|
|
auto res = strNum.toFloat(&ok);
|
|
|
|
if (!ok || strNum.isEmpty())
|
|
{
|
|
qCritical() << "err: the image comp command returned unexpected output and couldn't be converted to float";
|
|
qCritical() << " raw output: " << line;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
QStringList DetectLoop::buildArgs(const QString &prev, const QString &next)
|
|
{
|
|
auto args = parseArgs(shared->compCmd.toUtf8(), -1);
|
|
|
|
for (auto i = 0; i < args.size(); ++i)
|
|
{
|
|
if (args[i] == PREV_IMG) args[i] = prev;
|
|
if (args[i] == NEXT_IMG) args[i] = next;
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
QStringList DetectLoop::buildSnapArgs(const QString &vidSrc, const QString &imgPath)
|
|
{
|
|
QStringList ret;
|
|
|
|
ret.append("-hide_banner");
|
|
ret.append("-loglevel");
|
|
ret.append("panic");
|
|
ret.append("-y");
|
|
ret.append("-i");
|
|
ret.append(vidSrc);
|
|
ret.append("-frames:v");
|
|
ret.append("1");
|
|
ret.append(imgPath);
|
|
|
|
return ret;
|
|
}
|
|
|
|
QString DetectLoop::statusLine()
|
|
{
|
|
if (eTimer.elapsed() >= 5000)
|
|
{
|
|
emit starving();
|
|
|
|
return "STARVED";
|
|
}
|
|
else
|
|
{
|
|
return "OK ";
|
|
}
|
|
}
|
|
|
|
void DetectLoop::exec()
|
|
{
|
|
auto imgAPath = shared->buffPath + "/img/" + QFileInfo(vidAPath).baseName() + ".bmp";
|
|
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 (compArgs.isEmpty())
|
|
{
|
|
qCritical() << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd;
|
|
}
|
|
else
|
|
{
|
|
QProcess::execute("ffmpeg", snapArgsA);
|
|
QProcess::execute("ffmpeg", snapArgsB);
|
|
|
|
if (QFile::exists(imgAPath) && QFile::exists(imgBPath))
|
|
{
|
|
QProcess extComp;
|
|
|
|
extComp.start(compArgs[0], compArgs.mid(1));
|
|
extComp.waitForFinished();
|
|
|
|
float score = 0;
|
|
|
|
if (shared->outputType == "stdout")
|
|
{
|
|
score = getFloatFromExe(extComp.readAllStandardOutput());
|
|
}
|
|
else
|
|
{
|
|
score = getFloatFromExe(extComp.readAllStandardError());
|
|
}
|
|
|
|
qInfo() << compArgs.join(" ") << " --result: " << QString::number(score);
|
|
|
|
if (eventQue.inQue)
|
|
{
|
|
eventQue.queAge += 4;
|
|
|
|
if (eventQue.score < score)
|
|
{
|
|
eventQue.score = score;
|
|
eventQue.imgPath = imgBPath;
|
|
}
|
|
|
|
eventQue.vidList.append(vidAPath);
|
|
eventQue.vidList.append(vidBPath);
|
|
|
|
if (eventQue.queAge >= shared->evMaxSecs)
|
|
{
|
|
eventQue.inQue = false;
|
|
|
|
shared->recList.append(eventQue);
|
|
|
|
reset();
|
|
}
|
|
}
|
|
else if (score >= shared->imgThresh)
|
|
{
|
|
qInfo() << "--threshold_meet: " << QString::number(shared->imgThresh);
|
|
|
|
eventQue.score = score;
|
|
eventQue.imgPath = imgBPath;
|
|
eventQue.inQue = true;
|
|
eventQue.queAge = 0;
|
|
eventQue.timeStamp = QDateTime::currentDateTime().toString(DATETIME_FMT);
|
|
|
|
eventQue.vidList.append(vidAPath);
|
|
eventQue.vidList.append(vidBPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
vidAPath.clear();
|
|
vidBPath.clear();
|
|
}
|