Adjusted the event loop and motion detection to make better stand alone
m3u8 files. Hopefully doing this will make browsers treat the recorded
events as VODs instead of streams.
This commit is contained in:
Maurice ONeal 2023-03-12 13:22:10 -04:00
parent 3c5dbec24c
commit baa69da2cd
4 changed files with 104 additions and 90 deletions

View File

@ -28,6 +28,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <map>
#include <opencv4/opencv2/opencv.hpp> #include <opencv4/opencv2/opencv.hpp>
#include <opencv4/opencv2/videoio.hpp> #include <opencv4/opencv2/videoio.hpp>
@ -37,7 +38,7 @@ using namespace std;
using namespace std::filesystem; using namespace std::filesystem;
using namespace std::chrono; using namespace std::chrono;
#define APP_VER "2.0.t7" #define APP_VER "2.0.t8"
#define APP_NAME "Motion Watch" #define APP_NAME "Motion Watch"
#define REC_LOG_NAME "rec_log_lines.html" #define REC_LOG_NAME "rec_log_lines.html"
#define DET_LOG_NAME "det_log_lines.html" #define DET_LOG_NAME "det_log_lines.html"
@ -45,39 +46,40 @@ using namespace std::chrono;
struct pls_t struct pls_t
{ {
string fileName; string evName;
string clipPath; string dstPath;
string dstPath; vector<string> srcPaths;
string extINF; vector<string> dstPaths;
uint64_t createTime; vector<string> extINFs;
Mat thumbnail; uint64_t createTime;
Mat thumbnail;
}; };
struct shared_t struct shared_t
{ {
vector<pls_t> recList; map<string, pls_t> recList;
string conf; string conf;
string recLog; string recLog;
string detLog; string detLog;
string upkLog; string upkLog;
string recordUrl; string recordUrl;
string outDir; string outDir;
string postCmd; string postCmd;
string camName; string camName;
string webBg; string webBg;
string webTxt; string webTxt;
string webFont; string webFont;
string webRoot; string webRoot;
bool skipCmd; bool skipCmd;
bool postCmdRunning; bool postCmdRunning;
int schSec; int schSec;
int frameGap; int frameGap;
int pixThresh; int pixThresh;
int imgThresh; int imgThresh;
int maxDays; int maxDays;
int maxEvents; int maxEvents;
int maxLogSize; int maxLogSize;
int retCode; int retCode;
}; };
string genVidNameFromLive(const string &tsPath); string genVidNameFromLive(const string &tsPath);

View File

@ -27,35 +27,37 @@ void eventLoop(shared_t *share)
{ {
while (share->retCode == 0) while (share->retCode == 0)
{ {
while (!share->recList.empty()) for (auto it = share->recList.begin(); !share->recList.empty();)
{ {
auto pls = share->recList[0]; auto evName = it->first;
auto timeDiff = pls.createTime - genEpoch(); auto event = it->second;
auto timeDiff = genEpoch() - event.createTime;
// future time protection. this should never occur but // future time protection. this should never occur but
// this is in place in case the system time is incorrect. // this is in place in case the system time is incorrect.
if (timeDiff < 0) timeDiff = 0; if (timeDiff > 0)
// wait at least 6 seconds before processing the event in
// queue.
if (timeDiff < 6) sleep(6 - timeDiff);
try
{ {
wrOutm3u8(pls, share); // wait at least 61 seconds before processing the event in
genHTMLvid(pls.fileName); // queue.
if (timeDiff < 61) sleep(61 - timeDiff);
if (!exists(pls.fileName + ".jpg")) try
{ {
imwrite(string(pls.fileName + ".jpg").c_str(), pls.thumbnail); wrOutm3u8(event, share);
} genHTMLvid(evName);
}
catch (filesystem_error &ex)
{
recLog(string("err: ") + ex.what(), share);
}
share->recList.erase(share->recList.begin()); if (!exists(evName + ".jpg"))
{
imwrite(string(evName + ".jpg").c_str(), event.thumbnail);
}
}
catch (filesystem_error &ex)
{
recLog(string("err: ") + ex.what(), share);
}
share->recList.erase(it);
}
} }
sleep(5); sleep(5);

View File

@ -15,7 +15,9 @@
void detectMoInStream(const string &bufPath, shared_t *share) void detectMoInStream(const string &bufPath, shared_t *share)
{ {
ifstream fileIn(bufPath); ifstream fileIn(bufPath);
pls_t clip; string tsPath;
string extINF;
Mat thumbnail;
auto clipPathFilter = genTimeStr("VIDEO_TS/live/%Y/%j/%H/"); auto clipPathFilter = genTimeStr("VIDEO_TS/live/%Y/%j/%H/");
@ -23,25 +25,42 @@ void detectMoInStream(const string &bufPath, shared_t *share)
{ {
if (line.starts_with(clipPathFilter)) if (line.starts_with(clipPathFilter))
{ {
clip.clipPath = line; tsPath = line;
} }
else if (line.starts_with("#EXTINF")) else if (line.starts_with("#EXTINF"))
{ {
clip.extINF = line; extINF = line;
} }
} }
if (!clip.clipPath.empty() && !clip.extINF.empty()) if (!tsPath.empty() && !extINF.empty())
{ {
if (moDetect(clip.clipPath, clip.thumbnail, share)) if (moDetect(tsPath, thumbnail, share))
{ {
clip.fileName = genTimeStr("%Y-%j-%H-%M"); auto eventName = genTimeStr("%Y-%j-%H-%M");
clip.dstPath = genEventPath(clip.clipPath);
clip.createTime = genEpoch(); if (share->recList.find(eventName) != share->recList.end())
{
share->recList[eventName].srcPaths.push_back(tsPath);
share->recList[eventName].dstPaths.push_back(genEventPath(tsPath));
share->recList[eventName].extINFs.push_back(extINF);
}
else
{
pls_t event;
event.srcPaths.push_back(tsPath);
event.dstPaths.push_back(genEventPath(tsPath));
event.extINFs.push_back(extINF);
event.createTime = genEpoch();
event.thumbnail = thumbnail.clone();
event.evName = eventName;
share->recList.insert(pair{eventName, event});
}
share->skipCmd = true; share->skipCmd = true;
share->recList.push_back(clip);
} }
} }
} }
@ -117,38 +136,32 @@ bool moDetect(const string &buffFile, Mat &vidThumb, shared_t *share)
return mod; return mod;
} }
void wrOutm3u8(const pls_t &pls, shared_t *share) void wrOutm3u8(const pls_t &event, shared_t *share)
{ {
recLog("live-to-event-src: " + pls.clipPath, share); auto plsFile = event.evName + ".m3u8";
recLog("live-to-event-dst: " + pls.dstPath, share);
createDirTree(path(pls.dstPath).parent_path().string()); ofstream file(plsFile.c_str());
copy_file(pls.clipPath, pls.dstPath);
auto plsFile = pls.fileName + ".m3u8"; file << "#EXTM3U" << endl;
file << "#EXT-X-VERSION:3" << endl;
file << "#EXT-X-TARGETDURATION:10" << endl;
file << "#EXT-X-MEDIA-SEQUENCE:0" << endl;
file << "#EXT-X-START:TIME-OFFSET=0" << endl;
file << "#EXT-X-PLAYLIST-TYPE:VOD" << endl;
if (exists(plsFile)) for (auto i = 0; i < event.extINFs.size(); ++i)
{ {
ofstream file(plsFile.c_str(), ofstream::app); auto src = event.srcPaths[i];
auto dst = event.dstPaths[i];
file << endl; createDirTree(path(dst).parent_path().string());
file << pls.extINF << endl; copy_file(src, dst);
file << pls.dstPath;
file.close(); file << event.extINFs[i] << endl;
file << dst << endl;
} }
else
{
ofstream file(plsFile.c_str());
file << "#EXTM3U" << endl; file << "#EXT-X-ENDLIST" << endl;
file << "#EXT-X-VERSION:3" << endl;
file << "#EXT-X-TARGETDURATION:10" << endl;
file << "#EXT-X-MEDIA-SEQUENCE:0" << endl;
file << "#EXT-X-DISCONTINUITY" << endl;
file << pls.extINF << endl;
file << pls.dstPath;
file.close(); file.close();
}
} }

View File

@ -51,10 +51,7 @@ void genHTMLul(const string &outputDir, const string &title, shared_t *share)
htmlText += "</ul>\n"; htmlText += "</ul>\n";
htmlText += "<h4>Motion Events</h4>\n"; htmlText += "<h4>Motion Events</h4>\n";
if (!exists("stream.html")) genHTMLvid("stream");
{
genHTMLvid("stream");
}
} }
for (auto &&regName : regNames) for (auto &&regName : regNames)
@ -71,7 +68,7 @@ void genHTMLul(const string &outputDir, const string &title, shared_t *share)
// regName.substr(0, regName.size() - 5) removes .html // regName.substr(0, regName.size() - 5) removes .html
auto name = regName.substr(0, regName.size() - 5); auto name = regName.substr(0, regName.size() - 5);
htmlText += "<a href='" + regName + "'><img src='" + name + ".jpg" + "' style='width:12%;height:12%;'</a>\n"; htmlText += "<a href='" + regName + "'><img src='" + name + ".jpg" + "' style='width:25%;height:25%;'</a>\n";
} }
} }