-removed use of evtHist. will instead allow eventLoop to que up
 duplicate live video clips and then remove later using
 QStringList::removeDuplicates().

-changed up the ffmpeg commands to utilize tcp and re-added a tcp
 timeout argument, removing the need for command stall checking.

-added logic to pick the snapshot with the highest diff score as
 the event thumbnail.
This commit is contained in:
Zii 2023-06-09 16:24:32 -04:00
parent 16312a93f5
commit 4b4c2649b8
4 changed files with 108 additions and 84 deletions

View File

@ -83,6 +83,8 @@ void Loop::init()
loopTimer->setSingleShot(false); loopTimer->setSingleShot(false);
loopTimer->start(heartBeat * 1000); loopTimer->start(heartBeat * 1000);
loopSlot();
} }
void Loop::loopSlot() void Loop::loopSlot()
@ -105,8 +107,6 @@ bool Loop::exec()
RecLoop::RecLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) RecLoop::RecLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
{ {
once = true;
baseListRdy = false;
recProc = 0; recProc = 0;
imgProc = 0; imgProc = 0;
} }
@ -118,6 +118,8 @@ void RecLoop::init()
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &RecLoop::term); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &RecLoop::term);
updateCmd();
Loop::init(); Loop::init();
} }
@ -131,18 +133,23 @@ void RecLoop::updateCmd()
recArgs << "-strftime" << "1"; recArgs << "-strftime" << "1";
recArgs << "-strftime_mkdir" << "1"; recArgs << "-strftime_mkdir" << "1";
recArgs << "-hls_segment_filename" << "live/" + QString(STRFTIME_FMT) + ".ts"; recArgs << "-hls_segment_filename" << "live/" + QString(STRFTIME_FMT) + ".ts";
recArgs << "-hls_flags" << "delete_segments";
recArgs << "-y"; recArgs << "-y";
recArgs << "-vcodec" << "copy"; recArgs << "-vcodec" << "copy";
recArgs << "-f" << "hls"; recArgs << "-f" << "hls";
recArgs << "-hls_time" << "2"; recArgs << "-hls_time" << "2";
recArgs << "-hls_list_size" << "1000"; recArgs << "-hls_list_size" << "1000";
recArgs << "-hls_flags" << "append_list";
recArgs << "-rtsp_transport" << "tcp";
recArgs << "-stimeout" << "3000";
recArgs << "stream.m3u8"; recArgs << "stream.m3u8";
imgArgs << "-hide_banner"; imgArgs << "-hide_banner";
imgArgs << "-i" << shared->recordUrl; imgArgs << "-i" << shared->recordUrl;
imgArgs << "-strftime" << "1"; imgArgs << "-strftime" << "1";
imgArgs << "-strftime_mkdir" << "1";
imgArgs << "-vf" << "fps=1,scale=320:240"; imgArgs << "-vf" << "fps=1,scale=320:240";
imgArgs << "-rtsp_transport" << "tcp";
imgArgs << "-stimeout" << "3000";
imgArgs << "img/" + QString(STRFTIME_FMT) + ".bmp"; imgArgs << "img/" + QString(STRFTIME_FMT) + ".bmp";
recProc->setProgram("ffmpeg"); recProc->setProgram("ffmpeg");
@ -151,10 +158,10 @@ void RecLoop::updateCmd()
imgProc->setProgram("ffmpeg"); imgProc->setProgram("ffmpeg");
imgProc->setArguments(imgArgs); imgProc->setArguments(imgArgs);
curUrl = shared->recordUrl; recLog("rec_args: " + recArgs.join(" "), shared);
recLog("img_args: " + imgArgs.join(" "), shared);
recLog("rec_args_updated: " + recArgs.join(" "), shared); curUrl = shared->recordUrl;
recLog("img_args_updated: " + imgArgs.join(" "), shared);
} }
void RecLoop::term() void RecLoop::term()
@ -170,12 +177,27 @@ void RecLoop::reset()
{ {
recLog("--rec_and_img_cmds_resetting--", shared); recLog("--rec_and_img_cmds_resetting--", shared);
baseListRdy = false;
term(); term();
updateCmd(); updateCmd();
} }
void RecLoop::procError(const QString &desc, QProcess *proc)
{
if (proc->isOpen() && proc->state() == QProcess::NotRunning)
{
auto errBlob = QString(proc->readAllStandardError());
auto errLines = errBlob.split('\n');
if (!errLines.isEmpty())
{
for (auto &&line : errLines)
{
recLog(desc + "_cmd_stderr: " + line, shared);
}
}
}
}
void RecLoop::startProc(const QString &desc, QProcess *proc) void RecLoop::startProc(const QString &desc, QProcess *proc)
{ {
if (proc->state() == QProcess::NotRunning) if (proc->state() == QProcess::NotRunning)
@ -189,32 +211,17 @@ void RecLoop::startProc(const QString &desc, QProcess *proc)
else else
{ {
recLog(desc + "_cmd_start: fail", shared); recLog(desc + "_cmd_start: fail", shared);
recLog(desc + "_cmd_stderr: " + QString(proc->readAllStandardError()), shared); procError(desc, proc);
} }
} }
} }
bool RecLoop::exec() bool RecLoop::exec()
{ {
if (once) procError("img", imgProc);
{ procError("rec", recProc);
updateCmd(); once = false;
} if (curUrl != shared->recordUrl)
else if (!baseListRdy)
{
baseListRdy = true;
}
else if (backwardFacingFiles("live", ".ts", QDateTime::currentDateTime(), heartBeat).isEmpty())
{
recLog("backward facing files in the live stream are empty. cmd stall is suspected.", shared);
reset();
}
else if (backwardFacingFiles("img", ".bmp", QDateTime::currentDateTime(), heartBeat).isEmpty())
{
recLog("backward facing files in the image stream are empty. cmd stall is suspected.", shared);
reset();
}
else if (curUrl != shared->recordUrl)
{ {
recLog("a change in the recording URL was detected.", shared); recLog("a change in the recording URL was detected.", shared);
reset(); reset();
@ -247,6 +254,7 @@ bool Upkeep::exec()
initLogFrontPages(); initLogFrontPages();
enforceMaxEvents(shared); enforceMaxEvents(shared);
enforceMaxImages(); enforceMaxImages();
enforceMaxVids();
if (shared->evtHist.size() >= MAX_EVTHIST) if (shared->evtHist.size() >= MAX_EVTHIST)
{ {
@ -263,27 +271,32 @@ bool Upkeep::exec()
EventLoop::EventLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) EventLoop::EventLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
{ {
heartBeat = 5; heartBeat = 2;
} }
bool EventLoop::exec() bool EventLoop::exec()
{ {
if (!shared->recList.isEmpty()) QStringList vidList;
QString imgPath;
QString name;
float highScore = 0;
while (!shared->recList.isEmpty())
{ {
auto event = shared->recList[0]; auto event = shared->recList[0];
auto name = event.timeStamp.toString(DATETIME_FMT); auto maxFiles = shared->evMaxSecs / 2;
auto maxFiles = shared->evMaxSecs / 2; //there's 2 secs in each hls segment // there's 2 secs in each hls segment
auto vidList = backwardFacingFiles("live", ".ts", event.timeStamp, maxFiles / 2);
if (vidList.isEmpty()) if (highScore < event.score)
{ {
recLog("err: no backward faces files were found for event: " + name, shared); name = event.timeStamp.toString(DATETIME_FMT);
imgPath = event.imgPath;
highScore = event.score;
} }
else
{
vidList.removeLast();
vidList += forwardFacingFiles("live", ".ts", event.timeStamp, maxFiles / 2); vidList.append(backwardFacingFiles("live", ".ts", event.timeStamp, maxFiles / 2));
vidList.removeLast();
vidList.append(forwardFacingFiles("live", ".ts", event.timeStamp, maxFiles / 2));
for (auto &&hist : shared->evtHist) for (auto &&hist : shared->evtHist)
{ {
@ -293,10 +306,13 @@ bool EventLoop::exec()
} }
} }
if (!vidList.isEmpty()) shared->recList.removeFirst();
{ }
shared->evtHist.append(vidList);
vidList.removeDuplicates();
if (!vidList.size() > 1)
{
recLog("attempting write out of event: " + name, shared); recLog("attempting write out of event: " + name, shared);
if (wrOutVod(name, vidList)) if (wrOutVod(name, vidList))
@ -307,17 +323,13 @@ bool EventLoop::exec()
QStringList args; QStringList args;
args << "convert"; args << "convert";
args << event.imgPath; args << imgPath;
args << "events/" + name + ".jpg"; args << "events/" + name + ".jpg";
proc.start("magick", args); proc.start("magick", args);
proc.waitForFinished(); proc.waitForFinished();
} }
} }
}
shared->recList.removeFirst();
}
return Loop::exec(); return Loop::exec();
} }
@ -389,9 +401,9 @@ DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loo
{ {
pcTimer = 0; pcTimer = 0;
heartBeat = 2; heartBeat = 2;
delayCycles = 4; // this will be used to delay the delayCycles = 8; // this will be used to delay the
// actual start of DetectLoop by // actual start of DetectLoop by
// 8secs. // 16secs.
} }
void DetectLoop::init() void DetectLoop::init()
@ -474,13 +486,16 @@ bool DetectLoop::exec()
detLog(extComp.program() + " " + args.join(" ") + " --result: " + output, shared); detLog(extComp.program() + " " + args.join(" ") + " --result: " + output, shared);
if (output.toFloat() >= shared->imgThresh) auto score = output.toFloat();
if (score >= shared->imgThresh)
{ {
detLog("--threshold_breached: " + QString::number(shared->imgThresh), shared); detLog("--threshold_breached: " + QString::number(shared->imgThresh), shared);
evt_t event; evt_t event;
event.timeStamp = curDT; event.timeStamp = curDT;
event.score = score;
event.imgPath = images[pos]; event.imgPath = images[pos];
shared->recList.append(event); mod = true; shared->recList.append(event); mod = true;

View File

@ -65,22 +65,16 @@ private:
QProcess *recProc; QProcess *recProc;
QProcess *imgProc; QProcess *imgProc;
QStringList recList;
QStringList imgList;
QString curUrl; QString curUrl;
bool baseListRdy;
bool once;
void updateCmd(); void updateCmd();
void reset(); void reset();
void startProc(const QString &desc, QProcess *proc); void startProc(const QString &desc, QProcess *proc);
void procError(const QString &desc, QProcess *proc);
private slots: private slots:
void init(); void init();
public slots:
void term(); void term();
public: public:

View File

@ -139,6 +139,18 @@ void enforceMaxImages()
} }
} }
void enforceMaxVids()
{
auto names = lsFilesInDir("live", ".ts");
while (names.size() > MAX_VIDEOS)
{
QFile::remove("live/" + names[0]);
names.removeFirst();
}
}
void rdLine(const QString &param, const QString &line, QString *value) void rdLine(const QString &param, const QString &line, QString *value)
{ {
if (line.startsWith(param)) if (line.startsWith(param))

View File

@ -28,7 +28,7 @@
using namespace std; using namespace std;
#define APP_VER "3.0.t12" #define APP_VER "3.0.t13"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define APP_BIN "mow" #define APP_BIN "mow"
#define REC_LOG_NAME "rec_log_lines.html" #define REC_LOG_NAME "rec_log_lines.html"
@ -37,12 +37,14 @@ using namespace std;
#define DATETIME_FMT "yyyyMMddhhmmss" #define DATETIME_FMT "yyyyMMddhhmmss"
#define STRFTIME_FMT "%Y%m%d%H%M%S" #define STRFTIME_FMT "%Y%m%d%H%M%S"
#define MAX_IMAGES 1000 #define MAX_IMAGES 1000
#define MAX_VIDEOS 1000
#define MAX_EVTHIST 100 #define MAX_EVTHIST 100
struct evt_t struct evt_t
{ {
QDateTime timeStamp; QDateTime timeStamp;
QString imgPath; QString imgPath;
float score;
}; };
struct shared_t struct shared_t
@ -81,6 +83,7 @@ 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);
void enforceMaxImages(); void enforceMaxImages();
void enforceMaxVids();
class MultiInstance : public QObject class MultiInstance : public QObject
{ {