-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.
This commit is contained in:
zii 2024-10-09 12:38:49 -04:00
parent 88ea1086f6
commit ba54b5b44a
6 changed files with 138 additions and 176 deletions

View File

@ -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]);

View File

@ -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;

View File

@ -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(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,63 +167,25 @@ 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;
qCritical() << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd;
}
else
{
dTime = dTime.addSecs(1);
QProcess::execute("ffmpeg", snapArgsA);
QProcess::execute("ffmpeg", snapArgsB);
vidPath = shared->buffPath + "/live/" + dTime.toString(DATETIME_FMT) + shared->streamExt;
}
}
if (found)
if (QFile::exists(imgAPath) && QFile::exists(imgBPath))
{
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();
@ -225,38 +202,41 @@ void DetectLoop::exec()
qInfo() << compArgs.join(" ") << " --result: " << QString::number(score);
if (score >= shared->imgThresh)
if (eventQue.inQue)
{
qInfo() << "--threshold_meet: " << QString::number(shared->imgThresh);
eventQue.queAge += 4;
if (eventQue.score < score)
{
eventQue.imgPath = imgB;
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::currentDateTimeUtc().toString(DATETIME_FMT);
eventQue.timeStamp = QDateTime::currentDateTime().toString(DATETIME_FMT);
if (!eventQue.vidList.contains(vidPath))
{
eventQue.vidList.append(vidPath);
}
}
imgA.clear();
imgB.clear();
eventQue.vidList.append(vidAPath);
eventQue.vidList.append(vidBPath);
}
}
}
emit loop();
}
else
{
QTimer::singleShot(SEG_SIZE + 4, this, &DetectLoop::exec);
dTime = QDateTime::currentDateTimeUtc();
}
}

View File

@ -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

View File

@ -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));

View File

@ -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: