-pixel sizes for video and thumbnails are now configurable
-the amount of recording and snapshot threads are now
 configurable
-changed the default buffer and recording directories to
 drop the application name and also drop a level
-service files are now stored in a dedicated dir
-fixed logging so it will capture errors better
-moved folder structure building to rdConfig() and added more
 error capturing
-recording fps is now configurable
This commit is contained in:
Zii 2023-10-19 15:04:39 -04:00
parent 41ccf1d1e7
commit 83080cfe41
7 changed files with 200 additions and 122 deletions

View File

@ -1,7 +1,9 @@
#!/bin/sh #!/bin/sh
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
if ! command -v magick &> /dev/null magick -version &> /dev/null
if [ ! $? -eq 0 ]
then then
apt install -y git apt install -y git
git clone https://github.com/ImageMagick/ImageMagick.git .build-imagemagick git clone https://github.com/ImageMagick/ImageMagick.git .build-imagemagick

View File

@ -7,20 +7,20 @@ if [ ! -d "/opt/mow" ]; then
mkdir /opt/mow mkdir /opt/mow
fi fi
if [ ! -d "/var/opt/mow" ]; then if [ ! -d "/var/footage" ]; then
mkdir /var/opt/mow mkdir /var/footage
fi fi
if [ ! -d "/etc/mow" ]; then if [ ! -d "/etc/mow" ]; then
mkdir /etc/mow mkdir /etc/mow
fi fi
if [ ! -d "/var/opt/mow/buf" ]; then if [ ! -d "/var/buffer" ]; then
mkdir /var/opt/mow/buf mkdir /var/buffer
fi fi
if [ ! -d "/var/opt/mow/rec" ]; then if [ ! -d "/var/mow_serv" ]; then
mkdir /var/opt/mow/rec mkdir /var/mow_serv
fi fi
cp -v ./.build-mow/mow /opt/mow/bin cp -v ./.build-mow/mow /opt/mow/bin
@ -36,12 +36,13 @@ printf "rm -v /opt/mow/run\n" >> /opt/mow/uninst
printf "rm -v /opt/mow/uninst\n" >> /opt/mow/uninst printf "rm -v /opt/mow/uninst\n" >> /opt/mow/uninst
printf "rm -v /usr/bin/mow\n" >> /opt/mow/uninst printf "rm -v /usr/bin/mow\n" >> /opt/mow/uninst
printf "rm -rv /opt/mow\n" >> /opt/mow/uninst printf "rm -rv /opt/mow\n" >> /opt/mow/uninst
printf "rm -r /var/opt/mow/buf\n" >> /opt/mow/uninst
printf "deluser mow\n" >> /opt/mow/uninst printf "deluser mow\n" >> /opt/mow/uninst
useradd -r mow useradd -r mow
chown -R mow:mow /var/opt/mow chown -R -v mow:mow /var/footage
chown -R -v mow:mow /var/buffer
chown -R -v mow:mow /var/mow_serv
chmod -v +x /opt/mow/run chmod -v +x /opt/mow/run
chmod -v +x /opt/mow/bin chmod -v +x /opt/mow/bin

View File

@ -2,5 +2,25 @@
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt update -y apt update -y
apt install -y pkg-config cmake make g++ apt install -y pkg-config cmake make g++
apt install -y ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev x264 libx264-dev libilmbase-dev qt6-base-dev qtchooser qmake6 qt6-base-dev-tools libxkbcommon-dev libfuse-dev fuse3
if [ $? -eq 0 ]
then
apt install -y ffmpeg
apt install -y libavcodec-dev
apt install -y libavformat-dev
apt install -y libavutil-dev
apt install -y libswscale-dev
apt install -y x264
apt install -y libx264-dev
apt install -y libilmbase-dev
apt install -y qt6-base-dev
apt install -y qtchooser
apt install -y qmake6
apt install -y qt6-base-dev-tools
apt install -y libxkbcommon-dev
apt install -y libfuse-dev
apt install -y fuse3
sh imgmagick_build.sh sh imgmagick_build.sh
fi

View File

@ -14,38 +14,9 @@
Camera::Camera(QObject *parent) : QObject(parent) {} Camera::Camera(QObject *parent) : QObject(parent) {}
void Camera::cleanup()
{
if (QFileInfo::exists(shared.tmpDir + "/rec"))
{
QProcess::execute("rm", {shared.tmpDir + "/rec"});
}
}
int Camera::start(const QStringList &args) int Camera::start(const QStringList &args)
{ {
if (rdConf(getParam("-c", args), &shared)) if (rdConf(getParam("-c", args), &shared))
{
QProcess::execute("mkdir", {"-p", shared.outDir});
QProcess::execute("mkdir", {"-p", shared.tmpDir});
QProcess::execute("mkdir", {"-p", shared.tmpDir + "/live"});
QProcess::execute("mkdir", {"-p", shared.tmpDir + "/logs"});
QProcess::execute("mkdir", {"-p", shared.tmpDir + "/img"});
cleanup();
touch(shared.tmpDir + "/stream.m3u8");
QProcess::execute("ln", {"-s", shared.outDir, shared.tmpDir + "/rec"});
if (!QDir::setCurrent(shared.tmpDir))
{
QTextStream(stderr) << "err: failed to change/create the current working directory to camera folder: '" << shared.outDir << "' does it exists?" << Qt::endl;
shared.retCode = ENOENT;
}
else
{ {
auto thr1 = new QThread(nullptr); auto thr1 = new QThread(nullptr);
auto thr2 = new QThread(nullptr); auto thr2 = new QThread(nullptr);
@ -62,7 +33,6 @@ int Camera::start(const QStringList &args)
thr3->start(); thr3->start();
thr4->start(); thr4->start();
} }
}
return shared.retCode; return shared.retCode;
} }
@ -131,10 +101,14 @@ void RecLoop::updateCmd()
QStringList recArgs; QStringList recArgs;
QStringList imgArgs; QStringList imgArgs;
recArgs << "-c";
recArgs << buildThreadCount(shared->recThreads);
recArgs << "ffmpeg";
recArgs << "-hide_banner"; recArgs << "-hide_banner";
recArgs << "-i" << shared->recordUri; recArgs << "-i" << shared->recordUri;
recArgs << "-strftime" << "1"; recArgs << "-strftime" << "1";
recArgs << "-strftime_mkdir" << "1"; recArgs << "-strftime_mkdir" << "1";
recArgs << "-vf" << "fps=" + QString::number(shared->recFps) + ",scale=" + shared->recScale;
recArgs << "-hls_segment_filename" << "live/" + QString(STRFTIME_FMT) + shared->streamExt; recArgs << "-hls_segment_filename" << "live/" + QString(STRFTIME_FMT) + shared->streamExt;
recArgs << "-y"; recArgs << "-y";
recArgs << "-vcodec" << shared->streamCodec; recArgs << "-vcodec" << shared->streamCodec;
@ -142,23 +116,38 @@ void RecLoop::updateCmd()
recArgs << "-hls_time" << "2"; recArgs << "-hls_time" << "2";
recArgs << "-hls_list_size" << "1000"; recArgs << "-hls_list_size" << "1000";
recArgs << "-hls_flags" << "append_list+omit_endlist"; recArgs << "-hls_flags" << "append_list+omit_endlist";
if (shared->recordUri.contains("rtsp"))
{
recArgs << "-rtsp_transport" << "tcp"; recArgs << "-rtsp_transport" << "tcp";
}
recArgs << "-t" << QString::number(heartBeat); recArgs << "-t" << QString::number(heartBeat);
recArgs << "stream.m3u8"; recArgs << "stream.m3u8";
imgArgs << "-c";
imgArgs << buildThreadCount(shared->imgThreads);
imgArgs << "ffmpeg";
imgArgs << "-hide_banner"; imgArgs << "-hide_banner";
imgArgs << "-i" << shared->recordUri; imgArgs << "-i" << shared->recordUri;
imgArgs << "-strftime" << "1"; imgArgs << "-strftime" << "1";
imgArgs << "-strftime_mkdir" << "1"; imgArgs << "-strftime_mkdir" << "1";
imgArgs << "-vf" << "fps=1,scale=320:240"; imgArgs << "-vf" << "fps=1,scale=" + shared->imgScale;
if (shared->recordUri.contains("rtsp"))
{
imgArgs << "-rtsp_transport" << "tcp"; imgArgs << "-rtsp_transport" << "tcp";
}
imgArgs << "-t" << QString::number(heartBeat); imgArgs << "-t" << QString::number(heartBeat);
imgArgs << "img/" + QString(STRFTIME_FMT) + ".bmp"; imgArgs << "img/" + QString(STRFTIME_FMT) + ".bmp";
recProc->setProgram("ffmpeg"); recProc->setWorkingDirectory(shared->tmpDir);
recProc->setProgram("taskset");
recProc->setArguments(recArgs); recProc->setArguments(recArgs);
imgProc->setProgram("ffmpeg"); imgProc->setWorkingDirectory(shared->tmpDir);
imgProc->setProgram("taskset");
imgProc->setArguments(imgArgs); imgProc->setArguments(imgArgs);
recLog("rec_args: " + recArgs.join(" "), shared); recLog("rec_args: " + recArgs.join(" "), shared);
@ -181,8 +170,6 @@ void RecLoop::term()
} }
void RecLoop::procError(const QString &desc, QProcess *proc) void RecLoop::procError(const QString &desc, QProcess *proc)
{
if (proc->isOpen() && (proc->state() != QProcess::Running))
{ {
auto errBlob = QString(proc->readAllStandardError()); auto errBlob = QString(proc->readAllStandardError());
auto errLines = errBlob.split('\n'); auto errLines = errBlob.split('\n');
@ -195,7 +182,6 @@ void RecLoop::procError(const QString &desc, QProcess *proc)
} }
} }
} }
}
bool RecLoop::exec() bool RecLoop::exec()
{ {
@ -216,11 +202,11 @@ Upkeep::Upkeep(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(shared
bool Upkeep::exec() bool Upkeep::exec()
{ {
enforceMaxLogSize(QString("logs/") + REC_LOG_NAME, shared); enforceMaxLogSize(shared->tmpDir + "/logs/" + REC_LOG_NAME, shared);
enforceMaxLogSize(QString("logs/") + DET_LOG_NAME, shared); enforceMaxLogSize(shared->tmpDir + "/logs/" + DET_LOG_NAME, shared);
dumpLogs(QString("logs/") + REC_LOG_NAME, shared->recLog); dumpLogs(shared->tmpDir + "/logs/" + REC_LOG_NAME, shared->recLog);
dumpLogs(QString("logs/") + DET_LOG_NAME, shared->detLog); dumpLogs(shared->tmpDir + "/logs/" + DET_LOG_NAME, shared->detLog);
shared->logMutex.lock(); shared->logMutex.lock();
shared->recLog.clear(); shared->recLog.clear();
@ -228,7 +214,7 @@ bool Upkeep::exec()
shared->logMutex.unlock(); shared->logMutex.unlock();
enforceMaxEvents(shared); enforceMaxEvents(shared);
enforceMaxImages(); enforceMaxImages(shared);
enforceMaxVids(shared); enforceMaxVids(shared);
return Loop::exec(); return Loop::exec();
@ -258,7 +244,7 @@ bool EventLoop::exec()
args << "convert"; args << "convert";
args << imgPath; args << imgPath;
args << "rec/" + name + shared->thumbExt; args << shared->outDir + "/" + name + shared->thumbExt;
proc.start("magick", args); proc.start("magick", args);
proc.waitForFinished(); proc.waitForFinished();
@ -291,8 +277,8 @@ bool EventLoop::exec()
auto maxSecs = shared->evMaxSecs / 2; auto maxSecs = shared->evMaxSecs / 2;
// half the maxsecs value to get front-back half secs // half the maxsecs value to get front-back half secs
auto backFiles = backwardFacingFiles("live", shared->streamExt, event->timeStamp, maxSecs); auto backFiles = backwardFacingFiles(shared->tmpDir + "/live", shared->streamExt, event->timeStamp, maxSecs);
auto frontFiles = forwardFacingFiles("live", shared->streamExt, event->timeStamp, maxSecs); auto frontFiles = forwardFacingFiles(shared->tmpDir + "/live", shared->streamExt, event->timeStamp, maxSecs);
vidList.append(backFiles + frontFiles); vidList.append(backFiles + frontFiles);
rmIndx.append(i); rmIndx.append(i);
@ -351,7 +337,7 @@ bool EventLoop::wrOutVod()
args << "-safe" << "0"; args << "-safe" << "0";
args << "-i" << concat; args << "-i" << concat;
args << "-c" << "copy"; args << "-c" << "copy";
args << "rec/" + name + shared->recExt; args << shared->outDir + "/" + name + shared->recExt;
proc.setProgram("ffmpeg"); proc.setProgram("ffmpeg");
proc.setArguments(args); proc.setArguments(args);

View File

@ -24,8 +24,6 @@ private:
shared_t shared; shared_t shared;
void cleanup();
public: public:
explicit Camera(QObject *parent = nullptr); explicit Camera(QObject *parent = nullptr);

View File

@ -107,31 +107,31 @@ QStringList forwardFacingFiles(const QString &path, const QString &ext, const QD
void enforceMaxEvents(shared_t *share) void enforceMaxEvents(shared_t *share)
{ {
auto names = lsFilesInDir("events", ".mp4"); auto names = lsFilesInDir(share->outDir, share->recExt);
while (names.size() > share->maxEvents) while (names.size() > share->maxEvents)
{ {
auto nameOnly = "events/" + names[0]; auto nameOnly = share->outDir + "/" + names[0];
nameOnly.remove(".mp4"); nameOnly.chop(share->recExt.size());
auto mp4File = nameOnly + ".mp4"; auto vidFile = nameOnly + share->recExt;
auto imgFile = nameOnly + ".jpg"; auto imgFile = nameOnly + share->thumbExt;
QFile::remove(mp4File); QFile::remove(vidFile);
QFile::remove(imgFile); QFile::remove(imgFile);
names.removeFirst(); names.removeFirst();
} }
} }
void enforceMaxImages() void enforceMaxImages(shared_t *share)
{ {
auto names = lsFilesInDir("img", ".bmp"); auto names = lsFilesInDir(share->tmpDir + "/img", ".bmp");
while (names.size() > MAX_IMAGES) while (names.size() > MAX_IMAGES)
{ {
QFile::remove("img/" + names[0]); QFile::remove(share->tmpDir + "/img/" + names[0]);
names.removeFirst(); names.removeFirst();
} }
@ -139,11 +139,11 @@ void enforceMaxImages()
void enforceMaxVids(shared_t *share) void enforceMaxVids(shared_t *share)
{ {
auto names = lsFilesInDir("live", share->streamExt); auto names = lsFilesInDir(share->tmpDir + "/live", share->streamExt);
while (names.size() > MAX_VIDEOS) while (names.size() > MAX_VIDEOS)
{ {
QFile::remove("live/" + names[0]); QFile::remove(share->tmpDir + "/live/" + names[0]);
names.removeFirst(); names.removeFirst();
} }
@ -169,22 +169,9 @@ void rdLine(const QString &param, const QString &line, bool *value)
{ {
if (line.startsWith(param)) if (line.startsWith(param))
{ {
*value = (line == "y" || line == "Y"); auto val = line.mid(param.size()).trimmed();
}
}
void touch(const QString &path) *value = (val == "y" || val == "Y");
{
if (!QFile::exists(path))
{
QFile file(path);
if (file.open(QFile::WriteOnly))
{
file.write("");
}
file.close();
} }
} }
@ -196,6 +183,18 @@ void extCorrection(QString &ext)
} }
} }
bool mkPath(const QString &path)
{
auto ret = true;
if (!QDir().exists(path))
{
ret = QDir().mkpath(path);
}
return ret;
}
bool rdConf(const QString &filePath, shared_t *share) bool rdConf(const QString &filePath, shared_t *share)
{ {
QFile varFile(filePath); QFile varFile(filePath);
@ -204,7 +203,7 @@ bool rdConf(const QString &filePath, shared_t *share)
{ {
share->retCode = ENOENT; share->retCode = ENOENT;
QTextStream(stderr) << "err: config file: " << filePath << " does not exists or lack read permissions." << Qt::endl; QTextStream(stderr) << "err: config file - " << filePath << " does not exists or lack read permissions." << Qt::endl;
} }
else else
{ {
@ -212,6 +211,8 @@ bool rdConf(const QString &filePath, shared_t *share)
share->postCmd.clear(); share->postCmd.clear();
share->camName.clear(); share->camName.clear();
auto thrCount = QThread::idealThreadCount() / 2;
share->retCode = 0; share->retCode = 0;
share->imgThresh = 8000; share->imgThresh = 8000;
share->maxEvents = 100; share->maxEvents = 100;
@ -220,8 +221,8 @@ bool rdConf(const QString &filePath, shared_t *share)
share->postSecs = 60; share->postSecs = 60;
share->evMaxSecs = 30; share->evMaxSecs = 30;
share->conf = filePath; share->conf = filePath;
share->buffPath = "/var/opt/" + QString(APP_BIN) + "/buf"; share->buffPath = "/var/buffer";
share->recRoot = "/var/opt/" + QString(APP_BIN) + "/rec"; share->recPath = "/var/footage";
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";
@ -229,6 +230,11 @@ bool rdConf(const QString &filePath, shared_t *share)
share->recExt = ".mp4"; share->recExt = ".mp4";
share->thumbExt = ".jpg"; share->thumbExt = ".jpg";
share->singleTenant = false; share->singleTenant = false;
share->imgThreads = thrCount;
share->recThreads = thrCount;
share->recFps = 30;
share->recScale = "1280:720";
share->imgScale = "320:240";
QString line; QString line;
@ -241,7 +247,7 @@ bool rdConf(const QString &filePath, shared_t *share)
rdLine("cam_name = ", line, &share->camName); rdLine("cam_name = ", line, &share->camName);
rdLine("recording_uri = ", line, &share->recordUri); rdLine("recording_uri = ", line, &share->recordUri);
rdLine("buffer_path = ", line, &share->buffPath); rdLine("buffer_path = ", line, &share->buffPath);
rdLine("rec_root = ", line, &share->recRoot); rdLine("rec_path = ", line, &share->recPath);
rdLine("max_event_secs = ", line, &share->evMaxSecs); rdLine("max_event_secs = ", line, &share->evMaxSecs);
rdLine("post_secs = ", line, &share->postSecs); rdLine("post_secs = ", line, &share->postSecs);
rdLine("post_cmd = ", line, &share->postCmd); rdLine("post_cmd = ", line, &share->postCmd);
@ -255,6 +261,11 @@ bool rdConf(const QString &filePath, shared_t *share)
rdLine("rec_ext = ", line, &share->recExt); rdLine("rec_ext = ", line, &share->recExt);
rdLine("thumbnail_ext = ", line, &share->thumbExt); rdLine("thumbnail_ext = ", line, &share->thumbExt);
rdLine("single_tenant = ", line, &share->singleTenant); rdLine("single_tenant = ", line, &share->singleTenant);
rdLine("img_threads = ", line, &share->imgThreads);
rdLine("rec_threads = ", line, &share->recThreads);
rdLine("rec_fps = ", line, &share->recFps);
rdLine("rec_scale = ", line, &share->recScale);
rdLine("img_scale = ", line, &share->imgScale);
} }
} while(!line.isEmpty()); } while(!line.isEmpty());
@ -270,16 +281,56 @@ bool rdConf(const QString &filePath, shared_t *share)
if (share->singleTenant) if (share->singleTenant)
{ {
share->outDir = QDir().cleanPath(share->recRoot); share->outDir = QDir().cleanPath(share->recPath);
share->tmpDir = QDir().cleanPath(share->buffPath); share->tmpDir = QDir().cleanPath(share->buffPath);
} }
else else
{ {
share->outDir = QDir().cleanPath(share->recRoot) + "/" + share->camName; share->outDir = QDir().cleanPath(share->recPath) + "/" + share->camName;
share->tmpDir = QDir().cleanPath(share->buffPath) + "/" + share->camName; share->tmpDir = QDir().cleanPath(share->buffPath) + "/" + share->camName;
} }
share->servPath = QString("/var/opt/") + APP_BIN + "/" + APP_BIN + "." + share->camName + ".service"; auto servDir = QString("/var/") + APP_BIN + QString("_serv");
share->retCode = EACCES;
if (!mkPath(servDir))
{
QTextStream(stderr) << "err: failed to create service directory - " << servDir << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->recPath))
{
QTextStream(stderr) << "err: failed to create root recording directory - " << share->recPath << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->buffPath))
{
QTextStream(stderr) << "err: failed to create root buffer directory - " << share->buffPath << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->outDir))
{
QTextStream(stderr) << "err: failed to create recording directory - " << share->outDir << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->tmpDir))
{
QTextStream(stderr) << "err: failed to create buffer directory - " << share->tmpDir << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->tmpDir + "/live"))
{
QTextStream(stderr) << "err: failed to create 'live' in the buffer directory - " << share->tmpDir << "/live" << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->tmpDir + "/logs"))
{
QTextStream(stderr) << "err: failed to create 'logs' in the buffer directory - " << share->tmpDir << "/logs" << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->tmpDir + "/img"))
{
QTextStream(stderr) << "err: failed to create 'img' in the buffer directory - " << share->tmpDir << "/img" << " check for write permissions." << Qt::endl;
}
else
{
share->retCode = 0;
share->servPath = QString("/var/") + APP_BIN + QString("_serv/") + APP_BIN + "." + share->camName + ".service";
}
} }
return share->retCode == 0; return share->retCode == 0;
@ -287,7 +338,8 @@ bool rdConf(const QString &filePath, shared_t *share)
void rmServices() void rmServices()
{ {
auto files = lsFilesInDir(QString("/var/opt/") + APP_BIN, ".service"); auto path = QString("/var/") + APP_BIN + QString("_serv");
auto files = lsFilesInDir(path, ".service");
for (auto &&serv : files) for (auto &&serv : files)
{ {
@ -295,7 +347,7 @@ void rmServices()
QProcess::execute("systemctl", {"disable", serv}); QProcess::execute("systemctl", {"disable", serv});
QFile::remove(QString("/lib/systemd/system/") + serv); QFile::remove(QString("/lib/systemd/system/") + serv);
QFile::remove(QString("/var/opt/") + APP_BIN + "/" + serv); QFile::remove(path + "/" + serv);
} }
QProcess::execute("systemctl", {"daemon-reload"}); QProcess::execute("systemctl", {"daemon-reload"});
@ -303,7 +355,8 @@ void rmServices()
void listServices() void listServices()
{ {
auto files = lsFilesInDir(QString("/var/opt/") + APP_BIN, ".service"); auto path = QString("/var/") + APP_BIN + QString("_serv");
auto files = lsFilesInDir(path, ".service");
for (auto &&serv : files) for (auto &&serv : files)
{ {
@ -398,6 +451,18 @@ int loadServices(const QStringList &args)
return ret; return ret;
} }
QString buildThreadCount(int count)
{
QString ret = "0";
for (auto i = 1; i < count; ++i)
{
ret.append(","); ret.append(QString::number(i));
}
return ret;
}
QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos) QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos)
{ {
QStringList ret; QStringList ret;

View File

@ -29,7 +29,7 @@
using namespace std; using namespace std;
#define APP_VER "3.3.t1" #define APP_VER "3.3.t2"
#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 REC_LOG_NAME "rec.log"
@ -64,7 +64,7 @@ struct shared_t
QString buffPath; QString buffPath;
QString postCmd; QString postCmd;
QString camName; QString camName;
QString recRoot; QString recPath;
QString servPath; QString servPath;
QString outputType; QString outputType;
QString compCmd; QString compCmd;
@ -72,8 +72,13 @@ struct shared_t
QString streamExt; QString streamExt;
QString recExt; QString recExt;
QString thumbExt; QString thumbExt;
QString recScale;
QString imgScale;
bool singleTenant; bool singleTenant;
bool skipCmd; bool skipCmd;
int recFps;
int imgThreads;
int recThreads;
int evMaxSecs; int evMaxSecs;
int postSecs; int postSecs;
int imgThresh; int imgThresh;
@ -83,6 +88,7 @@ struct shared_t
}; };
QString getParam(const QString &key, const QStringList &args); QString getParam(const QString &key, const QStringList &args);
QString buildThreadCount(int count);
QStringList lsFilesInDir(const QString &path, const QString &ext = QString()); QStringList lsFilesInDir(const QString &path, const QString &ext = QString());
QStringList lsDirsInDir(const QString &path); QStringList lsDirsInDir(const QString &path);
QStringList listFacingFiles(const QString &path, const QString &ext, const QDateTime &stamp, int secs, char dir); QStringList listFacingFiles(const QString &path, const QString &ext, const QDateTime &stamp, int secs, char dir);
@ -90,15 +96,15 @@ QStringList backwardFacingFiles(const QString &path, const QString &ext, const Q
QStringList forwardFacingFiles(const QString &path, const QString &ext, const QDateTime &stamp, int secs); QStringList forwardFacingFiles(const QString &path, const QString &ext, const QDateTime &stamp, int secs);
QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos = nullptr); QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos = nullptr);
bool rdConf(const QString &filePath, shared_t *share); bool rdConf(const QString &filePath, shared_t *share);
bool mkPath(const QString &path);
int loadServices(const QStringList &args); int loadServices(const QStringList &args);
void listServices(); void listServices();
void rmServices(); void rmServices();
void touch(const QString &path);
void rdLine(const QString &param, const QString &line, QString *value); 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 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(); void enforceMaxImages(shared_t *share);
void enforceMaxVids(shared_t *share); void enforceMaxVids(shared_t *share);
void extCorrection(QString &ext); void extCorrection(QString &ext);