-the live stream is now a part of the camera's main page.
-fixed symm links for the main page not being created on install.
-removed deprecated ffmpeg timeout option.
-copied hls.js as a local file so live streams will still work
 without being connected to the internet.
This commit is contained in:
Zii 2023-08-26 09:52:51 -04:00
parent 3791b29cf7
commit 8f80ce34f1
7 changed files with 115 additions and 94 deletions

2
bin/hls.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -23,19 +23,22 @@ if [ ! -d "/var/opt/mow/web" ]; then
mkdir /var/opt/mow/web mkdir /var/opt/mow/web
fi fi
if [ -f "/var/opt/mow/web/index.html" ]; then if [ -e "/var/opt/mow/web/index.html" ]; then
rm -v /var/opt/mow/web/index.html rm -v /var/opt/mow/web/index.html
touch /var/opt/mow/buf/index.html
ln -sv /var/opt/mow/buf/index.html /var/opt/mow/web/index.html
fi fi
if [ -f "/var/opt/mow/web/theme.css" ]; then if [ -e "/var/opt/mow/web/theme.css" ]; then
rm -v /var/opt/mow/web/theme.css rm -v /var/opt/mow/web/theme.css
touch /var/opt/mow/buf/theme.css
ln -sv /var/opt/mow/buf/theme.css /var/opt/mow/web/theme.css
fi fi
touch /var/opt/mow/buf/index.html
touch /var/opt/mow/buf/theme.css
ln -sv /var/opt/mow/buf/index.html /var/opt/mow/web/index.html
ln -sv /var/opt/mow/buf/theme.css /var/opt/mow/web/theme.css
cp -v ./.build-mow/mow /opt/mow/bin cp -v ./.build-mow/mow /opt/mow/bin
cp -v ./bin/hls.js /var/opt/mow/web/hls.js
echo "writing /opt/mow/run" echo "writing /opt/mow/run"
printf "#!/bin/sh\n" > /opt/mow/run printf "#!/bin/sh\n" > /opt/mow/run

View File

@ -14,6 +14,16 @@
Camera::Camera(QObject *parent) : QObject(parent) {} Camera::Camera(QObject *parent) : QObject(parent) {}
void Camera::cleanup()
{
QProcess::execute("rm", {shared.outDir + "/live"});
QProcess::execute("rm", {shared.outDir + "/logs"});
QProcess::execute("rm", {shared.outDir + "/img"});
QProcess::execute("rm", {shared.outDir + "/index.html"});
QProcess::execute("rm", {shared.outDir + "/stream.m3u8"});
QProcess::execute("rm", {shared.tmpDir + "/events"});
}
int Camera::start(const QStringList &args) int Camera::start(const QStringList &args)
{ {
if (rdConf(getParam("-c", args), &shared)) if (rdConf(getParam("-c", args), &shared))
@ -26,17 +36,17 @@ int Camera::start(const QStringList &args)
QDir().mkpath(shared.tmpDir + "/logs"); QDir().mkpath(shared.tmpDir + "/logs");
QDir().mkpath(shared.tmpDir + "/img"); QDir().mkpath(shared.tmpDir + "/img");
cleanup();
touch(shared.tmpDir + "/index.html"); touch(shared.tmpDir + "/index.html");
touch(shared.tmpDir + "/stream.html");
touch(shared.tmpDir + "/stream.m3u8"); touch(shared.tmpDir + "/stream.m3u8");
QFile::link(shared.tmpDir + "/live", shared.outDir + "/live"); QProcess::execute("ln", {"-s", shared.tmpDir + "/live", shared.outDir + "/live"});
QFile::link(shared.tmpDir + "/logs", shared.outDir + "/logs"); QProcess::execute("ln", {"-s", shared.tmpDir + "/logs", shared.outDir + "/logs"});
QFile::link(shared.tmpDir + "/img", shared.outDir + "/img"); QProcess::execute("ln", {"-s", shared.tmpDir + "/img", shared.outDir + "/img"});
QFile::link(shared.tmpDir + "/index.html", shared.outDir + "/index.html"); QProcess::execute("ln", {"-s", shared.tmpDir + "/index.html", shared.outDir + "/index.html"});
QFile::link(shared.tmpDir + "/stream.html", shared.outDir + "/stream.html"); QProcess::execute("ln", {"-s", shared.tmpDir + "/stream.m3u8", shared.outDir + "/stream.m3u8"});
QFile::link(shared.tmpDir + "/stream.m3u8", shared.outDir + "/stream.m3u8"); QProcess::execute("ln", {"-s", shared.outDir + "/events", shared.tmpDir + "/events"});
QFile::link(shared.outDir + "/events", shared.tmpDir + "/events");
if (!QDir::setCurrent(shared.tmpDir)) if (!QDir::setCurrent(shared.tmpDir))
{ {
@ -142,7 +152,6 @@ void RecLoop::updateCmd()
recArgs << "-hls_list_size" << "1000"; recArgs << "-hls_list_size" << "1000";
recArgs << "-hls_flags" << "append_list+omit_endlist"; recArgs << "-hls_flags" << "append_list+omit_endlist";
recArgs << "-rtsp_transport" << "tcp"; recArgs << "-rtsp_transport" << "tcp";
recArgs << "-stimeout" << "3000";
recArgs << "-t" << QString::number(heartBeat); recArgs << "-t" << QString::number(heartBeat);
recArgs << "stream.m3u8"; recArgs << "stream.m3u8";
@ -152,7 +161,6 @@ void RecLoop::updateCmd()
imgArgs << "-strftime_mkdir" << "1"; imgArgs << "-strftime_mkdir" << "1";
imgArgs << "-vf" << "fps=1,scale=320:240"; imgArgs << "-vf" << "fps=1,scale=320:240";
imgArgs << "-rtsp_transport" << "tcp"; imgArgs << "-rtsp_transport" << "tcp";
imgArgs << "-stimeout" << "3000";
imgArgs << "-t" << QString::number(heartBeat); imgArgs << "-t" << QString::number(heartBeat);
imgArgs << "img/" + QString(STRFTIME_FMT) + ".bmp"; imgArgs << "img/" + QString(STRFTIME_FMT) + ".bmp";
@ -233,10 +241,9 @@ bool Upkeep::exec()
enforceMaxImages(); enforceMaxImages();
enforceMaxVids(); enforceMaxVids();
genHTMLul(".", shared->camName, shared); genFrontPage(shared);
genCSS(shared); genCSS(shared);
genHTMLul(shared->buffPath, QString(APP_NAME) + " " + QString(APP_VER), shared); genCamPage(shared);
return Loop::exec(); return Loop::exec();
} }

View File

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

View File

@ -30,7 +30,7 @@
using namespace std; using namespace std;
#define APP_VER "3.2.t2" #define APP_VER "3.2.t3"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define APP_BIN "mow" #define APP_BIN "mow"
#define REC_LOG_NAME "rec_log_lines.html" #define REC_LOG_NAME "rec_log_lines.html"

View File

@ -12,12 +12,8 @@
#include "web.h" #include "web.h"
void genHTMLul(const QString &outputDir, const QString &title, shared_t *share) void genFrontPage(shared_t *share)
{ {
QStringList logNames;
QStringList eveNames;
QStringList dirNames;
QString htmlText = "<!DOCTYPE html>\n"; QString htmlText = "<!DOCTYPE html>\n";
htmlText += "<html>\n"; htmlText += "<html>\n";
@ -29,12 +25,47 @@ void genHTMLul(const QString &outputDir, const QString &title, shared_t *share)
htmlText += "<link rel='stylesheet' href='/theme.css'>\n"; htmlText += "<link rel='stylesheet' href='/theme.css'>\n";
htmlText += "</head>\n"; htmlText += "</head>\n";
htmlText += "<body>\n"; htmlText += "<body>\n";
htmlText += "<h3>" + title + "</h3>\n"; htmlText += "<h3>" + QString(APP_NAME) + " " + QString(APP_VER) + "</h3>\n";
if (QDir().exists(outputDir + "/live")) auto dirNames = lsDirsInDir(share->buffPath);
htmlText += "<ul>\n";
for (auto &&dirName : dirNames)
{ {
eveNames = lsFilesInDir(outputDir + "/events", ".html"); htmlText += " <li><a href='" + dirName + "/index.html'>" + dirName + "</a></li>\n";
logNames = lsFilesInDir(outputDir + "/logs", "_log.html"); }
htmlText += "</ul>\n";
htmlText += "</body>\n";
htmlText += "</html>";
QFile outFile(QDir().cleanPath(share->buffPath) + "/index.html");
outFile.open(QFile::WriteOnly);
outFile.write(htmlText.toUtf8());
outFile.close();
}
void genCamPage(shared_t *share)
{
auto outputDir = QDir().cleanPath(share->tmpDir);
QString htmlText = "<!DOCTYPE html>\n";
htmlText += "<html>\n";
htmlText += "<head>\n";
htmlText += "<meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\" />\n";
htmlText += "<meta http-equiv=\"Pragma\" content=\"no-cache\" />\n";
htmlText += "<meta http-equiv=\"Expires\" content=\"0\" />\n";
htmlText += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n";
htmlText += "<link rel='stylesheet' href='/theme.css'>\n";
htmlText += "</head>\n";
htmlText += "<body>\n";
htmlText += "<h3>" + share->camName + "</h3>\n";
if (QDir().exists(outputDir + "/logs"))
{
auto logNames = lsFilesInDir(outputDir + "/logs", "_log.html");
htmlText += "<h4>Logs</h4>\n"; htmlText += "<h4>Logs</h4>\n";
htmlText += "<ul>\n"; htmlText += "<ul>\n";
@ -49,13 +80,20 @@ void genHTMLul(const QString &outputDir, const QString &title, shared_t *share)
} }
htmlText += "</ul>\n"; htmlText += "</ul>\n";
htmlText += "<h4>Live</h4>\n"; }
htmlText += "<ul>\n";
htmlText += " <li><a href='stream.html'>" + share->camName + ":live" + "</a></li>\n";
htmlText += "</ul>\n";
htmlText += "<h4>Motion Events</h4>\n";
genHTMLstream("stream"); if (QDir().exists(outputDir + "/live"))
{
htmlText += "<h4>Live</h4>\n";
genHTMLstream(htmlText);
}
if (QDir().exists(outputDir + "/events"))
{
auto eveNames = lsFilesInDir(outputDir + "/events", ".html");
htmlText += "<h4>Motion Events</h4>\n";
for (auto &&eveName : eveNames) for (auto &&eveName : eveNames)
{ {
@ -66,19 +104,6 @@ void genHTMLul(const QString &outputDir, const QString &title, shared_t *share)
htmlText += "<a href='events/" + eveName + "'><img src='events/" + name + ".jpg" + "' style='width:25%;height:25%;'</a>\n"; htmlText += "<a href='events/" + eveName + "'><img src='events/" + name + ".jpg" + "' style='width:25%;height:25%;'</a>\n";
} }
} }
else
{
dirNames = lsDirsInDir(outputDir);
htmlText += "<ul>\n";
for (auto &&dirName : dirNames)
{
htmlText += " <li><a href='" + dirName + "/index.html'>" + dirName + "</a></li>\n";
}
htmlText += "</ul>\n";
}
htmlText += "</body>\n"; htmlText += "</body>\n";
htmlText += "</html>"; htmlText += "</html>";
@ -90,51 +115,32 @@ void genHTMLul(const QString &outputDir, const QString &title, shared_t *share)
outFile.close(); outFile.close();
} }
void genHTMLstream(const QString &name) void genHTMLstream(QString &text)
{ {
QString htmlText = "<!DOCTYPE html>\n"; text += "<script src=\"/hls.js\">\n";
text += "</script>\n";
htmlText += "<html>\n"; text += "<video width=50% height=50% id=\"video\" controls>\n";
htmlText += "<head>\n"; text += "</video>\n";
htmlText += "<meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\" />\n"; text += "<script>\n";
htmlText += "<meta http-equiv=\"Pragma\" content=\"no-cache\" />\n"; text += " var video = document.getElementById('video');\n";
htmlText += "<meta http-equiv=\"Expires\" content=\"0\" />\n"; text += " if (Hls.isSupported()) {\n";
htmlText += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"; text += " var hls = new Hls({\n";
htmlText += "<link rel='stylesheet' href='/theme.css'>\n"; text += " debug: true,\n";
htmlText += "</head>\n"; text += " });\n";
htmlText += "<body>\n"; text += " hls.loadSource('stream.m3u8');\n";
htmlText += " <script src=\"https://cdn.jsdelivr.net/npm/hls.js@1\">\n"; text += " hls.attachMedia(video);\n";
htmlText += " </script>\n"; text += " hls.on(Hls.Events.MEDIA_ATTACHED, function () {\n";
htmlText += " <video width=100% height=100% id=\"video\" controls>\n"; text += " video.muted = true;\n";
htmlText += " </video>\n"; text += " video.play();\n";
htmlText += " <script>\n"; text += " });\n";
htmlText += " var video = document.getElementById('video');\n"; text += " }\n";
htmlText += " if (Hls.isSupported()) {\n"; text += " else if (video.canPlayType('application/vnd.apple.mpegurl')) {\n";
htmlText += " var hls = new Hls({\n"; text += " video.src = 'stream.m3u8';\n";
htmlText += " debug: true,\n"; text += " video.addEventListener('canplay', function () {\n";
htmlText += " });\n"; text += " video.play();\n";
htmlText += " hls.loadSource('" + name + ".m3u8');\n"; text += " });\n";
htmlText += " hls.attachMedia(video);\n"; text += " }\n";
htmlText += " hls.on(Hls.Events.MEDIA_ATTACHED, function () {\n"; text += "</script>\n";
htmlText += " video.muted = true;\n";
htmlText += " video.play();\n";
htmlText += " });\n";
htmlText += " }\n";
htmlText += " else if (video.canPlayType('application/vnd.apple.mpegurl')) {\n";
htmlText += " video.src = '" + name + ".m3u8';\n";
htmlText += " video.addEventListener('canplay', function () {\n";
htmlText += " video.play();\n";
htmlText += " });\n";
htmlText += " }\n";
htmlText += " </script>\n";
htmlText += "</body>\n";
htmlText += "</html>";
QFile outFile(name + ".html");
outFile.open(QFile::WriteOnly);
outFile.write(htmlText.toUtf8());
outFile.close();
} }
void genHTMLvod(const QString &name) void genHTMLvod(const QString &name)

View File

@ -15,8 +15,9 @@
#include "common.h" #include "common.h"
void genHTMLul(const QString &outputDir, const QString &title, shared_t *share); void genFrontPage(shared_t *share);
void genHTMLstream(const QString &name); void genCamPage(shared_t *share);
void genHTMLstream(QString &text);
void genHTMLvod(const QString &name); void genHTMLvod(const QString &name);
void genCSS(shared_t *share); void genCSS(shared_t *share);