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