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.
This commit is contained in:
Maurice ONeal 2023-05-17 15:06:58 -04:00
parent b5ebbace12
commit bbad30a5b0
5 changed files with 211 additions and 51 deletions

View File

@ -33,7 +33,7 @@ Camera::Camera(QObject *parent) : QObject(parent)
shared.webFont = "courier"; shared.webFont = "courier";
} }
bool Camera::start(const QStringList &args) int Camera::start(const QStringList &args)
{ {
auto ret = false; auto ret = false;
@ -43,25 +43,55 @@ bool Camera::start(const QStringList &args)
{ {
QDir("live").removeRecursively(); 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; shared = sharedRes;
heartBeat = 10; 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; 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; once = true;
} }
@ -95,6 +125,8 @@ void RecLoop::updateCmd()
proc.setProgram("ffmpeg"); proc.setProgram("ffmpeg");
proc.setArguments(args); proc.setArguments(args);
curUrl = shared->recordUrl;
recLog("rec_args_updated: " + args.join(" "), shared); recLog("rec_args_updated: " + args.join(" "), shared);
} }
@ -110,19 +142,18 @@ void RecLoop::reset()
bool RecLoop::exec() bool RecLoop::exec()
{ {
auto args = proc.arguments();
auto md5 = genMD5(QString("stream.m3u8")); auto md5 = genMD5(QString("stream.m3u8"));
if (once) if (once)
{ {
updateCmd(); once = false; streamMD5 = genMD5(QByteArray("FIRST")); updateCmd(); once = false; streamMD5 = genMD5(QByteArray("FIRST"));
} }
else if ((args[3] != shared->recordUrl) || (streamMD5 == md5)) else if ((curUrl != shared->recordUrl) || (streamMD5 == md5))
{ {
reset(); 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); recLog(hashLogLine, shared);
@ -144,7 +175,7 @@ bool RecLoop::exec()
return Loop::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() bool Upkeep::exec()
{ {
@ -193,7 +224,7 @@ bool Upkeep::exec()
return Loop::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; 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; heartBeat = 0;
evId = 0; evId = 0;
pcId = 0; pcId = 0;
}
resetTimers(); void DetectLoop::init()
{
thread()->usleep(200);
resetTimers(); Loop::init();
} }
void DetectLoop::resetTimers() void DetectLoop::resetTimers()
@ -326,7 +362,7 @@ void DetectLoop::timerEvent(QTimerEvent *event)
shared->maxScore = 0; shared->maxScore = 0;
} }
if (event->timerId() == pcId) if ((event->timerId() == pcId) && (!shared->postCmd.isEmpty()))
{ {
detLog("---POST_BREAK---", shared); detLog("---POST_BREAK---", shared);

View File

@ -18,26 +18,6 @@
#include "web.h" #include "web.h"
#include "mo_detect.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 class Camera : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -50,7 +30,35 @@ public:
explicit Camera(QObject *parent = nullptr); 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 class RecLoop : public Loop
@ -60,6 +68,7 @@ class RecLoop : public Loop
private: private:
QProcess proc; QProcess proc;
QString curUrl;
QByteArray streamMD5; QByteArray streamMD5;
bool once; bool once;
@ -68,7 +77,7 @@ private:
public: public:
explicit RecLoop(shared_t *shared, QObject *parent = nullptr); explicit RecLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr);
bool exec(); bool exec();
}; };
@ -79,7 +88,7 @@ class Upkeep : public Loop
public: public:
explicit Upkeep(shared_t *shared, QObject *parent = nullptr); explicit Upkeep(shared_t *shared, QThread *thr, QObject *parent = nullptr);
bool exec(); bool exec();
}; };
@ -94,7 +103,7 @@ private:
public: public:
explicit EventLoop(shared_t *shared, QObject *parent = nullptr); explicit EventLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr);
bool exec(); bool exec();
}; };
@ -112,9 +121,13 @@ private:
void resetTimers(); void resetTimers();
void timerEvent(QTimerEvent *event); void timerEvent(QTimerEvent *event);
private slots:
void init();
public: public:
explicit DetectLoop(shared_t *shared, QObject *parent = nullptr); explicit DetectLoop(shared_t *shared, QThread *thr, QObject *parent = nullptr);
bool exec(); bool exec();
}; };

View File

@ -190,7 +190,7 @@ bool rdConf(shared_t *share)
if (!QDir::setCurrent(share->outDir)) 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; share->retCode = ENOENT;
} }
@ -198,3 +198,78 @@ bool rdConf(shared_t *share)
return share->retCode == 0; 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;
}

View File

@ -30,8 +30,9 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
#define APP_VER "3.0.t1" #define APP_VER "3.0.t2"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define APP_BIN "mow"
#define REC_LOG_NAME "rec_log_lines.html" #define REC_LOG_NAME "rec_log_lines.html"
#define DET_LOG_NAME "det_log_lines.html" #define DET_LOG_NAME "det_log_lines.html"
#define UPK_LOG_NAME "upk_log_lines.html" #define UPK_LOG_NAME "upk_log_lines.html"
@ -86,4 +87,25 @@ void rdLine(const QString &param, const QString &line, QString *value);
void rdLine(const QString &param, const QString &line, int *value); void rdLine(const QString &param, const QString &line, int *value);
void enforceMaxEvents(shared_t *share); void enforceMaxEvents(shared_t *share);
class MultiInstance : public QObject
{
Q_OBJECT
private:
QList<QProcess*> 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 #endif // COMMON_H

View File

@ -10,9 +10,7 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
#include "mo_detect.h" #include "common.h"
#include "logger.h"
#include "web.h"
#include "camera.h" #include "camera.h"
int main(int argc, char** argv) 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) << "Motion Watch " << APP_VER << Qt::endl << Qt::endl;
QTextStream(stdout) << "Usage: mow <argument>" << Qt::endl << Qt::endl; QTextStream(stdout) << "Usage: mow <argument>" << Qt::endl << Qt::endl;
QTextStream(stdout) << "-h : display usage information about this application." << 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; QTextStream(stdout) << "-v : display the current version." << Qt::endl << Qt::endl;
} }
else if (args.contains("-v")) else if (args.contains("-v"))
{ {
QTextStream(stdout) << APP_VER << Qt::endl; 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")) else if (args.contains("-c"))
{ {
auto *cam = new Camera(&app); auto *cam = new Camera(&app);
if (cam->start(args)) ret = cam->start(args);
if (ret == 0)
{ {
ret = QCoreApplication::exec(); ret = QCoreApplication::exec();
} }