mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dd2170dfa | ||
|
|
5f3a3dd5cf | ||
|
|
8ad3563963 | ||
|
|
492128ef86 | ||
|
|
b4936274f2 | ||
|
|
64ccd5ba53 |
@@ -17,7 +17,7 @@ AdvSceneSwitcher.generalTab.status.autoStart.never="Never"
|
|||||||
AdvSceneSwitcher.generalTab.status.autoStart.recording="Recording"
|
AdvSceneSwitcher.generalTab.status.autoStart.recording="Recording"
|
||||||
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streaming"
|
AdvSceneSwitcher.generalTab.status.autoStart.streaming="Streaming"
|
||||||
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Recording or Streaming"
|
AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming="Recording or Streaming"
|
||||||
AdvSceneSwitcher.generalTab.status.checkInterval="Check switch conditions every"
|
AdvSceneSwitcher.generalTab.status.checkInterval="Check conditions every"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior="General behavior"
|
AdvSceneSwitcher.generalTab.generalBehavior="General behavior"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no actions are performed for"
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMet="If no actions are performed for"
|
||||||
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval."
|
AdvSceneSwitcher.generalTab.generalBehavior.onNoMetDelayTooltip="Will only ever be as accurate as the configured check interval."
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ void SwitcherData::Thread()
|
|||||||
switcher->firstIntervalAfterStop = false;
|
switcher->firstIntervalAfterStop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainLoopLock = nullptr;
|
||||||
blog(LOG_INFO, "stopped");
|
blog(LOG_INFO, "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,25 +106,12 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene,
|
|||||||
scene, transition, _duration.seconds);
|
scene, transition, _duration.seconds);
|
||||||
switcher->abortMacroWait = false;
|
switcher->abortMacroWait = false;
|
||||||
|
|
||||||
bool isInMainLoop = QThread::currentThread() == switcher->th;
|
std::unique_lock<std::mutex> lock(switcher->m);
|
||||||
if (isInMainLoop) {
|
if (expectedTransitionDuration < 0) {
|
||||||
if (expectedTransitionDuration < 0) {
|
waitForTransitionChange(transition, &lock, GetMacro());
|
||||||
waitForTransitionChange(transition, switcher->GetLock(),
|
|
||||||
GetMacro());
|
|
||||||
} else {
|
|
||||||
waitForTransitionChangeFixedDuration(
|
|
||||||
expectedTransitionDuration, switcher->GetLock(),
|
|
||||||
GetMacro());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
std::mutex temp;
|
waitForTransitionChangeFixedDuration(expectedTransitionDuration,
|
||||||
std::unique_lock<std::mutex> lock(temp);
|
&lock, GetMacro());
|
||||||
if (expectedTransitionDuration < 0) {
|
|
||||||
waitForTransitionChange(transition, &lock, GetMacro());
|
|
||||||
} else {
|
|
||||||
waitForTransitionChangeFixedDuration(
|
|
||||||
expectedTransitionDuration, &lock, GetMacro());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !switcher->abortMacroWait;
|
return !switcher->abortMacroWait;
|
||||||
|
|||||||
@@ -52,14 +52,8 @@ bool MacroActionWait::PerformAction()
|
|||||||
std::chrono::milliseconds((int)(sleepDuration * 1000));
|
std::chrono::milliseconds((int)(sleepDuration * 1000));
|
||||||
|
|
||||||
switcher->abortMacroWait = false;
|
switcher->abortMacroWait = false;
|
||||||
bool isInMainLoop = QThread::currentThread() == switcher->th;
|
std::unique_lock<std::mutex> lock(switcher->m);
|
||||||
if (isInMainLoop) {
|
waitHelper(&lock, GetMacro(), time);
|
||||||
waitHelper(switcher->GetLock(), GetMacro(), time);
|
|
||||||
} else {
|
|
||||||
std::mutex temp;
|
|
||||||
std::unique_lock<std::mutex> lock(temp);
|
|
||||||
waitHelper(&lock, GetMacro(), time);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !switcher->abortMacroWait;
|
return !switcher->abortMacroWait;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "macro-tree.hpp"
|
#include "macro-tree.hpp"
|
||||||
#include "macro.hpp"
|
#include "macro.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
#include "switcher-data-structs.hpp"
|
||||||
|
|
||||||
#include <obs.h>
|
#include <obs.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -563,6 +564,7 @@ void MacroTreeModel::GroupSelectedItems(QModelIndexList &indices)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
QString name = GetNewGroupName();
|
QString name = GetNewGroupName();
|
||||||
std::vector<std::shared_ptr<Macro>> items;
|
std::vector<std::shared_ptr<Macro>> items;
|
||||||
items.reserve(indices.size());
|
items.reserve(indices.size());
|
||||||
@@ -606,6 +608,7 @@ void MacroTreeModel::UngroupSelectedGroups(QModelIndexList &indices)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
for (int i = indices.count() - 1; i >= 0; i--) {
|
for (int i = indices.count() - 1; i >= 0; i--) {
|
||||||
std::shared_ptr<Macro> item = _macros[ModelIndexToMacroIndex(
|
std::shared_ptr<Macro> item = _macros[ModelIndexToMacroIndex(
|
||||||
indices[i].row(), _macros)];
|
indices[i].row(), _macros)];
|
||||||
@@ -966,6 +969,7 @@ void MacroTree::dropEvent(QDropEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move items in backend
|
// Move items in backend
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
int to = row;
|
int to = row;
|
||||||
try {
|
try {
|
||||||
to = ModelIndexToMacroIndex(row, items);
|
to = ModelIndexToMacroIndex(row, items);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Macro::CreateGroup(const std::string &name,
|
|||||||
{
|
{
|
||||||
auto group = std::make_shared<Macro>(name, false);
|
auto group = std::make_shared<Macro>(name, false);
|
||||||
for (auto &c : children) {
|
for (auto &c : children) {
|
||||||
c->SetParent(group.get());
|
c->SetParent(group);
|
||||||
}
|
}
|
||||||
group->_isGroup = true;
|
group->_isGroup = true;
|
||||||
group->_groupSize = children.size();
|
group->_groupSize = children.size();
|
||||||
@@ -82,7 +82,7 @@ void Macro::PrepareMoveToGroup(std::shared_ptr<Macro> group,
|
|||||||
oldGroup->_groupSize--;
|
oldGroup->_groupSize--;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->SetParent(group.get());
|
item->SetParent(group);
|
||||||
if (group) {
|
if (group) {
|
||||||
group->_groupSize++;
|
group->_groupSize++;
|
||||||
}
|
}
|
||||||
@@ -204,8 +204,9 @@ bool Macro::PerformActions(bool forceParallel, bool ignorePause)
|
|||||||
RunActions(ret, ignorePause);
|
RunActions(ret, ignorePause);
|
||||||
}
|
}
|
||||||
_wasExecutedRecently = true;
|
_wasExecutedRecently = true;
|
||||||
if (_parent) {
|
auto group = _parent.lock();
|
||||||
_parent->_wasExecutedRecently = true;
|
if (group) {
|
||||||
|
group->_wasExecutedRecently = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -313,6 +314,12 @@ void Macro::UpdateConditionIndices()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Macro *Macro::Parent()
|
||||||
|
{
|
||||||
|
auto p = _parent.lock();
|
||||||
|
return p.get();
|
||||||
|
}
|
||||||
|
|
||||||
bool Macro::Save(obs_data_t *obj) const
|
bool Macro::Save(obs_data_t *obj) const
|
||||||
{
|
{
|
||||||
obs_data_set_string(obj, "name", _name.c_str());
|
obs_data_set_string(obj, "name", _name.c_str());
|
||||||
@@ -709,18 +716,41 @@ void SwitcherData::loadMacros(obs_data_t *obj)
|
|||||||
obs_data_array_release(macroArray);
|
obs_data_array_release(macroArray);
|
||||||
|
|
||||||
int groupCount = 0;
|
int groupCount = 0;
|
||||||
Macro *group = nullptr;
|
std::shared_ptr<Macro> group;
|
||||||
|
std::vector<std::shared_ptr<Macro>> invalidGroups;
|
||||||
for (auto &m : macros) {
|
for (auto &m : macros) {
|
||||||
|
if (groupCount && m->IsGroup()) {
|
||||||
|
blog(LOG_ERROR,
|
||||||
|
"nested group detected - will delete \"%s\"",
|
||||||
|
m->Name().c_str());
|
||||||
|
invalidGroups.emplace_back(m);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (groupCount) {
|
if (groupCount) {
|
||||||
m->SetParent(group);
|
m->SetParent(group);
|
||||||
groupCount--;
|
groupCount--;
|
||||||
}
|
}
|
||||||
if (m->IsGroup()) {
|
if (m->IsGroup()) {
|
||||||
groupCount = m->GroupSize();
|
groupCount = m->GroupSize();
|
||||||
group = m.get();
|
group = m;
|
||||||
}
|
}
|
||||||
m->PostLoad();
|
m->PostLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (groupCount) {
|
||||||
|
blog(LOG_ERROR,
|
||||||
|
"invalid group size detected - will delete \"%s\"",
|
||||||
|
group->Name().c_str());
|
||||||
|
invalidGroups.emplace_back(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &m : invalidGroups) {
|
||||||
|
auto it = std::find(macros.begin(), macros.end(), m);
|
||||||
|
if (it == macros.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
macros.erase(it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SwitcherData::checkMacros()
|
bool SwitcherData::checkMacros()
|
||||||
@@ -741,8 +771,28 @@ bool SwitcherData::checkMacros()
|
|||||||
|
|
||||||
bool SwitcherData::runMacros()
|
bool SwitcherData::runMacros()
|
||||||
{
|
{
|
||||||
for (auto m : macros) {
|
// Create copy of macor list as elements might be removed, inserted, or
|
||||||
if (m->Matched()) {
|
// reordered while macros are currently being executed.
|
||||||
|
// For example, this can happen if a macro is performing a wait action,
|
||||||
|
// as the main lock will be unlocked during this time.
|
||||||
|
auto runPhaseMacros = macros;
|
||||||
|
|
||||||
|
// Avoid deadlocks when opening settings window and calling frontend
|
||||||
|
// API functions at the same time.
|
||||||
|
//
|
||||||
|
// If the timing is just right, the frontend API call will call
|
||||||
|
// QMetaObject::invokeMethod(...) with Qt::BlockingQueuedConnection
|
||||||
|
// while holding the main switcher mutex.
|
||||||
|
// But this invokeMethod call itself will be blocked as it is waiting
|
||||||
|
// the constructor of AdvSceneSwitcher() to complete.
|
||||||
|
// The constructor of AdvSceneSwitcher() cannot continue however as it
|
||||||
|
// cannot lock the main switcher mutex.
|
||||||
|
if (GetLock()) {
|
||||||
|
GetLock()->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &m : runPhaseMacros) {
|
||||||
|
if (m && m->Matched()) {
|
||||||
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
|
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
|
||||||
if (!m->PerformActions()) {
|
if (!m->PerformActions()) {
|
||||||
blog(LOG_WARNING, "abort macro: %s",
|
blog(LOG_WARNING, "abort macro: %s",
|
||||||
@@ -750,6 +800,9 @@ bool SwitcherData::runMacros()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (GetLock()) {
|
||||||
|
GetLock()->lock();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ public:
|
|||||||
std::shared_ptr<Macro> item);
|
std::shared_ptr<Macro> item);
|
||||||
bool IsGroup() { return _isGroup; }
|
bool IsGroup() { return _isGroup; }
|
||||||
uint32_t GroupSize() { return _groupSize; }
|
uint32_t GroupSize() { return _groupSize; }
|
||||||
bool IsSubitem() { return !!_parent; }
|
bool IsSubitem() { return !_parent.expired(); }
|
||||||
void SetCollapsed(bool val) { _isCollapsed = val; }
|
void SetCollapsed(bool val) { _isCollapsed = val; }
|
||||||
bool IsCollapsed() { return _isCollapsed; }
|
bool IsCollapsed() { return _isCollapsed; }
|
||||||
void SetParent(Macro *m) { _parent = m; }
|
void SetParent(std::shared_ptr<Macro> m) { _parent = m; }
|
||||||
Macro *Parent() { return _parent; }
|
Macro *Parent();
|
||||||
|
|
||||||
bool Save(obs_data_t *obj) const;
|
bool Save(obs_data_t *obj) const;
|
||||||
bool Load(obs_data_t *obj);
|
bool Load(obs_data_t *obj);
|
||||||
@@ -93,7 +93,7 @@ private:
|
|||||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||||
std::deque<std::shared_ptr<MacroAction>> _actions;
|
std::deque<std::shared_ptr<MacroAction>> _actions;
|
||||||
|
|
||||||
Macro *_parent = nullptr;
|
std::weak_ptr<Macro> _parent;
|
||||||
uint32_t _groupSize = 0;
|
uint32_t _groupSize = 0;
|
||||||
|
|
||||||
bool _runInParallel = false;
|
bool _runInParallel = false;
|
||||||
|
|||||||
@@ -198,12 +198,21 @@ void PreviewDialog::CheckForMatchLoop()
|
|||||||
if (_stop || isHidden()) {
|
if (_stop || isHidden()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!screenshot.done || !_video.ValidSelection()) {
|
if (!_video.ValidSelection()) {
|
||||||
_statusLabel->setText(obs_module_text(
|
_statusLabel->setText(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||||
_imageLabel->setPixmap(QPixmap());
|
_imageLabel->setPixmap(QPixmap());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!screenshot.done) {
|
||||||
|
_statusLabel->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.screenshotFail"));
|
||||||
|
_imageLabel->setPixmap(QPixmap());
|
||||||
|
if (_delay < 1000) {
|
||||||
|
_delay += 50;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (screenshot.image.width() == 0 ||
|
if (screenshot.image.width() == 0 ||
|
||||||
screenshot.image.height() == 0) {
|
screenshot.image.height() == 0) {
|
||||||
_statusLabel->setText(obs_module_text(
|
_statusLabel->setText(obs_module_text(
|
||||||
@@ -220,6 +229,8 @@ void PreviewDialog::CheckForMatchLoop()
|
|||||||
_areaParams.area.height);
|
_areaParams.area.height);
|
||||||
}
|
}
|
||||||
MarkMatch(screenshot.image);
|
MarkMatch(screenshot.image);
|
||||||
|
} else {
|
||||||
|
_statusLabel->setText("");
|
||||||
}
|
}
|
||||||
_imageLabel->setPixmap(QPixmap::fromImage(screenshot.image));
|
_imageLabel->setPixmap(QPixmap::fromImage(screenshot.image));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ struct SwitcherData {
|
|||||||
|
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
std::unique_lock<std::mutex> *mainLoopLock;
|
std::unique_lock<std::mutex> *mainLoopLock = nullptr;
|
||||||
|
|
||||||
bool transitionActive = false;
|
bool transitionActive = false;
|
||||||
bool waitForTransition = false;
|
bool waitForTransition = false;
|
||||||
|
|||||||
@@ -39,9 +39,8 @@ ScreenshotHelper::~ScreenshotHelper()
|
|||||||
gs_stagesurface_destroy(stagesurf);
|
gs_stagesurface_destroy(stagesurf);
|
||||||
gs_texrender_destroy(texrender);
|
gs_texrender_destroy(texrender);
|
||||||
obs_leave_graphics();
|
obs_leave_graphics();
|
||||||
|
|
||||||
obs_remove_tick_callback(ScreenshotTick, this);
|
|
||||||
}
|
}
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
if (_saveThread.joinable()) {
|
if (_saveThread.joinable()) {
|
||||||
_saveThread.join();
|
_saveThread.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
std::chrono::high_resolution_clock::time_point time;
|
std::chrono::high_resolution_clock::time_point time;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _initDone = false;
|
std::atomic_bool _initDone = false;
|
||||||
bool _blocking = false;
|
bool _blocking = false;
|
||||||
std::thread _saveThread;
|
std::thread _saveThread;
|
||||||
bool _saveToFile = false;
|
bool _saveToFile = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user