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
|
# also note to avoid using empty lines. if you're going to need an empty
|
||||||
# line, start it with a '#'
|
# line, start it with a '#'
|
||||||
#
|
#
|
||||||
recording_stream = rtsp://1.2.3.4:554/h264
|
recording_uri = rtsp://1.2.3.4:554/h264
|
||||||
# this is the url to the main stream of the IP camera that will be used
|
# this is the uri to the main stream of the IP camera that will be used
|
||||||
# to record footage.
|
# 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
|
buffer_path = /var/buffer
|
||||||
# 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
|
|
||||||
# this is the work directory the app will use to store live footage and
|
# 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
|
# 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.
|
# 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
|
cam_name = cam-1
|
||||||
# this is the optional camera name parameter to identify the camera. this
|
# 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
|
# name will also be used to as the base directory in buffer_path or
|
||||||
# defined, the name of the config file will be used.
|
# rec_path. if not defined, the name of the config file will be used.
|
||||||
#
|
#
|
||||||
max_event_secs = 30
|
max_event_secs = 30
|
||||||
# this is the maximum amount of secs of video footage that can be
|
# this is the maximum amount of secs of video footage that can be recorded
|
||||||
# recorded in a motion event.
|
# in a motion event.
|
||||||
#
|
#
|
||||||
img_comp_cmd = magick compare -metric FUZZ &prev& &next& /dev/null
|
img_comp_cmd = magick compare -metric FUZZ &prev& &next& /dev/null
|
||||||
# this is the command line template this application will use when calling
|
# 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,
|
# use to output the comparison score. this can only be stderr or stdout,
|
||||||
# any other stream name is considered invalid.
|
# 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
|
img_thresh = 8000
|
||||||
# this parameter defines the score threshold from img_comp_cmd that will
|
# this parameter defines the score threshold from img_comp_cmd that will
|
||||||
# be considered motion. any motion events will queue up max_event_secs
|
# 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
|
max_events = 100
|
||||||
# this indicates the maximum amount of motion event video clips to keep
|
# 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
|
post_secs = 60
|
||||||
# this is the amount of seconds to wait before running the command
|
# 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.
|
# 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.
|
# note: the call to this command will be delayed if motion was detected.
|
||||||
#
|
#
|
||||||
max_log_size = 50000
|
rec_fps = 30
|
||||||
# this is the maximum byte size of all log files that can be stored in
|
# this sets the recording frames per second for the footage recorded
|
||||||
# web_root. whenever this limit is met, the log file will be deleted and
|
# from the camera. this has no affect of using 'copy' as the
|
||||||
# then eventually recreated blank.
|
# stream_codec.
|
||||||
#
|
#
|
||||||
web_text = #dee5ee
|
rec_scale = 1280:720
|
||||||
# this can be used to customize the color of the text in the web
|
# this sets the pixel scale of the recorded footage from the camera. it
|
||||||
# interface. it can be defined as any color understood by html5 standard.
|
# 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
|
img_scale = 320:240
|
||||||
# this can be used to customize the background color of the web
|
# this sets the pixel size of the thumbnails for recorded stored in
|
||||||
# interface. just like web_text, it also follows the html5 standard.
|
# rec_path. it uses width, height numeric strings seperated by a colon,
|
||||||
|
# eg W:H.
|
||||||
#
|
#
|
||||||
web_font = courier
|
service_user = mow
|
||||||
# this will customize the text font family to use in the web interface.
|
# this sets the service local user of the application dictating the
|
||||||
# it is recommended to use mono-spaced font because this is also used to
|
# amount privilege it will have on the host. if not defined, the
|
||||||
# display logs and logs are best displayed in mono-spaced font.
|
# 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 ###
|
### Setup/Build/Install ###
|
||||||
|
|
|
@ -3,6 +3,10 @@ if [ -f "/opt/mow/uninst" ]; then
|
||||||
mow -u -f
|
mow -u -f
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "/opt" ]; then
|
||||||
|
mkdir /opt
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -d "/opt/mow" ]; then
|
if [ ! -d "/opt/mow" ]; then
|
||||||
mkdir /opt/mow
|
mkdir /opt/mow
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -212,6 +212,7 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->camName.clear();
|
share->camName.clear();
|
||||||
share->buffPath.clear();
|
share->buffPath.clear();
|
||||||
share->recPath.clear();
|
share->recPath.clear();
|
||||||
|
share->servGroup.clear();
|
||||||
|
|
||||||
auto thrCount = QThread::idealThreadCount() / 2;
|
auto thrCount = QThread::idealThreadCount() / 2;
|
||||||
|
|
||||||
|
@ -228,8 +229,6 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->streamExt = ".avi";
|
share->streamExt = ".avi";
|
||||||
share->recExt = ".avi";
|
share->recExt = ".avi";
|
||||||
share->thumbExt = ".jpg";
|
share->thumbExt = ".jpg";
|
||||||
share->imgThreads = thrCount;
|
|
||||||
share->recThreads = thrCount;
|
|
||||||
share->recFps = 30;
|
share->recFps = 30;
|
||||||
share->liveSecs = 80;
|
share->liveSecs = 80;
|
||||||
share->recScale = "1280:720";
|
share->recScale = "1280:720";
|
||||||
|
@ -259,12 +258,11 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
rdLine("stream_ext = ", line, &share->streamExt);
|
rdLine("stream_ext = ", line, &share->streamExt);
|
||||||
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("img_threads = ", line, &share->imgThreads);
|
|
||||||
rdLine("rec_threads = ", line, &share->recThreads);
|
|
||||||
rdLine("rec_fps = ", line, &share->recFps);
|
rdLine("rec_fps = ", line, &share->recFps);
|
||||||
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("service_group = ", line, &share->servGroup);
|
||||||
rdLine("live_secs = ", line, &share->liveSecs);
|
rdLine("live_secs = ", line, &share->liveSecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,31 +309,14 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
{
|
{
|
||||||
share->evMaxSecs = share->liveSecs - 4;
|
share->evMaxSecs = share->liveSecs - 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
share->retCode = EACCES;
|
if (share->servGroup.isEmpty())
|
||||||
|
|
||||||
if (!mkPath(share->recPath))
|
|
||||||
{
|
{
|
||||||
QTextStream(stderr) << "err: failed to create recording directory - " << share->recPath << " check for write permissions." << Qt::endl;
|
share->servGroup = share->servUser;
|
||||||
}
|
|
||||||
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;
|
return share->retCode == 0;
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define APP_VER "3.3.t8"
|
#define APP_VER "3.3.t9"
|
||||||
#define APP_NAME "Motion Watch"
|
#define APP_NAME "Motion Watch"
|
||||||
#define APP_BIN "mow"
|
#define APP_BIN "mow"
|
||||||
#define DATETIME_FMT "yyyyMMddhhmmss"
|
#define DATETIME_FMT "yyyyMMddhhmmss"
|
||||||
|
@ -74,12 +74,11 @@ struct shared_t
|
||||||
QString servMainLoop;
|
QString servMainLoop;
|
||||||
QString servVidLoop;
|
QString servVidLoop;
|
||||||
QString servUser;
|
QString servUser;
|
||||||
|
QString servGroup;
|
||||||
bool singleTenant;
|
bool singleTenant;
|
||||||
bool skipCmd;
|
bool skipCmd;
|
||||||
int liveSecs;
|
int liveSecs;
|
||||||
int recFps;
|
int recFps;
|
||||||
int imgThreads;
|
|
||||||
int recThreads;
|
|
||||||
int evMaxSecs;
|
int evMaxSecs;
|
||||||
int postSecs;
|
int postSecs;
|
||||||
int imgThresh;
|
int imgThresh;
|
||||||
|
|
|
@ -64,7 +64,7 @@ int loadService(const QString &desc, const QString &user, const QString &servNam
|
||||||
return ret;
|
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);
|
QFile file("/usr/bin/" + name);
|
||||||
|
|
||||||
|
@ -79,11 +79,18 @@ int loadSh(const QString &name, const QString &exeCmd, const QString &workDir)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
file.write("#!/bin/sh\n\n");
|
file.write("#!/bin/sh\n\n");
|
||||||
file.write("cd " + workDir.toUtf8() + "\n");
|
file.write("cd " + buffDir.toUtf8() + "\n");
|
||||||
file.write(exeCmd.toUtf8() + "\n");
|
file.write(exeCmd.toUtf8() + "\n");
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
mkPath(buffDir);
|
||||||
|
mkPath(outDir);
|
||||||
|
mkPath(buffDir + "/live");
|
||||||
|
mkPath(buffDir + "/img");
|
||||||
|
|
||||||
QProcess::execute("chmod", {"-v", "+x", file.fileName()});
|
QProcess::execute("chmod", {"-v", "+x", file.fileName()});
|
||||||
|
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", buffDir});
|
||||||
|
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", outDir});
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
@ -135,8 +142,8 @@ int loadServiceByConf(const QString &confFile, bool start)
|
||||||
{
|
{
|
||||||
auto desc = QString(APP_NAME) + " - " + conf.camName;
|
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.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);
|
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.servMainLoop, conf.buffPath, conf.recPath, start);
|
||||||
if (ret == 0) ret = loadService(desc, conf.servUser, conf.servVidLoop, 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.servMainLoop);
|
||||||
conf.retCode = rmService(conf.servVidLoop);
|
conf.retCode = rmService(conf.servVidLoop);
|
||||||
|
|
||||||
QDir live(conf.buffPath + "/live");
|
QDir(conf.buffPath).removeRecursively();
|
||||||
QDir img(conf.buffPath + "/img");
|
|
||||||
|
|
||||||
live.removeRecursively();
|
|
||||||
img.removeRecursively();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return conf.retCode;
|
return conf.retCode;
|
||||||
|
|
|
@ -24,6 +24,6 @@ int loadServiceByDir(const QString &path, bool start);
|
||||||
int rmServiceByConf(const QString &confFile);
|
int rmServiceByConf(const QString &confFile);
|
||||||
int rmService(const QString &servName);
|
int rmService(const QString &servName);
|
||||||
int rmServiceByDir(const QString &path);
|
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
|
#endif // SERVICES_H
|
||||||
|
|
Loading…
Reference in New Issue
Block a user