From bbad30a5b0a949a3f33bca05054f55c12e067773 Mon Sep 17 00:00:00 2001 From: Maurice ONeal Date: Wed, 17 May 2023 15:06:58 -0400 Subject: [PATCH] v3.0.t2 Added the much need code in Camera object to actual start all of the threads. Added multi instance support via the -d option. Made the Loop object loop structure slot-signal compatible so all objects using it can interupt the main loop to run other slots. --- src/camera.cpp | 74 +++++++++++++++++++++++++++++++++++------------- src/camera.h | 63 +++++++++++++++++++++++++---------------- src/common.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++- src/common.h | 24 +++++++++++++++- src/main.cpp | 24 ++++++++++++---- 5 files changed, 211 insertions(+), 51 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 416318f..6dafa75 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -33,7 +33,7 @@ Camera::Camera(QObject *parent) : QObject(parent) shared.webFont = "courier"; } -bool Camera::start(const QStringList &args) +int Camera::start(const QStringList &args) { auto ret = false; @@ -43,25 +43,55 @@ bool Camera::start(const QStringList &args) { QDir("live").removeRecursively(); + auto thr1 = new QThread(QCoreApplication::instance()); + auto thr2 = new QThread(QCoreApplication::instance()); + auto thr3 = new QThread(QCoreApplication::instance()); + auto thr4 = new QThread(QCoreApplication::instance()); + + new RecLoop(&shared, thr1, this); + new Upkeep(&shared, thr2, this); + new EventLoop(&shared, thr3, this); + new DetectLoop(&shared, thr4, this); + + thr1->start(); + thr2->start(); + thr3->start(); + thr4->start(); } - return ret; + return shared.retCode; } -Loop::Loop(shared_t *sharedRes, QObject *parent) : QObject(parent) +Loop::Loop(shared_t *sharedRes, QThread *thr, QObject *parent) : QObject(0) { shared = sharedRes; heartBeat = 10; + + connect(this, &Loop::loopSig, this, &Loop::loopSlot); + connect(thr, &QThread::started, this, &Loop::init); + connect(parent, &QObject::destroyed, this, &Loop::deleteLater); + connect(parent, &QObject::destroyed, thr, &QThread::terminate); + + moveToThread(thr); } -void Loop::loop() +void Loop::init() { - while (exec()) + emit loopSig(); +} + +void Loop::loopSlot() +{ + auto ret = exec(); + + if (heartBeat != 0) { - if (heartBeat != 0) - { - thread()->sleep(heartBeat); - } + thread()->sleep(heartBeat); + } + + if (ret) + { + emit loopSig(); } } @@ -70,7 +100,7 @@ bool Loop::exec() return shared->retCode == 0; } -RecLoop::RecLoop(shared_t *sharedRes, QObject *parent) : Loop(sharedRes, parent) +RecLoop::RecLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) { once = true; } @@ -95,6 +125,8 @@ void RecLoop::updateCmd() proc.setProgram("ffmpeg"); proc.setArguments(args); + curUrl = shared->recordUrl; + recLog("rec_args_updated: " + args.join(" "), shared); } @@ -110,19 +142,18 @@ void RecLoop::reset() bool RecLoop::exec() { - auto args = proc.arguments(); - auto md5 = genMD5(QString("stream.m3u8")); + auto md5 = genMD5(QString("stream.m3u8")); if (once) { updateCmd(); once = false; streamMD5 = genMD5(QByteArray("FIRST")); } - else if ((args[3] != shared->recordUrl) || (streamMD5 == md5)) + else if ((curUrl != shared->recordUrl) || (streamMD5 == md5)) { reset(); } - auto hashLogLine = "stream_hash prev:" + QString(streamMD5.toHex()) + " new:" + QString(md5.toHex()); + auto hashLogLine = "stream_hash--prev:" + QString(streamMD5.toHex()) + "--new:" + QString(md5.toHex()); recLog(hashLogLine, shared); @@ -144,7 +175,7 @@ bool RecLoop::exec() return Loop::exec(); } -Upkeep::Upkeep(shared_t *sharedRes, QObject *parent) : Loop(sharedRes, parent) {} +Upkeep::Upkeep(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) {} bool Upkeep::exec() { @@ -193,7 +224,7 @@ bool Upkeep::exec() return Loop::exec(); } -EventLoop::EventLoop(shared_t *sharedRes, QObject *parent) : Loop(sharedRes, parent) +EventLoop::EventLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) { heartBeat = 2; } @@ -281,13 +312,18 @@ bool EventLoop::wrOutVod(const evt_t &event) } } -DetectLoop::DetectLoop(shared_t *sharedRes, QObject *parent) : Loop(sharedRes, parent) +DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) { heartBeat = 0; evId = 0; pcId = 0; +} - resetTimers(); +void DetectLoop::init() +{ + thread()->usleep(200); + + resetTimers(); Loop::init(); } void DetectLoop::resetTimers() @@ -326,7 +362,7 @@ void DetectLoop::timerEvent(QTimerEvent *event) shared->maxScore = 0; } - if (event->timerId() == pcId) + if ((event->timerId() == pcId) && (!shared->postCmd.isEmpty())) { detLog("---POST_BREAK---", shared); diff --git a/src/camera.h b/src/camera.h index a3df5ac..834f211 100644 --- a/src/camera.h +++ b/src/camera.h @@ -18,26 +18,6 @@ #include "web.h" #include "mo_detect.h" -class Loop : public QObject -{ - Q_OBJECT - -protected: - - shared_t *shared; - int heartBeat; - -private: - - void loop(); - -public: - - explicit Loop(shared_t *shared, QObject *parent = nullptr); - - virtual bool exec(); -}; - class Camera : public QObject { Q_OBJECT @@ -50,7 +30,35 @@ public: explicit Camera(QObject *parent = nullptr); - bool start(const QStringList &args); + int start(const QStringList &args); +}; + +class Loop : public QObject +{ + Q_OBJECT + +protected: + + shared_t *shared; + int heartBeat; + +protected slots: + + virtual void init(); + +private slots: + + void loopSlot(); + +signals: + + void loopSig(); + +public: + + explicit Loop(shared_t *shared, QThread *thr, QObject *parent = nullptr); + + virtual bool exec(); }; class RecLoop : public Loop @@ -60,6 +68,7 @@ class RecLoop : public Loop private: QProcess proc; + QString curUrl; QByteArray streamMD5; bool once; @@ -68,7 +77,7 @@ private: public: - explicit RecLoop(shared_t *shared, QObject *parent = nullptr); + explicit RecLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr); bool exec(); }; @@ -79,7 +88,7 @@ class Upkeep : public Loop public: - explicit Upkeep(shared_t *shared, QObject *parent = nullptr); + explicit Upkeep(shared_t *shared, QThread *thr, QObject *parent = nullptr); bool exec(); }; @@ -94,7 +103,7 @@ private: public: - explicit EventLoop(shared_t *shared, QObject *parent = nullptr); + explicit EventLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr); bool exec(); }; @@ -112,9 +121,13 @@ private: void resetTimers(); void timerEvent(QTimerEvent *event); +private slots: + + void init(); + public: - explicit DetectLoop(shared_t *shared, QObject *parent = nullptr); + explicit DetectLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr); bool exec(); }; diff --git a/src/common.cpp b/src/common.cpp index 02dff20..8a8ff6d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -190,7 +190,7 @@ bool rdConf(shared_t *share) if (!QDir::setCurrent(share->outDir)) { - QTextStream(stderr) << "err: failed to change/create the current working directory to camera folder: '" << share->outDir << ".' does it exists?" << Qt::endl; + QTextStream(stderr) << "err: failed to change/create the current working directory to camera folder: '" << share->outDir << "' does it exists?" << Qt::endl; share->retCode = ENOENT; } @@ -198,3 +198,78 @@ bool rdConf(shared_t *share) return share->retCode == 0; } + +MultiInstance::MultiInstance(QObject *parent) : QObject(parent) {} + +void MultiInstance::instStdout() +{ + for (auto &&proc : procList) + { + QTextStream(stdout) << proc->readAllStandardOutput(); + } +} + +void MultiInstance::instStderr() +{ + for (auto &&proc : procList) + { + QTextStream(stderr) << proc->readAllStandardError(); + } +} + +void MultiInstance::procFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitCode) + Q_UNUSED(exitStatus) + + for (auto &&proc : procList) + { + if (proc->state() == QProcess::Running) + { + return; + } + } + + QCoreApplication::quit(); +} + +int MultiInstance::start(const QStringList &args) +{ + auto ret = ENOENT; + auto path = getParam("-d", args); + auto files = lsFilesInDir(path); + + if (!QDir(path).exists()) + { + QTextStream(stderr) << "err: the supplied directory in -d '" << path << "' does not exists or is not a directory."; + } + else if (files.isEmpty()) + { + QTextStream(stderr) << "err: no config files found in '" << path << "'"; + } + else + { + for (auto &&conf : files) + { + auto proc = new QProcess(this); + + QStringList subArgs; + + subArgs << "-c" << conf; + + connect(proc, &QProcess::readyReadStandardOutput, this, &MultiInstance::instStdout); + connect(proc, &QProcess::readyReadStandardError, this, &MultiInstance::instStderr); + connect(proc, &QProcess::finished, this, &MultiInstance::procFinished); + + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, proc, &QProcess::terminate); + + proc->setProgram(APP_BIN); + proc->setArguments(subArgs); + proc->start(); + + procList.append(proc); + } + } + + return ret; +} diff --git a/src/common.h b/src/common.h index 8a457f0..951a139 100644 --- a/src/common.h +++ b/src/common.h @@ -30,8 +30,9 @@ using namespace cv; using namespace std; -#define APP_VER "3.0.t1" +#define APP_VER "3.0.t2" #define APP_NAME "Motion Watch" +#define APP_BIN "mow" #define REC_LOG_NAME "rec_log_lines.html" #define DET_LOG_NAME "det_log_lines.html" #define UPK_LOG_NAME "upk_log_lines.html" @@ -86,4 +87,25 @@ void rdLine(const QString ¶m, const QString &line, QString *value); void rdLine(const QString ¶m, const QString &line, int *value); void enforceMaxEvents(shared_t *share); +class MultiInstance : public QObject +{ + Q_OBJECT + +private: + + QList procList; + +private slots: + + void instStdout(); + void instStderr(); + void procFinished(int exitCode, QProcess::ExitStatus exitStatus); + +public: + + explicit MultiInstance(QObject *parent = nullptr); + + int start(const QStringList &args); +}; + #endif // COMMON_H diff --git a/src/main.cpp b/src/main.cpp index 92d8ff9..78779ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,9 +10,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -#include "mo_detect.h" -#include "logger.h" -#include "web.h" +#include "common.h" #include "camera.h" int main(int argc, char** argv) @@ -30,18 +28,34 @@ int main(int argc, char** argv) QTextStream(stdout) << "Motion Watch " << APP_VER << Qt::endl << Qt::endl; QTextStream(stdout) << "Usage: mow " << Qt::endl << Qt::endl; QTextStream(stdout) << "-h : display usage information about this application." << Qt::endl; - QTextStream(stdout) << "-c : path to the config file." << Qt::endl; + QTextStream(stdout) << "-c : path to the config file used to run a single camera instance." << Qt::endl; + QTextStream(stdout) << "-d : path to a directory that can contain multiple config files." << Qt::endl; + QTextStream(stdout) << " each file found in the directory will be used to run a" << Qt::endl; + QTextStream(stdout) << " camera instance." << Qt::endl; QTextStream(stdout) << "-v : display the current version." << Qt::endl << Qt::endl; } else if (args.contains("-v")) { QTextStream(stdout) << APP_VER << Qt::endl; } + else if (args.contains("-d")) + { + auto *muli = new MultiInstance(&app); + + ret = muli->start(args); + + if (ret == 0) + { + ret = QCoreApplication::exec(); + } + } else if (args.contains("-c")) { auto *cam = new Camera(&app); - if (cam->start(args)) + ret = cam->start(args); + + if (ret == 0) { ret = QCoreApplication::exec(); }