-moved buff_path setup code away from rdconf() to loadSh(). this
 insures root will never take ownership of the camera buffer.
-added service group option.
-updated the documentation in preperation of release.
This commit is contained in:
zii 2023-12-23 17:38:11 -05:00
parent bb8a1fad45
commit 7ca4fc3dbe
6 changed files with 91 additions and 71 deletions

View File

@ -30,30 +30,34 @@ parameters supported and descriptions of each.
# also note to avoid using empty lines. if you're going to need an empty
# line, start it with a '#'
#
recording_stream = rtsp://1.2.3.4:554/h264
# this is the url to the main stream of the IP camera that will be used
# to record footage.
recording_uri = rtsp://1.2.3.4:554/h264
# this is the uri to the main stream of the IP camera that will be used
# to record footage. it can be a url to an rtsp stream or a direct device
# path such as /dev/video0.
#
web_root = /var/opt/mow/web
# this is the output directory that will be used to store recorded footage
# from the cameras as well as the web interface for the application.
# warning: this will overwrite any existing index.html files so be sure
# to choose a directory that doesn't have an existing website.
#
buffer_path = /var/opt/mow/buf
buffer_path = /var/buffer
# this is the work directory the app will use to store live footage and
# image frames. it's recommended to use a ram disk for this since there
# will be large amounts of io occuring here. 1g of space per camera is
# will be large amounts of io occuring here. 1GB of space per camera is
# a good rule of thumb.
#
rec_path = /var/footage
# this is video output directory that will be used to store any footage
# that contain any motion events.
#
live_secs = 30
# this is the maximum amount of seconds worth of live footage to keep in
# buffer_path before deleting the oldest 2 seconds worth of footage.
# note: each video clip in buffer_path should be 2 seconds long.
#
cam_name = cam-1
# this is the optional camera name parameter to identify the camera. this
# name will also be used to as the base directory in web_root. if not
# defined, the name of the config file will be used.
# name will also be used to as the base directory in buffer_path or
# rec_path. if not defined, the name of the config file will be used.
#
max_event_secs = 30
# this is the maximum amount of secs of video footage that can be
# recorded in a motion event.
# this is the maximum amount of secs of video footage that can be recorded
# in a motion event.
#
img_comp_cmd = magick compare -metric FUZZ &prev& &next& /dev/null
# this is the command line template this application will use when calling
@ -74,14 +78,35 @@ img_comp_out = stderr
# use to output the comparison score. this can only be stderr or stdout,
# any other stream name is considered invalid.
#
stream_codec = copy
# this is the encoding codec to use when recording footage from the camera.
# the list of supported codecs entirely depend on the hosts' ffmpeg
# installation. run 'ffmpeg -codecs' to determine this list. if not
# defined, 'copy' will be used as in it will just copy the codec format
# from camera itself.
#
stream_ext = .avi
# this is the file extension that will be used to when recording footage
# from the camera in buffer_path. ffmpeg will also use this to determine
# what format container to use for the video clips.
#
thumbnail_ext = .jpg
# this the image format that will be used when creating the thumbnails
# for the videos clips recorded to rec_path.
#
rec_ext = .avi
# this the the file extension this will be used when storaging motion
# footage to rec_path. ffmpeg will also use this to determine what format
# container to use for the video clips.
#
img_thresh = 8000
# this parameter defines the score threshold from img_comp_cmd that will
# be considered motion. any motion events will queue up max_event_secs
# worth of hls clips to be written out to web_root.
# worth of video clips to be written out to rec_path.
#
max_events = 100
# this indicates the maximum amount of motion event video clips to keep
# before deleting the oldest clip.
# in rec_path before deleting the oldest clip.
#
post_secs = 60
# this is the amount of seconds to wait before running the command
@ -93,23 +118,31 @@ post_cmd = move_the_ptz_camera.py
# is to move a ptz camera to the next position of it's patrol pattern.
# note: the call to this command will be delayed if motion was detected.
#
max_log_size = 50000
# this is the maximum byte size of all log files that can be stored in
# web_root. whenever this limit is met, the log file will be deleted and
# then eventually recreated blank.
rec_fps = 30
# this sets the recording frames per second for the footage recorded
# from the camera. this has no affect of using 'copy' as the
# stream_codec.
#
web_text = #dee5ee
# this can be used to customize the color of the text in the web
# interface. it can be defined as any color understood by html5 standard.
rec_scale = 1280:720
# this sets the pixel scale of the recorded footage from the camera. it
# uses width, height numeric strings seperated by a colon, eg W:H. this
# has no affect of using 'copy' as the stream_codec.
#
web_bg = #485564
# this can be used to customize the background color of the web
# interface. just like web_text, it also follows the html5 standard.
img_scale = 320:240
# this sets the pixel size of the thumbnails for recorded stored in
# rec_path. it uses width, height numeric strings seperated by a colon,
# eg W:H.
#
web_font = courier
# this will customize the text font family to use in the web interface.
# it is recommended to use mono-spaced font because this is also used to
# display logs and logs are best displayed in mono-spaced font.
service_user = mow
# this sets the service local user of the application dictating the
# amount privilege it will have on the host. if not defined, the
# unprivileged user 'mow' will be used. which ever user is defined here
# just make sure it has read/write access to buffer_path and rec_path.
#
service_group = mow
# this sets the service local group of the application that can further
# refine host privileges. if not defined, the name stored in
# service_user will be used.
```
### Setup/Build/Install ###

View File

@ -3,6 +3,10 @@ if [ -f "/opt/mow/uninst" ]; then
mow -u -f
fi
if [ ! -d "/opt" ]; then
mkdir /opt
fi
if [ ! -d "/opt/mow" ]; then
mkdir /opt/mow
fi

View File

@ -212,6 +212,7 @@ bool rdConf(const QString &filePath, shared_t *share)
share->camName.clear();
share->buffPath.clear();
share->recPath.clear();
share->servGroup.clear();
auto thrCount = QThread::idealThreadCount() / 2;
@ -228,8 +229,6 @@ bool rdConf(const QString &filePath, shared_t *share)
share->streamExt = ".avi";
share->recExt = ".avi";
share->thumbExt = ".jpg";
share->imgThreads = thrCount;
share->recThreads = thrCount;
share->recFps = 30;
share->liveSecs = 80;
share->recScale = "1280:720";
@ -259,12 +258,11 @@ bool rdConf(const QString &filePath, shared_t *share)
rdLine("stream_ext = ", line, &share->streamExt);
rdLine("rec_ext = ", line, &share->recExt);
rdLine("thumbnail_ext = ", line, &share->thumbExt);
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);
rdLine("service_user = ", line, &share->servUser);
rdLine("service_group = ", line, &share->servGroup);
rdLine("live_secs = ", line, &share->liveSecs);
}
@ -312,30 +310,13 @@ bool rdConf(const QString &filePath, shared_t *share)
share->evMaxSecs = share->liveSecs - 4;
}
share->retCode = EACCES;
if (share->servGroup.isEmpty())
{
share->servGroup = share->servUser;
}
if (!mkPath(share->recPath))
{
QTextStream(stderr) << "err: failed to create recording directory - " << share->recPath << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->buffPath))
{
QTextStream(stderr) << "err: failed to create buffer directory - " << share->buffPath << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->buffPath + "/img"))
{
QTextStream(stderr) << "err: failed to create 'img' in the buffer directory - " << share->buffPath << "/img" << " check for write permissions." << Qt::endl;
}
else if (!mkPath(share->buffPath + "/live"))
{
QTextStream(stderr) << "err: failed to create 'live' in the buffer directory - " << share->buffPath << "/live" << " check for write permissions." << Qt::endl;
}
else
{
share->retCode = 0;
share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName;
share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName;
}
share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName;
share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName;
}
return share->retCode == 0;

View File

@ -30,7 +30,7 @@
using namespace std;
#define APP_VER "3.3.t8"
#define APP_VER "3.3.t9"
#define APP_NAME "Motion Watch"
#define APP_BIN "mow"
#define DATETIME_FMT "yyyyMMddhhmmss"
@ -74,12 +74,11 @@ struct shared_t
QString servMainLoop;
QString servVidLoop;
QString servUser;
QString servGroup;
bool singleTenant;
bool skipCmd;
int liveSecs;
int recFps;
int imgThreads;
int recThreads;
int evMaxSecs;
int postSecs;
int imgThresh;

View File

@ -64,7 +64,7 @@ int loadService(const QString &desc, const QString &user, const QString &servNam
return ret;
}
int loadSh(const QString &name, const QString &exeCmd, const QString &workDir)
int loadSh(const QString &name, const QString &exeCmd, const QString &buffDir, const QString &outDir, const QString &servUser, const QString &servGroup)
{
QFile file("/usr/bin/" + name);
@ -79,11 +79,18 @@ int loadSh(const QString &name, const QString &exeCmd, const QString &workDir)
else
{
file.write("#!/bin/sh\n\n");
file.write("cd " + workDir.toUtf8() + "\n");
file.write("cd " + buffDir.toUtf8() + "\n");
file.write(exeCmd.toUtf8() + "\n");
file.close();
mkPath(buffDir);
mkPath(outDir);
mkPath(buffDir + "/live");
mkPath(buffDir + "/img");
QProcess::execute("chmod", {"-v", "+x", file.fileName()});
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", buffDir});
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", outDir});
}
file.close();
@ -135,8 +142,8 @@ int loadServiceByConf(const QString &confFile, bool start)
{
auto desc = QString(APP_NAME) + " - " + conf.camName;
if (ret == 0) ret = loadSh(conf.servMainLoop, camCmdFromConf(&conf, MAIN_LOOP), conf.buffPath);
if (ret == 0) ret = loadSh(conf.servVidLoop, camCmdFromConf(&conf, VID_LOOP), conf.buffPath);
if (ret == 0) ret = loadSh(conf.servMainLoop, camCmdFromConf(&conf, MAIN_LOOP), conf.buffPath, conf.recPath, conf.servUser, conf.servGroup);
if (ret == 0) ret = loadSh(conf.servVidLoop, camCmdFromConf(&conf, VID_LOOP), conf.buffPath, conf.recPath, conf.servUser, conf.servGroup);
if (ret == 0) ret = loadService(desc, conf.servUser, conf.servMainLoop, conf.buffPath, conf.recPath, start);
if (ret == 0) ret = loadService(desc, conf.servUser, conf.servVidLoop, conf.buffPath, conf.recPath, start);
@ -171,11 +178,7 @@ int rmServiceByConf(const QString &confFile)
conf.retCode = rmService(conf.servMainLoop);
conf.retCode = rmService(conf.servVidLoop);
QDir live(conf.buffPath + "/live");
QDir img(conf.buffPath + "/img");
live.removeRecursively();
img.removeRecursively();
QDir(conf.buffPath).removeRecursively();
}
return conf.retCode;

View File

@ -24,6 +24,6 @@ int loadServiceByDir(const QString &path, bool start);
int rmServiceByConf(const QString &confFile);
int rmService(const QString &servName);
int rmServiceByDir(const QString &path);
int loadSh(const QString &name, const QString &exeCmd, const QString &workDir);
int loadSh(const QString &name, const QString &exeCmd, const QString &buffDir, const QString &outDir, const QString &servUser, const QString &servGroup);
#endif // SERVICES_H