v3.6.t1
-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:
parent
88ea1086f6
commit
ba54b5b44a
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user