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: