From ba54b5b44a7da86fbdc35fe721f65dad48d5a548 Mon Sep 17 00:00:00 2001 From: zii Date: Wed, 9 Oct 2024 12:38:49 -0400 Subject: [PATCH] v3.6.t1 -updated the way the video clips from the live stream are named to simple unsigned int number increments. doing this makes the file names much more predictable and removes the need for filesystem monitoring. --- src/common.cpp | 4 +- src/common.h | 4 +- src/detect_loop.cpp | 222 ++++++++++++++++++++------------------------ src/detect_loop.h | 11 +-- src/record_loop.cpp | 69 ++++++-------- src/record_loop.h | 4 +- 6 files changed, 138 insertions(+), 176 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 1ff29ce..6e49eb6 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() > MAX_IMAGES) + while (names.size() > (share->liveSecs / 2)) { 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 / SEG_SIZE)) + while (names.size() > (share->liveSecs / 2)) { QFile::remove(share->buffPath + "/live/" + names[0]); diff --git a/src/common.h b/src/common.h index 707b988..62e01f3 100644 --- a/src/common.h +++ b/src/common.h @@ -36,11 +36,8 @@ using namespace std; #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 { @@ -76,6 +73,7 @@ struct shared_t QString thumbExt; QString recScale; QString imgScale; + quint64 seed; bool singleTenant; bool skipCmd; int liveSecs; diff --git a/src/detect_loop.cpp b/src/detect_loop.cpp index b763ead..63c5927 100644 --- a/src/detect_loop.cpp +++ b/src/detect_loop.cpp @@ -15,6 +15,7 @@ DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QObject(parent) { pcTimer = 0; + deTimer = 0; shared = sharedRes; connect(thr, &QThread::started, this, &DetectLoop::init); @@ -26,21 +27,15 @@ DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QOb void DetectLoop::init() { pcTimer = new QTimer(this); - evTimer = new QTimer(this); - dTime = QDateTime::currentDateTimeUtc(); + deTimer = new QTimer(this); - connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak); - connect(evTimer, &QTimer::timeout, this, &DetectLoop::evBreak); - connect(this, &DetectLoop::loop, this, &DetectLoop::exec); + connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak); + connect(deTimer, &QTimer::timeout, this, &DetectLoop::preExec); pcTimer->start(shared->postSecs * 1000); - evTimer->start(shared->evMaxSecs * 1000); + deTimer->start(2000); setupBuffDir(shared->buffPath); - - eTimer.start(); - - QTimer::singleShot(SEG_SIZE + 4, this, &DetectLoop::exec); } void DetectLoop::reset() @@ -54,6 +49,19 @@ void DetectLoop::reset() eventQue.timeStamp.clear(); } +void DetectLoop::preExec() +{ + vidAPath = shared->buffPath + "/live/" + QString::number(shared->seed - 2).leftJustified(21, '0') + shared->streamExt; + vidBPath = shared->buffPath + "/live/" + QString::number(shared->seed - 1).leftJustified(21, '0') + shared->streamExt; + + eTimer.start(); + + if (QFileInfo::exists(vidAPath) && QFileInfo::exists(vidBPath)) + { + exec(); + } +} + void DetectLoop::pcBreak() { prevClips.clear(); @@ -84,16 +92,6 @@ void DetectLoop::pcBreak() } } -void DetectLoop::evBreak() -{ - if (eventQue.inQue) - { - shared->recList.append(eventQue); - - reset(); - } -} - float DetectLoop::getFloatFromExe(const QByteArray &line) { QString strLine(line); @@ -136,9 +134,26 @@ 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() >= 30000) + if (eTimer.elapsed() >= 5000) { emit starving(); @@ -152,111 +167,76 @@ QString DetectLoop::statusLine() void DetectLoop::exec() { - auto vidPath = shared->buffPath + "/live/" + dTime.toString(DATETIME_FMT) + shared->streamExt; - auto found = false; + 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); - for (auto i = 0; i < 100; ++i) + if (compArgs.isEmpty()) { - if (QFileInfo::exists(vidPath)) - { - found = true; break; - } - else - { - dTime = dTime.addSecs(1); - - vidPath = shared->buffPath + "/live/" + dTime.toString(DATETIME_FMT) + shared->streamExt; - } - } - - 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(); + qCritical() << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd; } else { - QTimer::singleShot(SEG_SIZE + 4, this, &DetectLoop::exec); + QProcess::execute("ffmpeg", snapArgsA); + QProcess::execute("ffmpeg", snapArgsB); - dTime = QDateTime::currentDateTimeUtc(); + 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); + } + } } } diff --git a/src/detect_loop.h b/src/detect_loop.h index 0074962..305a427 100644 --- a/src/detect_loop.h +++ b/src/detect_loop.h @@ -23,37 +23,34 @@ private: QString vidAPath; QString vidBPath; - QString vidAName; - QString vidBName; QStringList prevClips; QTimer *pcTimer; - QTimer *evTimer; - QDateTime dTime; + QTimer *deTimer; 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 evBreak(); - void exec(); + void preExec(); 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 d0f8b76..9624f0a 100644 --- a/src/record_loop.cpp +++ b/src/record_loop.cpp @@ -15,16 +15,16 @@ RecordLoop::RecordLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : QProcess(parent) { checkTimer = 0; - status = 0; shared = sharedRes; + sharedRes->seed = 0; + connect(thr, &QThread::started, this, &RecordLoop::init); connect(thr, &QThread::finished, this, &RecordLoop::deleteLater); 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); } @@ -48,56 +48,38 @@ void RecordLoop::init() restart(); } -void RecordLoop::segFinished(int retCode) +QString RecordLoop::camCmdFromConf() { - 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); + auto ret = "ffmpeg -hide_banner -y -i '" + shared->recordUri + "' -strftime 1 -strftime_mkdir 1 "; if (shared->recordUri.contains("rtsp")) { - ret.append("-rtsp_transport"); - ret.append("udp"); + ret += "-rtsp_transport udp "; + } + + if (shared->seed == 18446744073709551615ULL) + { + setupBuffDir(shared->buffPath, true); + + shared->seed = 0; } if (shared->vidCodec != "copy") { - ret.append("-vf"); - ret.append("fps=" + QString::number(shared->recFps) + ",scale=" + shared->recScale); + ret += "-vf fps=" + QString::number(shared->recFps) + ",scale=" + shared->recScale + " "; } - 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); + 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::number(shared->seed++).leftJustified(21, '0') + shared->streamExt; return ret; } QString RecordLoop::statusLine() { - if ((status == 0) || (status == 255)) + if (state() == QProcess::Running) { return "OK "; } @@ -117,13 +99,20 @@ 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 a62a1d8..494a779 100644 --- a/src/record_loop.h +++ b/src/record_loop.h @@ -28,14 +28,12 @@ private: shared_t *shared; QTimer *checkTimer; - int status; - QStringList camCmdFromConf(); + QString camCmdFromConf(); public slots: void restart(); - void segFinished(int retCode); public: