From 123d308a0d34402f27402b0adf094faf52158d07 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 28 Mar 2022 21:52:38 +0200 Subject: [PATCH] Add option to select OSB' main video output --- data/locale/en-US.ini | 1 + .../opencv/CMakeLists.txt | 6 +- .../opencv/macro-condition-video.cpp | 42 +++--- .../opencv/macro-condition-video.hpp | 7 +- .../opencv/preview-dialog.cpp | 5 +- .../opencv/video-selection.cpp | 122 ++++++++++++++++++ .../opencv/video-selection.hpp | 43 ++++++ src/headers/utility.hpp | 4 +- src/utility.cpp | 7 +- 9 files changed, 206 insertions(+), 31 deletions(-) create mode 100644 src/external-macro-modules/opencv/video-selection.cpp create mode 100644 src/external-macro-modules/opencv/video-selection.hpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index a1bd6bd4..1988153f 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -729,6 +729,7 @@ AdvSceneSwitcher.selectWindow="--select window--" AdvSceneSwitcher.selectSource="--select source--" AdvSceneSwitcher.selectAudioSource="--select audio source--" AdvSceneSwitcher.selectVideoSource="--select video source--" +AdvSceneSwitcher.OBSVideoOutput="OBS video output" AdvSceneSwitcher.selectMediaSource="--select media source--" AdvSceneSwitcher.selectProcess="--select process--" AdvSceneSwitcher.selectFilter="--select filter--" diff --git a/src/external-macro-modules/opencv/CMakeLists.txt b/src/external-macro-modules/opencv/CMakeLists.txt index b3bd6b0c..c7ad8110 100644 --- a/src/external-macro-modules/opencv/CMakeLists.txt +++ b/src/external-macro-modules/opencv/CMakeLists.txt @@ -21,12 +21,14 @@ set(module_SOURCES area-selection.hpp macro-condition-video.cpp macro-condition-video.hpp + opencv-helpers.cpp + opencv-helpers.hpp preview-dialog.cpp preview-dialog.hpp threshold-slider.cpp threshold-slider.hpp - opencv-helpers.cpp - opencv-helpers.hpp) + video-selection.cpp + video-selection.hpp) add_library(advanced-scene-switcher-opencv MODULE ${module_SOURCES}) if(BUILD_OUT_OF_TREE) diff --git a/src/external-macro-modules/opencv/macro-condition-video.cpp b/src/external-macro-modules/opencv/macro-condition-video.cpp index e470bfbd..69d0c956 100644 --- a/src/external-macro-modules/opencv/macro-condition-video.cpp +++ b/src/external-macro-modules/opencv/macro-condition-video.cpp @@ -72,8 +72,11 @@ bool MacroConditionVideo::CheckShouldBeSkipped() bool MacroConditionVideo::CheckCondition() { - bool match = false; + if (!_video.ValidSelection()) { + return false; + } + bool match = false; if (CheckShouldBeSkipped()) { return _lastMatchResult; } @@ -99,8 +102,7 @@ bool MacroConditionVideo::CheckCondition() bool MacroConditionVideo::Save(obs_data_t *obj) { MacroCondition::Save(obj); - obs_data_set_string(obj, "videoSource", - GetWeakSourceName(_videoSource).c_str()); + _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, "usePatternForChangedCheck", @@ -133,8 +135,10 @@ bool isMinNeighborsValid(int minNeighbors) bool MacroConditionVideo::Load(obs_data_t *obj) { MacroCondition::Load(obj); - const char *videoSourceName = obs_data_get_string(obj, "videoSource"); - _videoSource = GetWeakSourceByName(videoSourceName); + _video.Load(obj); + if (obs_data_has_user_value(obj, "videoSource")) { + _video.Load(obj, "videoSource"); + } _condition = static_cast(obs_data_get_int(obj, "condition")); _file = obs_data_get_string(obj, "filePath"); @@ -178,15 +182,12 @@ bool MacroConditionVideo::Load(obs_data_t *obj) std::string MacroConditionVideo::GetShortDesc() { - if (_videoSource) { - return GetWeakSourceName(_videoSource); - } - return ""; + return _video.ToString(); } void MacroConditionVideo::GetScreenshot() { - auto source = obs_weak_source_get_source(_videoSource); + auto source = obs_weak_source_get_source(_video.GetVideo()); _screenshotData.~ScreenshotHelper(); new (&_screenshotData) ScreenshotHelper(source); obs_source_release(source); @@ -281,7 +282,7 @@ static inline void populateConditionSelection(QComboBox *list) MacroConditionVideoEdit::MacroConditionVideoEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), - _videoSelection(new QComboBox()), + _videoSelection(new VideoSelectionWidget(this)), _condition(new QComboBox()), _usePatternForChangedCheck(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))), @@ -331,8 +332,9 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( _throttleCount->setSingleStep(GetSwitcher()->interval); QWidget::connect(_videoSelection, - SIGNAL(currentTextChanged(const QString &)), this, - SLOT(SourceChanged(const QString &))); + SIGNAL(VideoSelectionChange(const VideoSelection &)), + this, + SLOT(VideoSelectionChanged(const VideoSelection &))); QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this, @@ -371,7 +373,6 @@ MacroConditionVideoEdit::MacroConditionVideoEdit( QWidget::connect(_selectArea, SIGNAL(clicked()), this, SLOT(SelectAreaClicked())); - populateVideoSelection(_videoSelection); populateConditionSelection(_condition); QHBoxLayout *entryLine1Layout = new QHBoxLayout; @@ -471,14 +472,14 @@ void MacroConditionVideoEdit::UpdatePreviewTooltip() this->setToolTip(html); } -void MacroConditionVideoEdit::SourceChanged(const QString &text) +void MacroConditionVideoEdit::VideoSelectionChanged(const VideoSelection &v) { if (_loading || !_entryData) { return; } std::lock_guard lock(GetSwitcher()->m); - _entryData->_videoSource = GetWeakSourceByQString(text); + _entryData->_video = v; _entryData->ResetLastMatch(); emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); @@ -533,7 +534,7 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked() QString path; bool useExistingFile = false; // Ask whether to create screenshot or to select existing file - if (_entryData->_videoSource) { + if (_entryData->_video.ValidSelection()) { QMessageBox msgBox( QMessageBox::Question, obs_module_text("AdvSceneSwitcher.windowTitle"), @@ -560,8 +561,8 @@ void MacroConditionVideoEdit::ImageBrowseButtonClicked() } } else { - auto source = - obs_weak_source_get_source(_entryData->_videoSource); + auto source = obs_weak_source_get_source( + _entryData->_video.GetVideo()); ScreenshotHelper screenshot(source); obs_source_release(source); @@ -818,8 +819,7 @@ void MacroConditionVideoEdit::UpdateEntryData() return; } - _videoSelection->setCurrentText( - GetWeakSourceName(_entryData->_videoSource).c_str()); + _videoSelection->SetVideoSelection(_entryData->_video); _condition->setCurrentIndex(static_cast(_entryData->_condition)); _imagePath->SetPath(QString::fromStdString(_entryData->_file)); _usePatternForChangedCheck->setChecked( diff --git a/src/external-macro-modules/opencv/macro-condition-video.hpp b/src/external-macro-modules/opencv/macro-condition-video.hpp index 5f845b34..5f068450 100644 --- a/src/external-macro-modules/opencv/macro-condition-video.hpp +++ b/src/external-macro-modules/opencv/macro-condition-video.hpp @@ -3,6 +3,7 @@ #include "threshold-slider.hpp" #include "preview-dialog.hpp" #include "area-selection.hpp" +#include "video-selection.hpp" #include #include @@ -45,7 +46,7 @@ public: std::string GetModelDataPath() { return _modelDataPath; } void ResetLastMatch() { _lastMatchResult = false; } - OBSWeakSource _videoSource; + VideoSelection _video; VideoCondition _condition = VideoCondition::MATCH; std::string _file = obs_module_text("AdvSceneSwitcher.enterPath"); bool _useAlphaAsMask = false; @@ -104,7 +105,7 @@ public: void UpdatePreviewTooltip(); private slots: - void SourceChanged(const QString &text); + void VideoSelectionChanged(const VideoSelection &); void ConditionChanged(int cond); void ImagePathChanged(const QString &text); void ImageBrowseButtonClicked(); @@ -130,7 +131,7 @@ signals: void HeaderInfoChanged(const QString &); protected: - QComboBox *_videoSelection; + VideoSelectionWidget *_videoSelection; QComboBox *_condition; QCheckBox *_usePatternForChangedCheck; diff --git a/src/external-macro-modules/opencv/preview-dialog.cpp b/src/external-macro-modules/opencv/preview-dialog.cpp index 10278f87..de29f6c0 100644 --- a/src/external-macro-modules/opencv/preview-dialog.cpp +++ b/src/external-macro-modules/opencv/preview-dialog.cpp @@ -126,7 +126,7 @@ void PreviewDialog::CheckForMatchLoop() while (!_stop) { std::unique_lock lock(*_mtx); auto source = obs_weak_source_get_source( - _conditionData->_videoSource); + _conditionData->_video.GetVideo()); ScreenshotHelper screenshot(source); obs_source_release(source); cv.wait_for(lock, std::chrono::seconds(1)); @@ -136,7 +136,8 @@ void PreviewDialog::CheckForMatchLoop() if (isHidden()) { continue; } - if (!screenshot.done) { + if (!screenshot.done || + !_conditionData->_video.ValidSelection()) { _statusLabel->setText(obs_module_text( "AdvSceneSwitcher.condition.video.screenshotFail")); _imageLabel->setPixmap(QPixmap()); diff --git a/src/external-macro-modules/opencv/video-selection.cpp b/src/external-macro-modules/opencv/video-selection.cpp new file mode 100644 index 00000000..6003b0dd --- /dev/null +++ b/src/external-macro-modules/opencv/video-selection.cpp @@ -0,0 +1,122 @@ +#include "video-selection.hpp" +#include "utility.hpp" + +void VideoSelection::Save(obs_data_t *obj, const char *name, + const char *typeName) +{ + obs_data_set_int(obj, typeName, static_cast(_type)); + + switch (_type) { + case VideoSelectionType::SOURCE: + obs_data_set_string(obj, name, + GetWeakSourceName(_source).c_str()); + break; + default: + break; + } +} + +void VideoSelection::Load(obs_data_t *obj, const char *name, + const char *typeName) +{ + _type = static_cast( + obs_data_get_int(obj, typeName)); + auto target = obs_data_get_string(obj, name); + switch (_type) { + case VideoSelectionType::SOURCE: + _source = GetWeakSourceByName(target); + break; + case VideoSelectionType::OBS_MAIN: + _source = nullptr; + break; + default: + break; + } +} + +OBSWeakSource VideoSelection::GetVideo() +{ + if (_type == VideoSelectionType::SOURCE) { + return _source; + } + return nullptr; +} + +std::string VideoSelection::ToString() +{ + switch (_type) { + case VideoSelectionType::SOURCE: + return GetWeakSourceName(_source); + case VideoSelectionType::OBS_MAIN: + return obs_module_text("AdvSceneSwitcher.OBSVideoOutput"); + default: + break; + } + return ""; +} + +bool VideoSelection::ValidSelection() +{ + return _type == VideoSelectionType::OBS_MAIN || !!_source; +} + +VideoSelectionWidget::VideoSelectionWidget(QWidget *parent, bool addOBSVideoOut) + : QComboBox(parent) +{ + setDuplicatesEnabled(true); + populateVideoSelection(this, addOBSVideoOut); + QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(SelectionChanged(const QString &))); +} + +void VideoSelectionWidget::SetVideoSelection(VideoSelection &t) +{ + int idx; + switch (t.GetType()) { + case VideoSelectionType::SOURCE: + setCurrentText(QString::fromStdString(t.ToString())); + break; + case VideoSelectionType::OBS_MAIN: + idx = findText(QString::fromStdString(obs_module_text( + obs_module_text("AdvSceneSwitcher.OBSVideoOutput")))); + if (idx != -1) { + setCurrentIndex(idx); + } + break; + default: + setCurrentIndex(0); + break; + } +} + +static bool isFirstEntry(QComboBox *l, QString name, int idx) +{ + for (int i = 0; i < l->count(); i++) { + if (l->itemText(i) == name) { + return idx == i; + } + } + return false; +} + +bool VideoSelectionWidget::IsOBSVideoOutSelected(const QString &name) +{ + if (name == QString::fromStdString(obs_module_text( + "AdvSceneSwitcher.OBSVideoOutput"))) { + return isFirstEntry(this, name, currentIndex()); + } + return false; +} + +void VideoSelectionWidget::SelectionChanged(const QString &name) +{ + VideoSelection t; + if (IsOBSVideoOutSelected(name)) { + t._type = VideoSelectionType::OBS_MAIN; + } else { + auto source = GetWeakSourceByQString(name); + t._type = VideoSelectionType::SOURCE; + t._source = source; + } + emit VideoSelectionChange(t); +} diff --git a/src/external-macro-modules/opencv/video-selection.hpp b/src/external-macro-modules/opencv/video-selection.hpp new file mode 100644 index 00000000..0f362e7a --- /dev/null +++ b/src/external-macro-modules/opencv/video-selection.hpp @@ -0,0 +1,43 @@ +#pragma once +#include +#include +#include + +enum class VideoSelectionType { + SOURCE, + OBS_MAIN, +}; + +class VideoSelection { +public: + void Save(obs_data_t *obj, const char *name = "video", + const char *typeName = "videoType"); + void Load(obs_data_t *obj, const char *name = "video", + const char *typeName = "videoType"); + + VideoSelectionType GetType() { return _type; } + OBSWeakSource GetVideo(); + std::string ToString(); + bool ValidSelection(); + +private: + OBSWeakSource _source; + VideoSelectionType _type = VideoSelectionType::SOURCE; + friend class VideoSelectionWidget; +}; + +class VideoSelectionWidget : public QComboBox { + Q_OBJECT + +public: + VideoSelectionWidget(QWidget *parent, bool addOBSVideoOut = true); + void SetVideoSelection(VideoSelection &); +signals: + void VideoSelectionChange(const VideoSelection &); + +private slots: + void SelectionChanged(const QString &name); + +private: + bool IsOBSVideoOutSelected(const QString &name); +}; diff --git a/src/headers/utility.hpp b/src/headers/utility.hpp index 7aa75597..aff6bbc3 100644 --- a/src/headers/utility.hpp +++ b/src/headers/utility.hpp @@ -63,8 +63,8 @@ void populateTransitionSelection(QComboBox *sel, bool addCurrent = true, bool addAny = false); void populateWindowSelection(QComboBox *sel, bool addSelect = true); void populateAudioSelection(QComboBox *sel, bool addSelect = true); -void populateVideoSelection(QComboBox *sel, bool addScenes = false, - bool addSelect = true); +void populateVideoSelection(QComboBox *sel, bool addMainOutput = false, + bool addScenes = false, bool addSelect = true); void populateMediaSelection(QComboBox *sel, bool addSelect = true); void populateProcessSelection(QComboBox *sel, bool addSelect = true); void populateSourceSelection(QComboBox *list, bool addSelect = true); diff --git a/src/utility.cpp b/src/utility.cpp index da455874..26953277 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -600,7 +600,8 @@ void populateAudioSelection(QComboBox *sel, bool addSelect) sel->setCurrentIndex(0); } -void populateVideoSelection(QComboBox *sel, bool addScenes, bool addSelect) +void populateVideoSelection(QComboBox *sel, bool addMainOutput, bool addScenes, + bool addSelect) { auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */ @@ -628,6 +629,10 @@ void populateVideoSelection(QComboBox *sel, bool addScenes, bool addSelect) } sel->model()->sort(0); + if (addMainOutput) { + sel->insertItem( + 0, obs_module_text("AdvSceneSwitcher.OBSVideoOutput")); + } if (addSelect) { addSelectionEntry( sel,