Add transition condition

This commit is contained in:
WarmUpTill
2021-09-22 21:53:34 +02:00
committed by WarmUpTill
parent 9be00ba3ca
commit 08933a3683
6 changed files with 370 additions and 0 deletions

View File

@@ -241,6 +241,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-condition-source.hpp
src/headers/macro-condition-streaming.hpp
src/headers/macro-condition-timer.hpp
src/headers/macro-condition-transition.hpp
src/headers/macro-condition-video.hpp
src/headers/macro-condition-virtual-cam.hpp
src/headers/macro-condition-window.hpp
@@ -330,6 +331,7 @@ set(advanced-scene-switcher_SOURCES
src/macro-condition-source.cpp
src/macro-condition-streaming.cpp
src/macro-condition-timer.cpp
src/macro-condition-transition.cpp
src/macro-condition-video.cpp
src/macro-condition-virtual-cam.cpp
src/macro-condition-window.cpp

View File

@@ -215,6 +215,13 @@ AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
AdvSceneSwitcher.condition.sceneTransform.entry.line1="On {{scenes}} {{sources}} matches transform"
AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}"
AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}"
AdvSceneSwitcher.condition.transition="Transition"
AdvSceneSwitcher.condition.transition.type.current="Current transition type is"
AdvSceneSwitcher.condition.transition.type.duration="Current transition duration is"
AdvSceneSwitcher.condition.transition.type.started="Transition active"
AdvSceneSwitcher.condition.transition.type.ended="Transition ended"
AdvSceneSwitcher.condition.transition.durationSuffix="seconds"
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{duration}}{{durationSuffix}}"
; Macro Actions
AdvSceneSwitcher.action.switchScene="Switch scene"

View File

@@ -547,6 +547,12 @@ void setReplayBufferSaved()
switcher->replayBufferSaved = true;
}
void setTranstionEnd()
{
switcher->lastTransitionEndTime =
std::chrono::high_resolution_clock::now();
}
// Note to future self:
// be careful using switcher->m here as there is potential for deadlocks when using
// frontend functions such as obs_frontend_set_current_scene()
@@ -583,6 +589,9 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
setReplayBufferSaved();
break;
#endif
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
setTranstionEnd();
break;
default:
break;
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include "macro.hpp"
#include "duration-control.hpp"
#include "transition-selection.hpp"
#include <QWidget>
#include <QComboBox>
enum class TransitionCondition {
CURRENT,
DURATION,
STARTED,
ENDED,
};
class MacroConditionTransition : public MacroCondition {
public:
bool CheckCondition();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetShortDesc();
std::string GetId() { return id; };
static std::shared_ptr<MacroCondition> Create()
{
return std::make_shared<MacroConditionTransition>();
}
void ConnectToTransitionSignals();
void DisconnectTransitionSignals();
TransitionCondition _condition = TransitionCondition::CURRENT;
TransitionSelection _transition;
Duration _duration;
private:
static void TransitionStarted(void *data, calldata_t *);
static void TransitionEnded(void *data, calldata_t *);
bool _started = false;
bool _ended = false;
std::chrono::high_resolution_clock::time_point _lastTransitionEndTime{};
static bool _registered;
static const std::string id;
};
class MacroConditionTransitionEdit : public QWidget {
Q_OBJECT
public:
MacroConditionTransitionEdit(
QWidget *parent,
std::shared_ptr<MacroConditionTransition> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionTransitionEdit(
parent,
std::dynamic_pointer_cast<MacroConditionTransition>(
cond));
}
private slots:
void ConditionChanged(int cond);
void TransitionChanged(const TransitionSelection &);
void DurationChanged(double seconds);
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_conditions;
TransitionSelectionWidget *_transitions;
DurationSelection *_duration;
QLabel *_durationSuffix;
std::shared_ptr<MacroConditionTransition> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@@ -80,6 +80,7 @@ struct SwitcherData {
OBSWeakSource currentScene = nullptr;
OBSWeakSource previousScene = nullptr;
std::chrono::high_resolution_clock::time_point lastSceneChangeTime{};
std::chrono::high_resolution_clock::time_point lastTransitionEndTime{};
OBSWeakSource lastRandomScene;
SceneGroup *lastRandomSceneGroup;
OBSWeakSource nonMatchingScene;

View File

@@ -0,0 +1,272 @@
#include "headers/macro-condition-edit.hpp"
#include "headers/macro-condition-transition.hpp"
#include "headers/utility.hpp"
#include "headers/advanced-scene-switcher.hpp"
const std::string MacroConditionTransition::id = "transition";
bool MacroConditionTransition::_registered = MacroConditionFactory::Register(
MacroConditionTransition::id,
{MacroConditionTransition::Create, MacroConditionTransitionEdit::Create,
"AdvSceneSwitcher.condition.transition", false});
static std::map<TransitionCondition, std::string> filterConditionTypes = {
{TransitionCondition::CURRENT,
"AdvSceneSwitcher.condition.transition.type.current"},
{TransitionCondition::DURATION,
"AdvSceneSwitcher.condition.transition.type.duration"},
{TransitionCondition::STARTED,
"AdvSceneSwitcher.condition.transition.type.started"},
{TransitionCondition::ENDED,
"AdvSceneSwitcher.condition.transition.type.ended"},
};
bool isCurrentTransition(OBSWeakSource &t)
{
bool match;
auto tSource = obs_frontend_get_current_transition();
auto tWeakSource = obs_source_get_weak_source(tSource);
match = t == tWeakSource;
obs_weak_source_release(tWeakSource);
obs_source_release(tSource);
return match;
}
// Relies on the fact that switcher->currentScene will only be updated on event
// OBS_FRONTEND_EVENT_SCENE_CHANGED but obs_frontend_get_current_scene() will
// already return the scene to be transitioned to.
bool anySceneTransitionStarted()
{
bool ret;
auto currentSceneSrouce = obs_frontend_get_current_scene();
auto currentScene = obs_source_get_weak_source(currentSceneSrouce);
ret = switcher->currentScene != currentScene;
obs_weak_source_release(currentScene);
obs_source_release(currentSceneSrouce);
return ret;
}
bool MacroConditionTransition::CheckCondition()
{
bool anyTransitionEnded = _lastTransitionEndTime !=
switcher->lastTransitionEndTime;
bool transitionStarted = false;
bool transitionEnded = false;
if (_transition.GetType() == TransitionSelectionType::ANY) {
transitionStarted = anySceneTransitionStarted();
transitionEnded = anyTransitionEnded;
} else {
transitionStarted = _started;
transitionEnded = _ended;
}
bool ret = false;
switch (_condition) {
case TransitionCondition::CURRENT: {
auto transition = _transition.GetTransition();
ret = isCurrentTransition(transition);
break;
}
case TransitionCondition::DURATION:
ret = _duration.seconds * 1000 ==
obs_frontend_get_transition_duration();
break;
case TransitionCondition::STARTED:
ret = transitionStarted;
break;
case TransitionCondition::ENDED:
ret = transitionEnded;
break;
default:
break;
}
// Reset for next interval
if (_started) {
_started = false;
}
if (_ended) {
_ended = false;
}
if (anyTransitionEnded) {
_lastTransitionEndTime = switcher->lastTransitionEndTime;
}
return ret;
}
void MacroConditionTransition::ConnectToTransitionSignals()
{
auto source = obs_weak_source_get_source(_transition.GetTransition());
auto sh = obs_source_get_signal_handler(source);
signal_handler_connect(sh, "transition_start", TransitionStarted, this);
signal_handler_connect(sh, "transition_stop", TransitionEnded, this);
obs_source_release(source);
}
void MacroConditionTransition::DisconnectTransitionSignals()
{
auto source = obs_weak_source_get_source(_transition.GetTransition());
auto sh = obs_source_get_signal_handler(source);
signal_handler_disconnect(sh, "transition_start", TransitionStarted,
this);
signal_handler_disconnect(sh, "transition_stop", TransitionEnded, this);
obs_source_release(source);
}
void MacroConditionTransition::TransitionStarted(void *data, calldata_t *)
{
auto *transitionCond = static_cast<MacroConditionTransition *>(data);
transitionCond->_started = true;
}
void MacroConditionTransition::TransitionEnded(void *data, calldata_t *)
{
auto *transitionCond = static_cast<MacroConditionTransition *>(data);
transitionCond->_ended = true;
}
bool MacroConditionTransition::Save(obs_data_t *obj)
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
_transition.Save(obj);
_duration.Save(obj);
return true;
}
bool MacroConditionTransition::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_condition = static_cast<TransitionCondition>(
obs_data_get_int(obj, "condition"));
_transition.Load(obj);
_duration.Load(obj);
ConnectToTransitionSignals();
return true;
}
std::string MacroConditionTransition::GetShortDesc()
{
return _transition.ToString();
}
static inline void populateConditionSelection(QComboBox *list)
{
for (auto entry : filterConditionTypes) {
list->addItem(obs_module_text(entry.second.c_str()));
}
}
MacroConditionTransitionEdit::MacroConditionTransitionEdit(
QWidget *parent, std::shared_ptr<MacroConditionTransition> entryData)
: QWidget(parent)
{
_conditions = new QComboBox();
_transitions = new TransitionSelectionWidget(this, true, true);
_duration = new DurationSelection(this, false);
_durationSuffix = new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.transition.durationSuffix"));
populateConditionSelection(_conditions);
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_transitions,
SIGNAL(TransitionChanged(const TransitionSelection &)),
this,
SLOT(TransitionChanged(const TransitionSelection &)));
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
SLOT(DurationChanged(double)));
QHBoxLayout *mainLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{conditions}}", _conditions},
{"{{transitions}}", _transitions},
{"{{duration}}", _duration},
{"{{durationSuffix}}", _durationSuffix},
};
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.transition.entry"),
mainLayout, widgetPlaceholders);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionTransitionEdit::ConditionChanged(int index)
{
if (_loading || !_entryData) {
return;
}
{
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_condition =
static_cast<TransitionCondition>(index);
}
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionTransitionEdit::TransitionChanged(
const TransitionSelection &t)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->DisconnectTransitionSignals();
_entryData->_transition = t;
_entryData->ConnectToTransitionSignals();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionTransitionEdit::DurationChanged(double seconds)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration.seconds = seconds;
}
void MacroConditionTransitionEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
_transitions->setVisible(
_entryData->_condition == TransitionCondition::CURRENT ||
_entryData->_condition == TransitionCondition::STARTED ||
_entryData->_condition == TransitionCondition::ENDED);
_duration->setVisible(_entryData->_condition ==
TransitionCondition::DURATION);
_durationSuffix->setVisible(_entryData->_condition ==
TransitionCondition::DURATION);
bool addCurrent = _entryData->_condition ==
TransitionCondition::DURATION;
bool addAny = _entryData->_condition == TransitionCondition::STARTED ||
_entryData->_condition == TransitionCondition::ENDED;
_transitions->Repopulate(addCurrent, addAny);
}
void MacroConditionTransitionEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
SetWidgetVisibility();
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_transitions->SetTransition(_entryData->_transition);
_duration->SetDuration(_entryData->_duration);
}