diff --git a/CMakeLists.txt b/CMakeLists.txt index 169898f..7c31c93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) include_directories(${OpenCV_INCLUDE_DIRS}) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) +find_package(QT NAMES Qt6 COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Multimedia REQUIRED) find_package(OpenCV REQUIRED) add_executable(mow @@ -31,4 +31,4 @@ add_executable(mow src/camera.cpp ) -target_link_libraries(mow Qt${QT_VERSION_MAJOR}::Core ${OpenCV_LIBS}) +target_link_libraries(mow Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Multimedia ${OpenCV_LIBS}) diff --git a/setup.sh b/setup.sh index 18b6c6f..fb6c338 100644 --- a/setup.sh +++ b/setup.sh @@ -1,24 +1,4 @@ #!/bin/sh apt update -y -apt install -y pkg-config -apt install -y cmake -apt install -y make -apt install -y g++ -apt install -y wget -apt install -y unzip -apt install -y git -apt install -y ffmpeg -apt install -y libavcodec-dev -apt install -y libavformat-dev -apt install -y libavutil-dev -apt install -y libswscale-dev -apt install -y libgstreamer1.0-dev -apt install -y x264 -apt install -y libx264-dev -apt install -y libilmbase-dev -apt install -y libopencv-dev -apt install -y qtbase5-dev -apt install -y qtchooser -apt install -y qt5-qmake -apt install -y qtbase5-dev-tools -apt install -y apache2 +apt install -y pkg-config cmake make g++ wget unzip git +apt install -y ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libgstreamer1.0-dev x264 libx264-dev libilmbase-dev libopencv-dev qt6-base-dev qtchooser qmake6 qt6-base-dev-tools qt6-multimedia-dev apache2 diff --git a/src/camera.cpp b/src/camera.cpp index bfd7c0d..e6df08b 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -19,6 +19,7 @@ Camera::Camera(QObject *parent) : QObject(parent) shared.camName.clear(); shared.retCode = 0; + shared.maxScore = 0; shared.pixThresh = 50; shared.imgThresh = 800; shared.maxEvents = 40; @@ -240,7 +241,8 @@ bool EventLoop::exec() if (wrOutVod(event)) { genHTMLvod(event.evName); - imwrite(QString("events/" + event.evName + ".jpg").toUtf8().data(), event.thumbnail); + + event.thumbnail.save(QString("events/" + event.evName + ".jpg")); } shared->recList.removeFirst(); @@ -314,16 +316,22 @@ bool EventLoop::wrOutVod(const evt_t &event) DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent) { - heartBeat = 0; - evId = 0; - pcId = 0; + heartBeat = 0; + evId = 0; + pcId = 0; + player = new QMediaPlayer(this); + frameAnalyzer = new MoDetect(shared, this); + + player->setVideoSink(frameAnalyzer); + + connect(player, &QMediaPlayer::errorOccurred, this, &DetectLoop::playError); } void DetectLoop::init() { thread()->sleep(1); - resetTimers(); Loop::init(); + resetTimers(); } void DetectLoop::resetTimers() @@ -357,7 +365,8 @@ void DetectLoop::timerEvent(QTimerEvent *event) shared->curEvent.srcPaths.clear(); shared->curEvent.evName.clear(); - shared->curEvent.thumbnail.release(); + + shared->curEvent.thumbnail = QImage(); shared->maxScore = 0; } @@ -380,6 +389,31 @@ void DetectLoop::timerEvent(QTimerEvent *event) } } +void DetectLoop::resetDetect() +{ + if (player->playbackState() != QMediaPlayer::PlayingState) + { + frameAnalyzer->stop(); + + exec(); + } +} + +void DetectLoop::playError(QMediaPlayer::Error error, const QString &errorString) +{ + Q_UNUSED(error) + + detLog("err: media player error - " + errorString, shared); + resetDetect(); +} + +void DetectLoop::playStateChanged(QMediaPlayer::PlaybackState newState) +{ + Q_UNUSED(newState) + + resetDetect(); +} + bool DetectLoop::exec() { QFile fileIn("stream.m3u8"); @@ -429,12 +463,9 @@ bool DetectLoop::exec() { prevTs = tsPath; - if (moDetect(tsPath, thread(), shared)) - { - shared->curEvent.srcPaths.append(tsPath); - - shared->skipCmd = true; - } + player->setSource(QUrl::fromLocalFile(tsPath)); + frameAnalyzer->play(tsPath); + player->play(); } return Loop::exec(); diff --git a/src/camera.h b/src/camera.h index 834f211..642ef8a 100644 --- a/src/camera.h +++ b/src/camera.h @@ -114,16 +114,21 @@ class DetectLoop : public Loop private: - int pcId; - int evId; - QString prevTs; + int pcId; + int evId; + QString prevTs; + QMediaPlayer *player; + MoDetect *frameAnalyzer; void resetTimers(); + void resetDetect(); void timerEvent(QTimerEvent *event); private slots: void init(); + void playError(QMediaPlayer::Error error, const QString &errorString); + void playStateChanged(QMediaPlayer::PlaybackState newState); public: diff --git a/src/common.h b/src/common.h index 726ea67..d65459f 100644 --- a/src/common.h +++ b/src/common.h @@ -23,6 +23,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -31,7 +35,7 @@ using namespace cv; using namespace std; -#define APP_VER "3.0.t3" +#define APP_VER "3.0.t4" #define APP_NAME "Motion Watch" #define APP_BIN "mow" #define REC_LOG_NAME "rec_log_lines.html" @@ -42,7 +46,7 @@ struct evt_t { QString evName; QStringList srcPaths; - Mat thumbnail; + QImage thumbnail; }; struct shared_t diff --git a/src/mo_detect.cpp b/src/mo_detect.cpp index d1536dc..15339f4 100644 --- a/src/mo_detect.cpp +++ b/src/mo_detect.cpp @@ -12,93 +12,70 @@ #include "mo_detect.h" -bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share) +MoDetect::MoDetect(shared_t *share, QObject *parent) : QVideoSink(parent) { - Mat prevGray; - Mat nextGray; + shared = share; - cvtColor(prev, prevGray, COLOR_BGR2GRAY); - cvtColor(next, nextGray, COLOR_BGR2GRAY); - - Mat diff; - - absdiff(prevGray, nextGray, diff); - threshold(diff, diff, share->pixThresh, 255, THRESH_BINARY); - - score = countNonZero(diff); - - detLog("diff_score: " + QString::number(score) + " thresh: " + QString::number(share->imgThresh), share); - - return score >= share->imgThresh; + connect(this, &MoDetect::videoFrameChanged, this, &MoDetect::newFrame); } -bool moDetect(const QString &buffFile, QThread *thr, shared_t *share) +void MoDetect::stop() { - auto score = 0; - auto mod = false; + baseImg = QImage(); gap = 0; shared->maxScore = 0; +} - detLog("stream_clip: " + buffFile, share); +void MoDetect::play(const QString &path) +{ + filePath = path; +} - VideoCapture capture; - - if (!capture.open(buffFile.toUtf8().data(), CAP_FFMPEG)) +void MoDetect::newFrame() +{ + if (!baseImg.isNull()) { - thr->usleep(500); - - capture.open(buffFile.toUtf8().data(), CAP_FFMPEG); + baseImg = videoFrame().toImage().convertToFormat(QImage::Format_Grayscale8); } - - if (capture.isOpened()) + else if (shared->frameGap > gap) { - Mat prev; - Mat next; - - int fps = capture.get(cv::CAP_PROP_FPS); - - for (auto gap = 0, frm = fps; capture.grab(); ++gap, ++frm) - { - if (frm == fps) thr->sleep(1); frm = 1; - - if (prev.empty()) - { - capture.retrieve(prev); - } - else if (gap == (share->frameGap - 1)) - { - capture.retrieve(next); - - if (!next.empty()) - { - if (imgDiff(prev, next, score, share)) - { - mod = true; - - if (share->maxScore <= score) - { - share->maxScore = score; - - resize(next, share->curEvent.thumbnail, Size(720, 480), INTER_LINEAR); - } - } - } - - prev = next.clone(); - gap = 0; - - next.release(); - } - else - { - capture.grab(); - } - } + gap++; } else { - detLog("err: failed to open: " + buffFile + " after 500 msecs. giving up.", share); + auto nextImg = videoFrame().toImage(); + auto nextImgGray = nextImg.convertToFormat(QImage::Format_Grayscale8); + auto score = 0; + + if (diff(baseImg, nextImgGray, &score)) + { + shared->skipCmd = true; + + if (!shared->curEvent.srcPaths.contains(filePath)) + { + shared->curEvent.srcPaths.append(filePath); + + if (shared->maxScore <= score) + { + shared->maxScore = score; + + shared->curEvent.thumbnail = nextImg; + } + } + } } - - capture.release(); - - return mod; +} + +bool MoDetect::diff(QImage &base, QImage &next, int *score) +{ + Mat baseMat = Mat(base.height(), base.width(), CV_8U, base.bits(), base.bytesPerLine()); + Mat nextMat = Mat(next.height(), next.width(), CV_8U, next.bits(), next.bytesPerLine()); + Mat diffMat; + + absdiff(baseMat, nextMat, diffMat); + threshold(diffMat, diffMat, shared->pixThresh, 255, THRESH_BINARY); + + *score = countNonZero(diffMat); + + detLog("diff_score: " + QString::number(*score) + " thresh: " + QString::number(shared->imgThresh), shared); + + return *score >= shared->imgThresh; } diff --git a/src/mo_detect.h b/src/mo_detect.h index fc7f320..3048356 100644 --- a/src/mo_detect.h +++ b/src/mo_detect.h @@ -16,7 +16,29 @@ #include "common.h" #include "logger.h" -bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share); -bool moDetect(const QString &buffFile, QThread *thr, shared_t *share); +class MoDetect : public QVideoSink +{ + Q_OBJECT + +private: + + QImage baseImg; + QString filePath; + shared_t *shared; + int gap; + + bool diff(QImage &base, QImage &next, int *score); + +private slots: + + void newFrame(); + +public: + + void play(const QString &path); + void stop(); + + explicit MoDetect(shared_t *share, QObject *parent = nullptr); +}; #endif // MO_DETECT_H