diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index d0668668..8ac6bc95 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -151,6 +151,8 @@ AdvSceneSwitcher.condition.video.condition.object="contains object" AdvSceneSwitcher.condition.video.askFileAction="Do you want to use an existing file or create a screenshot of the currently selected source?" AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file" AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot" +AdvSceneSwitcher.condition.video.reduceLatency="Reduce matching latency" +AdvSceneSwitcher.condition.video.reduceLatency.tooltip="Enabling will reduce the matching latency, but will slow down the condition checks of all macros overall." AdvSceneSwitcher.condition.video.usePatternForChangedCheck="Use pattern matching" AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip="This will allow you to control how much the image has to change for the condition to be true." AdvSceneSwitcher.condition.video.patternThreshold="Threshold: " diff --git a/src/macro-core/macro-action-screenshot.cpp b/src/macro-core/macro-action-screenshot.cpp index 86895131..6c3fc28e 100644 --- a/src/macro-core/macro-action-screenshot.cpp +++ b/src/macro-core/macro-action-screenshot.cpp @@ -24,7 +24,7 @@ void MacroActionScreenshot::CustomScreenshot() { auto s = obs_weak_source_get_source(_source); _screenshot.~ScreenshotHelper(); - new (&_screenshot) ScreenshotHelper(s, true, _path); + new (&_screenshot) ScreenshotHelper(s, false, 0, true, _path); obs_source_release(s); } diff --git a/src/macro-external/opencv/macro-condition-video.cpp b/src/macro-external/opencv/macro-condition-video.cpp index 027e5780..2303231f 100644 --- a/src/macro-external/opencv/macro-condition-video.cpp +++ b/src/macro-external/opencv/macro-condition-video.cpp @@ -82,6 +82,10 @@ bool MacroConditionVideo::CheckCondition() return _lastMatchResult; } + if (_blockUntilScreenshotDone) { + GetScreenshot(true); + } + if (_screenshotData.done) { match = Compare(); _lastMatchResult = match; @@ -94,7 +98,7 @@ bool MacroConditionVideo::CheckCondition() match = _lastMatchResult; } - if (_getNextScreenshot) { + if (!_blockUntilScreenshotDone && _getNextScreenshot) { GetScreenshot(); } return match; @@ -106,6 +110,8 @@ bool MacroConditionVideo::Save(obs_data_t *obj) _video.Save(obj); obs_data_set_int(obj, "condition", static_cast(_condition)); obs_data_set_string(obj, "filePath", _file.c_str()); + obs_data_set_bool(obj, "blockUntilScreenshotDone", + _blockUntilScreenshotDone); obs_data_set_bool(obj, "usePatternForChangedCheck", _usePatternForChangedCheck); obs_data_set_double(obj, "threshold", _patternThreshold); @@ -143,6 +149,8 @@ bool MacroConditionVideo::Load(obs_data_t *obj) _condition = static_cast(obs_data_get_int(obj, "condition")); _file = obs_data_get_string(obj, "filePath"); + _blockUntilScreenshotDone = + obs_data_get_bool(obj, "blockUntilScreenshotDone"); _usePatternForChangedCheck = obs_data_get_bool(obj, "usePatternForChangedCheck"); _patternThreshold = obs_data_get_double(obj, "threshold"); @@ -186,11 +194,12 @@ std::string MacroConditionVideo::GetShortDesc() return _video.ToString(); } -void MacroConditionVideo::GetScreenshot() +void MacroConditionVideo::GetScreenshot(bool blocking) { auto source = obs_weak_source_get_source(_video.GetVideo()); _screenshotData.~ScreenshotHelper(); - new (&_screenshotData) ScreenshotHelper(source); + new (&_screenshotData) + ScreenshotHelper(source, blocking, GetSwitcher()->interval); obs_source_release(source); _getNextScreenshot = false; } @@ -287,6 +296,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( : QWidget(parent), _videoSelection(new VideoSelectionWidget(this)), _condition(new QComboBox()), + _reduceLatency(new QCheckBox(obs_module_text( + "AdvSceneSwitcher.condition.video.reduceLatency"))), _usePatternForChangedCheck(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))), _imagePath(new FileSelection()), @@ -326,6 +337,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( "AdvSceneSwitcher.condition.video.showMatch"))), _previewDialog(this, entryData.get(), &GetSwitcher()->m) { + _reduceLatency->setToolTip(obs_module_text( + "AdvSceneSwitcher.condition.video.reduceLatency.tooltip")); _imagePath->Button()->disconnect(); _usePatternForChangedCheck->setToolTip(obs_module_text( "AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip")); @@ -341,6 +354,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( SLOT(VideoSelectionChanged(const VideoSelection &))); QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); + QWidget::connect(_reduceLatency, SIGNAL(stateChanged(int)), this, + SLOT(ReduceLatencyChanged(int))); QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this, SLOT(ImagePathChanged(const QString &))); QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this, @@ -383,6 +398,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( std::unordered_map widgetPlaceholders = { {"{{videoSources}}", _videoSelection}, {"{{condition}}", _condition}, + {"{{reduceLatency}}", _reduceLatency}, {"{{imagePath}}", _imagePath}, {"{{minNeighbors}}", _minNeighbors}, {"{{minSize}}", _minSize}, @@ -442,6 +458,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( mainLayout->addLayout(_sizeLayout); mainLayout->addLayout(_throttleControlLayout); mainLayout->addLayout(_checkAreaControlLayout); + mainLayout->addWidget(_reduceLatency); mainLayout->addLayout(showMatchLayout); setLayout(mainLayout); @@ -635,6 +652,16 @@ void MacroConditionVideoEdit::PatternThresholdChanged(double value) _entryData->_patternThreshold = value; } +void MacroConditionVideoEdit::ReduceLatencyChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(GetSwitcher()->m); + _entryData->_blockUntilScreenshotDone = value; +} + void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value) { if (_loading || !_entryData) { @@ -851,6 +878,7 @@ void MacroConditionVideoEdit::UpdateEntryData() _videoSelection->SetVideoSelection(_entryData->_video); _condition->setCurrentIndex(static_cast(_entryData->_condition)); + _reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone); _imagePath->SetPath(QString::fromStdString(_entryData->_file)); _usePatternForChangedCheck->setChecked( _entryData->_usePatternForChangedCheck); diff --git a/src/macro-external/opencv/macro-condition-video.hpp b/src/macro-external/opencv/macro-condition-video.hpp index 5f068450..aab7c862 100644 --- a/src/macro-external/opencv/macro-condition-video.hpp +++ b/src/macro-external/opencv/macro-condition-video.hpp @@ -40,7 +40,7 @@ public: { return std::make_shared(m); } - void GetScreenshot(); + void GetScreenshot(bool blocking = false); bool LoadImageFromFile(); bool LoadModelData(std::string &path); std::string GetModelDataPath() { return _modelDataPath; } @@ -49,6 +49,13 @@ public: VideoSelection _video; VideoCondition _condition = VideoCondition::MATCH; std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); + // Enabling this will reduce matching latency, but slow down the + // the condition checks of all macros overall. + // + // If not set the screenshot will be gathered in one interval and + // checked in the next one. + // If set both operations will happen in the same interval. + bool _blockUntilScreenshotDone = false; bool _useAlphaAsMask = false; bool _usePatternForChangedCheck = false; PatternMatchData _patternData; @@ -107,8 +114,11 @@ public: private slots: void VideoSelectionChanged(const VideoSelection &); void ConditionChanged(int cond); + void ReduceLatencyChanged(int value); + void ImagePathChanged(const QString &text); void ImageBrowseButtonClicked(); + void UsePatternForChangedCheckChanged(int value); void PatternThresholdChanged(double); void UseAlphaAsMaskChanged(int value); @@ -134,8 +144,11 @@ protected: VideoSelectionWidget *_videoSelection; QComboBox *_condition; + QCheckBox *_reduceLatency; + QCheckBox *_usePatternForChangedCheck; FileSelection *_imagePath; + ThresholdSlider *_patternThreshold; QCheckBox *_useAlphaAsMask; diff --git a/src/utils/screenshot-helper.cpp b/src/utils/screenshot-helper.cpp index 0036604f..47370be1 100644 --- a/src/utils/screenshot-helper.cpp +++ b/src/utils/screenshot-helper.cpp @@ -1,16 +1,35 @@ #include "screenshot-helper.hpp" #include "advanced-scene-switcher.hpp" +#include + static void ScreenshotTick(void *param, float); -ScreenshotHelper::ScreenshotHelper(obs_source_t *source, bool saveToFile, +ScreenshotHelper::ScreenshotHelper(obs_source_t *source, bool blocking, + int timeout, bool saveToFile, std::string path) : weakSource(OBSGetWeakRef(source)), + _blocking(blocking), _saveToFile(saveToFile), _path(path) { + std::unique_lock lock(_mutex); _initDone = true; obs_add_tick_callback(ScreenshotTick, this); + if (_blocking) { + auto res = + _cv.wait_for(lock, std::chrono::milliseconds(timeout)); + if (res == std::cv_status::timeout) { + if (source) { + blog(LOG_WARNING, + "Failed to get screenshot in time for source %s", + obs_source_get_name(source)); + } else { + blog(LOG_WARNING, + "Failed to get screenshot in time"); + } + } + } } ScreenshotHelper::~ScreenshotHelper() @@ -104,6 +123,8 @@ void ScreenshotHelper::MarkDone() { time = std::chrono::high_resolution_clock::now(); done = true; + std::unique_lock lock(_mutex); + _cv.notify_all(); } void ScreenshotHelper::WriteToFile() @@ -148,8 +169,8 @@ static void ScreenshotTick(void *param, float) break; case STAGE_COPY_AND_SAVE: data->Copy(); - data->MarkDone(); data->WriteToFile(); + data->MarkDone(); obs_remove_tick_callback(ScreenshotTick, data); break; diff --git a/src/utils/screenshot-helper.hpp b/src/utils/screenshot-helper.hpp index eef9202b..4c0ed5c5 100644 --- a/src/utils/screenshot-helper.hpp +++ b/src/utils/screenshot-helper.hpp @@ -4,11 +4,14 @@ #include #include #include +#include +#include class ScreenshotHelper { public: ScreenshotHelper() = default; - ScreenshotHelper(obs_source_t *source, bool saveToFile = false, + ScreenshotHelper(obs_source_t *source, bool blocking = false, + int timeout = 1000, bool saveToFile = false, std::string path = ""); ScreenshotHelper &operator=(const ScreenshotHelper &) = delete; ScreenshotHelper(const ScreenshotHelper &) = delete; @@ -34,7 +37,10 @@ public: private: bool _initDone = false; + bool _blocking = false; std::thread _saveThread; bool _saveToFile = false; std::string _path = ""; + std::mutex _mutex; + std::condition_variable _cv; };