-completely reformed the eventloop code to be more efficent and
 removed the use backward/forward facing functions.
-added the live_secs config option that limits the amount of live
 footage the app will record to the buffer directory.
-moved away from ffmpeg hls formatting. live footage will instead
 just be recorded in clips inside of the 'live' directory, the
 stream.m3u8 file will not longer be created. doing this removes
 the codec/container limitations that hls imposed.
-changed up the default conf values that better suits a low spec
 machine like a resberrypi.
This commit is contained in:
Zii 2023-11-05 18:44:50 -05:00
parent 525c342c0f
commit 60a24c9d67
6 changed files with 174 additions and 218 deletions

View File

@ -20,15 +20,12 @@ int Camera::start(const QStringList &args)
{ {
auto thr1 = new QThread(nullptr); auto thr1 = new QThread(nullptr);
auto thr2 = new QThread(nullptr); auto thr2 = new QThread(nullptr);
auto thr3 = new QThread(nullptr);
new Upkeep(&shared, thr1, nullptr); new EventLoop(&shared, thr1, nullptr);
new EventLoop(&shared, thr2, nullptr); new DetectLoop(&shared, thr2, nullptr);
new DetectLoop(&shared, thr3, nullptr);
thr1->start(); thr1->start();
thr2->start(); thr2->start();
thr3->start();
} }
return shared.retCode; return shared.retCode;
@ -75,103 +72,22 @@ bool Loop::exec()
return shared->retCode == 0; return shared->retCode == 0;
} }
Upkeep::Upkeep(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) {}
bool Upkeep::exec()
{
enforceMaxEvents(shared);
enforceMaxImages(shared);
enforceMaxVids(shared);
return Loop::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 = 2; heartBeat = 2;
highScore = 0;
cycles = 0;
} }
bool EventLoop::exec() bool EventLoop::wrOutVod(const evt_t &event)
{ {
if (!vidList.isEmpty())
{
vidList.removeDuplicates();
if (vidList.size() > 1)
{
QTextStream(stdout) << "attempting write out of event: " << name << Qt::endl;
if (wrOutVod())
{
QProcess proc;
QStringList args;
args << "convert";
args << imgPath;
args << shared->recPath + "/" + name + shared->thumbExt;
proc.start("magick", args);
proc.waitForFinished();
}
}
cycles = 0;
highScore = 0;
vidList.clear();
}
QList<int> rmIndx;
for (auto i = 0; i < shared->recList.size(); ++i)
{
auto event = &shared->recList[i];
if (highScore < event->score)
{
name = event->timeStamp.toString(DATETIME_FMT);
imgPath = event->imgPath;
highScore = event->score;
}
if (event->queAge >= (shared->evMaxSecs / heartBeat))
{
auto maxSecs = shared->evMaxSecs / 2;
// half the maxsecs value to get front-back half secs
auto backFiles = backwardFacingFiles(shared->buffPath + "/live", shared->streamExt, event->timeStamp, maxSecs);
auto frontFiles = forwardFacingFiles(shared->buffPath + "/live", shared->streamExt, event->timeStamp, maxSecs);
vidList.append(backFiles + frontFiles);
rmIndx.append(i);
}
else
{
event->queAge += heartBeat;
}
}
for (auto i : rmIndx)
{
shared->recList.removeAt(i);
}
return Loop::exec();
}
bool EventLoop::wrOutVod()
{
auto cnt = 0;
auto concat = name + ".tmp";
auto ret = false; auto ret = false;
auto cnt = 0;
auto concat = shared->buffPath + "/live/" + event.timeStamp + ".ctmp";
QFile file(concat); QFile file(concat, this);
file.open(QFile::WriteOnly); file.open(QFile::WriteOnly);
for (auto &&vid : vidList) for (auto &&vid : event.vidList)
{ {
QTextStream(stdout) << "event_src: " << vid << Qt::endl; QTextStream(stdout) << "event_src: " << vid << Qt::endl;
@ -189,7 +105,7 @@ bool EventLoop::wrOutVod()
if (cnt == 0) if (cnt == 0)
{ {
QTextStream(stderr) << "err: none of the event hls clips exists, canceling write out." << Qt::endl; QTextStream(stderr) << "err: none of the event hls clips exists, cancelling write out." << Qt::endl;
QFile::remove(concat); QFile::remove(concat);
} }
@ -202,29 +118,59 @@ bool EventLoop::wrOutVod()
args << "-safe" << "0"; args << "-safe" << "0";
args << "-i" << concat; args << "-i" << concat;
args << "-c" << "copy"; args << "-c" << "copy";
args << shared->recPath + "/" + name + shared->recExt; args << shared->recPath + "/" + event.timeStamp + shared->recExt;
QProcess::execute("ffmpeg", args); if (QProcess::execute("ffmpeg", args) == 0)
{
ret = true;
}
QFile::remove(concat); QFile::remove(concat);
} }
return ret; return ret;
} }
bool EventLoop::exec()
{
enforceMaxEvents(shared);
enforceMaxImages(shared);
enforceMaxClips(shared);
if (!shared->recList.isEmpty())
{
auto event = shared->recList.takeFirst();
QTextStream(stdout) << "attempting write out of event: " << event.timeStamp << Qt::endl;
if (wrOutVod(event))
{
QStringList args;
args << "convert";
args << event.imgPath;
args << shared->recPath + "/" + event.timeStamp + shared->thumbExt;
QProcess::execute("magick", args);
}
}
return Loop::exec();
}
DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
{ {
seed = 0; pcTimer = 0;
pcTimer = 0; heartBeat = 2;
heartBeat = 2;
delayCycles = 12; // this will be used to delay the
// actual start of DetectLoop by
// 24secs.
} }
void DetectLoop::init() void DetectLoop::init()
{ {
pcTimer = new QTimer(this); pcTimer = new QTimer(this);
mod = false;
eventQue.queAge = 0;
eventQue.score = 0;
eventQue.inQue = false;
connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak); connect(pcTimer, &QTimer::timeout, this, &DetectLoop::pcBreak);
@ -244,15 +190,12 @@ void DetectLoop::pcBreak()
{ {
QTextStream(stdout) << "---POST_BREAK---" << Qt::endl; QTextStream(stdout) << "---POST_BREAK---" << Qt::endl;
if (mod) if (eventQue.inQue)
{ {
QTextStream(stdout) << "motion detected, skipping the post command." << Qt::endl; QTextStream(stdout) << "motion detected, skipping the post command." << Qt::endl;
} }
else else
{ {
if (delayCycles == 0) delayCycles = 5;
else delayCycles += 5;
QTextStream(stdout) << "no motion detected, running post command: " << shared->postCmd << Qt::endl; QTextStream(stdout) << "no motion detected, running post command: " << shared->postCmd << Qt::endl;
auto args = parseArgs(shared->postCmd.toUtf8(), -1); auto args = parseArgs(shared->postCmd.toUtf8(), -1);
@ -267,8 +210,6 @@ void DetectLoop::pcBreak()
} }
} }
} }
mod = false;
} }
float DetectLoop::getFloatFromExe(const QByteArray &line) float DetectLoop::getFloatFromExe(const QByteArray &line)
@ -288,7 +229,16 @@ float DetectLoop::getFloatFromExe(const QByteArray &line)
} }
} }
return strNum.toFloat(); auto ok = false;
auto res = strNum.toFloat(&ok);
if (!ok || strNum.isEmpty())
{
QTextStream(stderr) << "err: the image comp command returned unexpected output and couldn't be converted to float." << Qt::endl;
QTextStream(stderr) << " raw output: " << line << Qt::endl;
}
return res;
} }
QStringList DetectLoop::buildArgs(const QString &prev, const QString &next) QStringList DetectLoop::buildArgs(const QString &prev, const QString &next)
@ -308,6 +258,7 @@ QStringList DetectLoop::buildSnapArgs(const QString &vidSrc, const QString &imgP
{ {
QStringList ret; QStringList ret;
ret.append("-hide_banner");
ret.append("-y"); ret.append("-y");
ret.append("-i"); ret.append("-i");
ret.append(vidSrc); ret.append(vidSrc);
@ -320,88 +271,95 @@ QStringList DetectLoop::buildSnapArgs(const QString &vidSrc, const QString &imgP
bool DetectLoop::exec() bool DetectLoop::exec()
{ {
if (delayCycles > 0) if (eventQue.inQue)
{ {
delayCycles -= 1; eventQue.queAge += heartBeat;
}
QTextStream(stdout) << "delay: detection cycle skipped. cycles left to be skipped: " << QString::number(delayCycles) << Qt::endl; auto clips = lsFilesInDir(shared->buffPath + "/live", shared->streamExt);
if (clips.size() < 2)
{
QTextStream(stdout) << "warning: didn't pick up enough clips files from the video stream. number of files: " << QString::number(clips.size()) << Qt::endl;
QTextStream(stdout) << " will try again on the next loop." << Qt::endl;
} }
else else
{ {
auto imgAPath = "img/" + QString::number(seed++) + ".bmp"; auto vidAPath = shared->buffPath + "/live/" + clips[clips.size() - 2];
auto imgBPath = "img/" + QString::number(seed++) + ".bmp"; auto vidBPath = shared->buffPath + "/live/" + clips[clips.size() - 1];
auto curDT = QDateTime::currentDateTime(); auto imgAPath = shared->buffPath + "/img/" + QFileInfo(vidAPath).baseName() + ".bmp";
auto clips = backwardFacingFiles("live", shared->streamExt, curDT, 16); auto imgBPath = shared->buffPath + "/img/" + QFileInfo(vidBPath).baseName() + ".bmp";
auto snapArgsA = buildSnapArgs(vidAPath, imgAPath);
auto snapArgsB = buildSnapArgs(vidBPath, imgBPath);
auto compArgs = buildArgs(imgAPath, imgBPath);
if (clips.size() < 2) if (compArgs.isEmpty())
{ {
QTextStream(stdout) << "warning: didn't pick up enough clips files from the video stream. number of files: " << QString::number(clips.size()) << Qt::endl; QTextStream(stderr) << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd << Qt::endl;
QTextStream(stdout) << " will try again on the next loop." << Qt::endl;
} }
else else
{ {
auto snapArgsA = buildSnapArgs(clips[clips.size() - 2], imgAPath); QProcess::execute("ffmpeg", snapArgsA);
auto snapArgsB = buildSnapArgs(clips[clips.size() - 1], imgAPath); QProcess::execute("ffmpeg", snapArgsB);
auto compArgs = buildArgs(imgAPath, imgBPath);
if (compArgs.isEmpty()) if (QFile::exists(imgAPath) && QFile::exists(imgBPath))
{ {
QTextStream(stderr) << "err: could not parse a executable name from img_comp_cmd: " << shared->compCmd << Qt::endl;
}
else
{
QProcess snapFromVidA;
QProcess snapFromVidB;
QProcess extComp; QProcess extComp;
snapFromVidA.start("ffmpeg", snapArgsA);
snapFromVidA.waitForFinished();
snapFromVidB.start("ffmpeg", snapArgsB);
snapFromVidB.waitForFinished();
extComp.start(compArgs[0], compArgs.mid(1)); extComp.start(compArgs[0], compArgs.mid(1));
extComp.waitForFinished(); extComp.waitForFinished();
float score = 0; float score = 0;
auto ok = true;
if (shared->outputType == "stdout") if (shared->outputType == "stdout")
{ {
score = getFloatFromExe(extComp.readAllStandardOutput()); score = getFloatFromExe(extComp.readAllStandardOutput());
} }
else if (shared->outputType == "stderr") else
{ {
score = getFloatFromExe(extComp.readAllStandardError()); score = getFloatFromExe(extComp.readAllStandardError());
} }
else
{
ok = false;
}
if (!ok) QTextStream(stdout) << compArgs.join(" ") << " --result: " << QString::number(score) << Qt::endl;
{
QTextStream(stderr) << "err: img_comp_out: " << shared->outputType << " is not valid. it must be 'stdout' or 'stderr'" << Qt::endl;
}
else
{
QTextStream(stdout) << compArgs.join(" ") << " --result: " << QString::number(score) << Qt::endl;
if (score >= shared->imgThresh) if (eventQue.inQue)
{
if (eventQue.score < score)
{ {
QTextStream(stdout) << "--threshold_breached: " << QString::number(shared->imgThresh) << Qt::endl; eventQue.score = score;
eventQue.imgPath = imgBPath;
evt_t event;
event.timeStamp = curDT;
event.score = score;
event.imgPath = imgBPath;
event.queAge = 0;
mod = true;
shared->recList.append(event);
} }
eventQue.vidList.append(vidAPath);
eventQue.vidList.append(vidBPath);
if (eventQue.queAge >= shared->evMaxSecs)
{
eventQue.inQue = false;
shared->recList.append(eventQue);
eventQue.imgPath.clear();
eventQue.vidList.clear();
eventQue.timeStamp.clear();
eventQue.score = 0;
eventQue.queAge = 0;
}
}
else if (score >= shared->imgThresh)
{
QTextStream(stdout) << "--threshold_breached: " << QString::number(shared->imgThresh) << Qt::endl;
eventQue.score = score;
eventQue.imgPath = imgBPath;
eventQue.inQue = true;
eventQue.queAge = 0;
eventQue.timeStamp = QDateTime::currentDateTime().toString(DATETIME_FMT);
eventQue.vidList.clear();
eventQue.vidList.append(vidAPath);
eventQue.vidList.append(vidBPath);
} }
} }
} }

View File

@ -55,30 +55,13 @@ public:
virtual bool exec(); virtual bool exec();
}; };
class Upkeep : public Loop
{
Q_OBJECT
public:
explicit Upkeep(shared_t *shared, QThread *thr, QObject *parent = nullptr);
bool exec();
};
class EventLoop : public Loop class EventLoop : public Loop
{ {
Q_OBJECT Q_OBJECT
private: private:
QStringList vidList; bool wrOutVod(const evt_t &event);
QString imgPath;
QString name;
float highScore;
uint cycles;
bool wrOutVod();
public: public:
@ -94,9 +77,7 @@ class DetectLoop : public Loop
private: private:
QTimer *pcTimer; QTimer *pcTimer;
uint delayCycles; evt_t eventQue;
bool mod;
quint64 seed;
void resetTimers(); void resetTimers();
float getFloatFromExe(const QByteArray &line); float getFloatFromExe(const QByteArray &line);

View File

@ -129,7 +129,7 @@ void enforceMaxImages(shared_t *share)
{ {
auto names = lsFilesInDir(share->buffPath + "/img", ".bmp"); 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]); QFile::remove(share->buffPath + "/img/" + names[0]);
@ -137,11 +137,11 @@ void enforceMaxImages(shared_t *share)
} }
} }
void enforceMaxVids(shared_t *share) void enforceMaxClips(shared_t *share)
{ {
auto names = lsFilesInDir(share->buffPath + "/live", share->streamExt); auto names = lsFilesInDir(share->buffPath + "/live", share->streamExt);
while (names.size() > MAX_VIDEOS) while (names.size() > (share->liveSecs / 2))
{ {
QFile::remove(share->buffPath + "/live/" + names[0]); QFile::remove(share->buffPath + "/live/" + names[0]);
@ -217,8 +217,7 @@ bool rdConf(const QString &filePath, shared_t *share)
share->retCode = 0; share->retCode = 0;
share->imgThresh = 8000; share->imgThresh = 8000;
share->maxEvents = 100; share->maxEvents = 30;
share->maxLogSize = 100000;
share->skipCmd = false; share->skipCmd = false;
share->postSecs = 60; share->postSecs = 60;
share->evMaxSecs = 30; share->evMaxSecs = 30;
@ -226,12 +225,13 @@ bool rdConf(const QString &filePath, shared_t *share)
share->outputType = "stderr"; share->outputType = "stderr";
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null"; share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
share->streamCodec = "copy"; share->streamCodec = "copy";
share->streamExt = ".ts"; share->streamExt = ".avi";
share->recExt = ".mp4"; share->recExt = ".avi";
share->thumbExt = ".jpg"; share->thumbExt = ".jpg";
share->imgThreads = thrCount; share->imgThreads = thrCount;
share->recThreads = thrCount; share->recThreads = thrCount;
share->recFps = 30; share->recFps = 30;
share->liveSecs = 80;
share->recScale = "1280:720"; share->recScale = "1280:720";
share->imgScale = "320:240"; share->imgScale = "320:240";
share->servUser = APP_BIN; share->servUser = APP_BIN;
@ -253,7 +253,6 @@ bool rdConf(const QString &filePath, shared_t *share)
rdLine("post_cmd = ", line, &share->postCmd); rdLine("post_cmd = ", line, &share->postCmd);
rdLine("img_thresh = ", line, &share->imgThresh); rdLine("img_thresh = ", line, &share->imgThresh);
rdLine("max_events = ", line, &share->maxEvents); rdLine("max_events = ", line, &share->maxEvents);
rdLine("max_log_size = ", line, &share->maxLogSize);
rdLine("img_comp_out = ", line, &share->outputType); rdLine("img_comp_out = ", line, &share->outputType);
rdLine("img_comp_cmd = ", line, &share->compCmd); rdLine("img_comp_cmd = ", line, &share->compCmd);
rdLine("stream_codec = ", line, &share->streamCodec); rdLine("stream_codec = ", line, &share->streamCodec);
@ -266,18 +265,24 @@ bool rdConf(const QString &filePath, shared_t *share)
rdLine("rec_scale = ", line, &share->recScale); rdLine("rec_scale = ", line, &share->recScale);
rdLine("img_scale = ", line, &share->imgScale); rdLine("img_scale = ", line, &share->imgScale);
rdLine("service_user = ", line, &share->servUser); rdLine("service_user = ", line, &share->servUser);
rdLine("live_secs = ", line, &share->liveSecs);
} }
} while(!line.isEmpty()); } while(!line.isEmpty());
if (share->camName.isEmpty()) if (share->camName.isEmpty())
{ {
share->camName = QFileInfo(share->conf).fileName(); share->camName = QFileInfo(share->conf).baseName();
} }
extCorrection(share->streamExt); extCorrection(share->streamExt);
extCorrection(share->recExt); extCorrection(share->recExt);
extCorrection(share->thumbExt); extCorrection(share->thumbExt);
if (share->outputType != "stdout" && share->outputType != "stderr")
{
share->outputType = "stderr";
}
if (share->buffPath.isEmpty()) if (share->buffPath.isEmpty())
{ {
@ -296,6 +301,16 @@ bool rdConf(const QString &filePath, shared_t *share)
{ {
share->recPath = QDir::cleanPath(share->recPath); share->recPath = QDir::cleanPath(share->recPath);
} }
if (share->liveSecs < 10)
{
share->liveSecs = 10;
}
if ((share->liveSecs - 4) < share->evMaxSecs)
{
share->evMaxSecs = share->liveSecs - 4;
}
share->retCode = EACCES; share->retCode = EACCES;

View File

@ -29,42 +29,34 @@
using namespace std; using namespace std;
#define APP_VER "3.3.t3" #define APP_VER "3.3.t4"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define APP_BIN "mow" #define APP_BIN "mow"
#define REC_LOG_NAME "rec.log"
#define DET_LOG_NAME "det.log"
#define UPK_LOG_NAME "upk.log"
#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_VIDEOS 1000
#define PREV_IMG "&prev&" #define PREV_IMG "&prev&"
#define NEXT_IMG "&next&" #define NEXT_IMG "&next&"
enum CmdExeType enum CmdExeType
{ {
MAIN_LOOP, MAIN_LOOP,
VID_LOOP, VID_LOOP
IMG_LOOP
}; };
struct evt_t struct evt_t
{ {
QDateTime timeStamp; QList<QString> vidList;
QString imgPath; QString timeStamp;
float score; QString imgPath;
uint queAge; float score;
bool inQue;
int queAge;
}; };
struct shared_t struct shared_t
{ {
QList<evt_t> recList; QList<evt_t> recList;
QMutex recMutex;
QMutex logMutex;
QString conf; QString conf;
QString recLog;
QString detLog;
QString recordUri; QString recordUri;
QString buffPath; QString buffPath;
QString postCmd; QString postCmd;
@ -80,10 +72,10 @@ struct shared_t
QString imgScale; QString imgScale;
QString servMainLoop; QString servMainLoop;
QString servVidLoop; QString servVidLoop;
QString servImgLoop;
QString servUser; QString servUser;
bool singleTenant; bool singleTenant;
bool skipCmd; bool skipCmd;
int liveSecs;
int recFps; int recFps;
int imgThreads; int imgThreads;
int recThreads; int recThreads;
@ -91,7 +83,6 @@ struct shared_t
int postSecs; int postSecs;
int imgThresh; int imgThresh;
int maxEvents; int maxEvents;
int maxLogSize;
int retCode; int retCode;
}; };
@ -110,7 +101,7 @@ void rdLine(const QString &param, const QString &line, int *value);
void rdLine(const QString &param, const QString &line, bool *value); void rdLine(const QString &param, const QString &line, bool *value);
void enforceMaxEvents(shared_t *share); void enforceMaxEvents(shared_t *share);
void enforceMaxImages(shared_t *share); void enforceMaxImages(shared_t *share);
void enforceMaxVids(shared_t *share); void enforceMaxClips(shared_t *share);
void extCorrection(QString &ext); void extCorrection(QString &ext);
#endif // COMMON_H #endif // COMMON_H

View File

@ -23,6 +23,7 @@ void showHelp()
QTextStream(stdout) << "-i : all valid config files found in /etc/mow will be used to create" << Qt::endl; QTextStream(stdout) << "-i : all valid config files found in /etc/mow will be used to create" << Qt::endl;
QTextStream(stdout) << " a full instance; full meaning main and vid loop systemd services" << Qt::endl; QTextStream(stdout) << " a full instance; full meaning main and vid loop systemd services" << Qt::endl;
QTextStream(stdout) << " will be created for each config file." << Qt::endl; QTextStream(stdout) << " will be created for each config file." << Qt::endl;
QTextStream(stdout) << "-d : this is the same as -i except it will not auto start the services." << Qt::endl;
QTextStream(stdout) << "-v : display the current version." << Qt::endl; QTextStream(stdout) << "-v : display the current version." << Qt::endl;
QTextStream(stdout) << "-u : uninstall the entire app from your system, including all" << Qt::endl; QTextStream(stdout) << "-u : uninstall the entire app from your system, including all" << Qt::endl;
QTextStream(stdout) << " systemd services related to it." << Qt::endl; QTextStream(stdout) << " systemd services related to it." << Qt::endl;

View File

@ -103,17 +103,21 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
} }
else else
{ {
ret += "taskset -c " + buildThreadCount(conf->recThreads) + " "; ret += "ffmpeg -hide_banner -y -i '" + conf->recordUri + "' -strftime 1 -strftime_mkdir 1 ";
ret += "ffmpeg -hide_banner -i '" + conf->recordUri + "' -strftime 1 -strftime_mkdir 1 ";
if (conf->recordUri.contains("rtsp")) if (conf->recordUri.contains("rtsp"))
{ {
ret += "-rtsp_transport tcp "; ret += "-rtsp_transport tcp ";
} }
ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " "; if (conf->streamCodec != "copy")
ret += "-hls_segment_filename live/" + QString(STRFTIME_FMT) + conf->streamExt + " "; {
ret += "-y -vcodec " + conf->streamCodec + " -f hls -hls_time 2 -hls_list_size 1000 -hls_flags append_list+omit_endlist -t 60 stream.m3u8"; ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " ";
}
ret += "-vcodec " + conf->streamCodec + " ";
ret += "-reset_timestamps 1 -sc_threshold 0 -g 2 -force_key_frames \"expr:gte(t, n_forced * 2)\" -t 60 -segment_time 2 -f segment ";
ret += conf->buffPath + "/live/" + QString(STRFTIME_FMT) + conf->streamExt;
} }
return ret; return ret;
@ -150,8 +154,8 @@ int rmService(const QString &servName)
if (QFile::exists(path)) if (QFile::exists(path))
{ {
ret = QProcess::execute("systemctl", {"stop", servName}); if (ret == 0) ret = QProcess::execute("systemctl", {"stop", servName});
ret = QProcess::execute("systemctl", {"disable", servName}); if (ret == 0) ret = QProcess::execute("systemctl", {"disable", servName});
QFile::remove(path); QFile::remove(path);
QFile::remove("/usr/bin/" + servName); QFile::remove("/usr/bin/" + servName);
@ -168,6 +172,12 @@ int rmServiceByConf(const QString &confFile)
{ {
conf.retCode = rmService(conf.servMainLoop); conf.retCode = rmService(conf.servMainLoop);
conf.retCode = rmService(conf.servVidLoop); conf.retCode = rmService(conf.servVidLoop);
QDir live(conf.buffPath + "/live");
QDir img(conf.buffPath + "/img");
live.removeRecursively();
img.removeRecursively();
} }
return conf.retCode; return conf.retCode;