diff --git a/src/common.cpp b/src/common.cpp index 6e49eb6..1ff29ce 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -92,7 +92,7 @@ void enforceMaxImages(shared_t *share) { auto names = lsFilesInDir(share->buffPath + "/img", ".bmp"); - while (names.size() > (share->liveSecs / 2)) + while (names.size() > MAX_IMAGES) { QFile::remove(share->buffPath + "/img/" + names[0]); @@ -104,7 +104,7 @@ void enforceMaxClips(shared_t *share) { auto names = lsFilesInDir(share->buffPath + "/live", share->streamExt); - while (names.size() > (share->liveSecs / 2)) + while (names.size() > (share->liveSecs / SEG_SIZE)) { QFile::remove(share->buffPath + "/live/" + names[0]); diff --git a/src/common.h b/src/common.h index b10ad77..707b988 100644 --- a/src/common.h +++ b/src/common.h @@ -32,13 +32,15 @@ using namespace std; -#define APP_VERSION "3.5" +#define APP_VERSION "3.6.t1" #define APP_NAME "JustMotion" #define APP_TARGET "jmotion" #define DATETIME_FMT "yyyyMMddhhmmss" #define STRFTIME_FMT "%Y%m%d%H%M%S" #define PREV_IMG "&prev&" #define NEXT_IMG "&next&" +#define SEG_SIZE 10 +#define MAX_IMAGES 100 enum CmdExeType { diff --git a/src/detect_loop.cpp b/src/detect_loop.cpp index b92db0d..b763ead 100644 --- a/src/detect_loop.cpp +++ b/src/detect_loop.cpp @@ -12,7 +12,7 @@ #include "detect_loop.h" -DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QFileSystemWatcher(parent) +DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QObject(parent) { pcTimer = 0; shared = sharedRes; @@ -26,14 +26,21 @@ DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QFi void DetectLoop::init() { pcTimer = new QTimer(this); + evTimer = new QTimer(this); + dTime = QDateTime::currentDateTimeUtc(); - connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak); - connect(this, &QFileSystemWatcher::directoryChanged, this, &DetectLoop::updated); + connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak); + connect(evTimer, &QTimer::timeout, this, &DetectLoop::evBreak); + connect(this, &DetectLoop::loop, this, &DetectLoop::exec); pcTimer->start(shared->postSecs * 1000); + evTimer->start(shared->evMaxSecs * 1000); setupBuffDir(shared->buffPath); - addPath(shared->buffPath + "/live"); + + eTimer.start(); + + QTimer::singleShot(SEG_SIZE + 4, this, &DetectLoop::exec); } void DetectLoop::reset() @@ -47,29 +54,6 @@ void DetectLoop::reset() 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(); @@ -100,6 +84,16 @@ void DetectLoop::pcBreak() } } +void DetectLoop::evBreak() +{ + if (eventQue.inQue) + { + shared->recList.append(eventQue); + + reset(); + } +} + float DetectLoop::getFloatFromExe(const QByteArray &line) { QString strLine(line); @@ -142,26 +136,9 @@ QStringList DetectLoop::buildArgs(const QString &prev, const QString &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) + if (eTimer.elapsed() >= 30000) { emit starving(); @@ -175,79 +152,111 @@ QString DetectLoop::statusLine() 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); + auto vidPath = shared->buffPath + "/live/" + dTime.toString(DATETIME_FMT) + shared->streamExt; + auto found = false; - if (compArgs.isEmpty()) + for (auto i = 0; i < 100; ++i) { - 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)) + if (QFileInfo::exists(vidPath)) { - QProcess extComp; + found = true; break; + } + else + { + dTime = dTime.addSecs(1); - 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); - } + vidPath = shared->buffPath + "/live/" + dTime.toString(DATETIME_FMT) + shared->streamExt; } } - vidAPath.clear(); - vidBPath.clear(); + if (found) + { + auto imgPath = shared->buffPath + "/img/" + QFileInfo(vidPath).baseName() + "-%03d" + ".bmp"; + + QProcess ffmpeg; + QStringList args; + QString imgA; + QString imgB; + + args.append("ffmpeg"); + args.append("-hide_banner"); + //args.append("-loglevel"); + //args.append("panic"); + args.append("-y"); + args.append("-i"); + args.append(vidPath); + args.append("-vf"); + args.append("fps=1/1"); + args.append(imgPath); + + ffmpeg.start(args[0], args.mid(1)); + ffmpeg.waitForFinished(); + + for (auto i = 1; i <= SEG_SIZE; ++i) + { + auto imgNum = QString::number(i).rightJustified(3, '0'); + auto imgPath = shared->buffPath + "/img/" + QFileInfo(vidPath).baseName() + "-" + imgNum + ".bmp"; + + if (QFileInfo::exists(imgPath)) + { + if (imgA.isEmpty()) imgA = imgPath; + else if (imgB.isEmpty()) imgB = imgPath; + else + { + eTimer.restart(); + + QProcess extComp; + + auto compArgs = buildArgs(imgA, imgB); + + 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 (score >= shared->imgThresh) + { + qInfo() << "--threshold_meet: " << QString::number(shared->imgThresh); + + if (eventQue.score < score) + { + eventQue.imgPath = imgB; + eventQue.score = score; + } + + eventQue.inQue = true; + eventQue.queAge = 0; + eventQue.timeStamp = QDateTime::currentDateTimeUtc().toString(DATETIME_FMT); + + if (!eventQue.vidList.contains(vidPath)) + { + eventQue.vidList.append(vidPath); + } + } + + imgA.clear(); + imgB.clear(); + } + } + } + + emit loop(); + } + else + { + QTimer::singleShot(SEG_SIZE + 4, this, &DetectLoop::exec); + + dTime = QDateTime::currentDateTimeUtc(); + } } diff --git a/src/detect_loop.h b/src/detect_loop.h index ebd2c5c..0074962 100644 --- a/src/detect_loop.h +++ b/src/detect_loop.h @@ -15,7 +15,7 @@ #include "common.h" -class DetectLoop : public QFileSystemWatcher +class DetectLoop : public QObject { Q_OBJECT @@ -27,31 +27,33 @@ private: QString vidBName; QStringList prevClips; QTimer *pcTimer; + QTimer *evTimer; + QDateTime dTime; QElapsedTimer eTimer; evt_t eventQue; shared_t *shared; float getFloatFromExe(const QByteArray &line); QStringList buildArgs(const QString &prev, const QString &next); - QStringList buildSnapArgs(const QString &vidSrc, const QString &imgPath); private slots: void init(); void reset(); void pcBreak(); - void updated(const QString &path); + void evBreak(); + void exec(); public: explicit DetectLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr); - void exec(); QString statusLine(); signals: void starving(); + void loop(); }; #endif // DETECT_LOOP_H diff --git a/src/record_loop.cpp b/src/record_loop.cpp index eb949b5..d0f8b76 100644 --- a/src/record_loop.cpp +++ b/src/record_loop.cpp @@ -15,6 +15,7 @@ RecordLoop::RecordLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QProcess(parent) { checkTimer = 0; + status = 0; shared = sharedRes; connect(thr, &QThread::started, this, &RecordLoop::init); @@ -23,6 +24,7 @@ RecordLoop::RecordLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QPr connect(this, &RecordLoop::readyReadStandardOutput, this, &RecordLoop::resetTime); connect(this, &RecordLoop::readyReadStandardError, this, &RecordLoop::resetTime); connect(this, &RecordLoop::started, this, &RecordLoop::resetTime); + connect(this, &RecordLoop::finished, this, &RecordLoop::segFinished); moveToThread(thr); } @@ -46,31 +48,56 @@ void RecordLoop::init() restart(); } -QString RecordLoop::camCmdFromConf() +void RecordLoop::segFinished(int retCode) { - auto ret = "ffmpeg -hide_banner -y -i '" + shared->recordUri + "' -strftime 1 -strftime_mkdir 1 "; + status = retCode; + + restart(); +} + +QStringList RecordLoop::camCmdFromConf() +{ + auto tobj = QDateTime::currentDateTimeUtc(); + auto ret = QStringList(); + + ret.append("ffmpeg"); + ret.append("-hide_banner"); + ret.append("-y"); + ret.append("-i"); + ret.append(shared->recordUri); if (shared->recordUri.contains("rtsp")) { - ret += "-rtsp_transport udp "; + ret.append("-rtsp_transport"); + ret.append("udp"); } if (shared->vidCodec != "copy") { - ret += "-vf fps=" + QString::number(shared->recFps) + ",scale=" + shared->recScale + " "; + ret.append("-vf"); + ret.append("fps=" + QString::number(shared->recFps) + ",scale=" + shared->recScale); } - ret += "-vcodec " + shared->vidCodec + " "; - ret += "-acodec " + shared->audCodec + " "; - ret += "-reset_timestamps 1 -sc_threshold 0 -g 2 -force_key_frames 'expr:gte(t, n_forced * 2)' -segment_time 2 -f segment "; - ret += shared->buffPath + "/live/" + QString(STRFTIME_FMT) + shared->streamExt; + ret.append("-vcodec"); ret.append(shared->vidCodec); + ret.append("-acodec"); ret.append(shared->audCodec); + ret.append("-reset_timestamps"); + ret.append("1"); + ret.append("-sc_threshold"); + ret.append("0"); + ret.append("-g"); + ret.append("2"); + ret.append("-force_key_frames"); + ret.append("expr:gte(t, n_forced * 2)"); + ret.append("-t"); + ret.append(QString::number(SEG_SIZE)); + ret.append(shared->buffPath + "/live/" + tobj.toString(DATETIME_FMT) + shared->streamExt); return ret; } QString RecordLoop::statusLine() { - if (state() == QProcess::Running) + if ((status == 0) || (status == 255)) { return "OK "; } @@ -90,20 +117,13 @@ void RecordLoop::restart() if (state() == QProcess::Running) { terminate(); - waitForFinished(); - } - - auto cmdLine = camCmdFromConf(); - auto args = parseArgs(cmdLine.toUtf8(), -1); - - qInfo() << "start recording command: " << cmdLine; - - if (args.isEmpty()) - { - qCritical() << "err: couldn't parse a program name"; } else { + resetTime(); + + auto args = camCmdFromConf(); + setProgram(args[0]); setArguments(args.mid(1)); diff --git a/src/record_loop.h b/src/record_loop.h index 494a779..a62a1d8 100644 --- a/src/record_loop.h +++ b/src/record_loop.h @@ -28,12 +28,14 @@ private: shared_t *shared; QTimer *checkTimer; + int status; - QString camCmdFromConf(); + QStringList camCmdFromConf(); public slots: void restart(); + void segFinished(int retCode); public: