v3.3.t9
-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:
parent
bb8a1fad45
commit
7ca4fc3dbe
95
README.md
95
README.md
|
@ -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 ###
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,31 +310,14 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
return share->retCode == 0;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user