v3.3.t2
-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:
parent
41ccf1d1e7
commit
83080cfe41
|
@ -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
|
||||||
|
|
17
install.sh
17
install.sh
|
@ -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
|
||||||
|
|
24
setup.sh
24
setup.sh
|
@ -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
|
|
||||||
sh imgmagick_build.sh
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -182,8 +171,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');
|
||||||
|
|
||||||
|
@ -194,7 +181,6 @@ void RecLoop::procError(const QString &desc, QProcess *proc)
|
||||||
recLog(desc + "_cmd_stderr: " + line, shared);
|
recLog(desc + "_cmd_stderr: " + line, shared);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
137
src/common.cpp
137
src/common.cpp
|
@ -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 ¶m, 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;
|
||||||
|
|
14
src/common.h
14
src/common.h
|
@ -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 ¶m, const QString &line, QString *value);
|
void rdLine(const QString ¶m, const QString &line, QString *value);
|
||||||
void rdLine(const QString ¶m, const QString &line, int *value);
|
void rdLine(const QString ¶m, const QString &line, int *value);
|
||||||
void rdLine(const QString ¶m, const QString &line, bool *value);
|
void rdLine(const QString ¶m, 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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user