JustMotion/src/detect_loop.cpp
zii 7046dd1162 v3.5.t3
-recording loop detected in "starving" state now triggers and auto
 restart of the recording loop.

-preparing to release to main.
2024-08-02 07:55:04 -04:00

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();
}