diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 9de1230b..f1fc9e12 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -365,7 +365,7 @@ - Use thread priority (experimental) + Use thread priority @@ -385,6 +385,13 @@ + + + + (raising the priority higher than "Normal" is usually not recommended) + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index 0f2196f2..df59e982 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -64,8 +64,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent) auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */ { QComboBox *combo = reinterpret_cast(data); - if (obs_source_media_get_state(source) != - OBS_MEDIA_STATE_NONE) { + if (strcmp(obs_source_get_id(source), "ffmpeg_source") == 0) { const char *name = obs_source_get_name(source); combo->addItem(name); } @@ -354,7 +353,7 @@ SceneSwitcher::SceneSwitcher(QWidget *parent) ui->readPathLineEdit->setDisabled(true); } - if (switcher->th.joinable()) + if (switcher->th && switcher->th->isRunning()) SetStarted(); else SetStopped(); @@ -401,8 +400,17 @@ SceneSwitcher::SceneSwitcher(QWidget *parent) item->setData(Qt::UserRole, text); } - for (std::string p : switcher->threadPrioritiesNamesOrderdByPrio) { - ui->threadPriority->addItem(p.c_str()); + for (int i = 0; i < switcher->threadPriorities.size(); ++i) { + ui->threadPriority->addItem( + switcher->threadPriorities[i].name.c_str()); + ui->threadPriority->setItemData( + i, switcher->threadPriorities[i].description.c_str(), + Qt::ToolTipRole); + if (switcher->threadPriority == + switcher->threadPriorities[i].value) { + ui->threadPriority->setCurrentText( + switcher->threadPriorities[i].name.c_str()); + } } } @@ -885,6 +893,9 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) obs_data_set_int(obj, "priority7", switcher->functionNamesByPriority[7]); + obs_data_set_int(obj, "threadPriority", + switcher->threadPriority); + obs_data_set_obj(save_data, "advanced-scene-switcher", obj); obs_data_array_release(array); @@ -1246,12 +1257,19 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) array_obj, "restriction"); uint64_t time = obs_data_get_int(array_obj, "time"); + string mediaStr = MakeMediaSwitchName(source, scene, + transition, state, + restriction, time) + .toUtf8() + .constData(); + switcher->mediaSwitches.emplace_back( GetWeakSourceByName(scene), GetWeakSourceByName(source), GetWeakTransitionByName(transition), state, restriction, time, - (strcmp(scene, PREVIOUS_SCENE_NAME) == 0)); + (strcmp(scene, PREVIOUS_SCENE_NAME) == 0), + mediaStr); obs_data_release(array_obj); } @@ -1362,6 +1380,11 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) (DEFAULT_PRIORITY_7); } + obs_data_set_default_int(obj, "threadPriority", + QThread::NormalPriority); + switcher->threadPriority = + obs_data_get_int(obj, "threadPriority"); + obs_data_array_release(array); obs_data_array_release(screenRegionArray); obs_data_array_release(pauseScenesArray); @@ -1392,6 +1415,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) ********************************************************************************/ void SwitcherData::Thread() { + blog(LOG_INFO, "Advanced Scene Switcher started"); //to avoid scene duplication when rapidly switching scene collection this_thread::sleep_for(chrono::seconds(2)); @@ -1483,7 +1507,8 @@ void SwitcherData::Thread() switchScene(scene, transition, lock); } } -endLoop:; +endLoop: + blog(LOG_INFO, "Advanced Scene Switcher stopped"); } void switchScene(OBSWeakSource &scene, OBSWeakSource &transition, @@ -1542,19 +1567,23 @@ bool SwitcherData::sceneChangedDuringWait() void SwitcherData::Start() { - if (!th.joinable()) { + if (!(th && th->isRunning())) { stop = false; - switcher->th = thread([]() { switcher->Thread(); }); + switcher->th = new SwitcherThread(); + switcher->th->start((QThread::Priority)switcher->threadPriority); } } + void SwitcherData::Stop() { - if (th.joinable()) { + if (th && th->isRunning()) { switcher->stop = true; transitionCv.notify_one(); cv.notify_one(); - th.join(); + th->wait(); + delete th; + th = nullptr; } } diff --git a/src/general.cpp b/src/general.cpp index 3fe65e72..9bc85203 100644 --- a/src/general.cpp +++ b/src/general.cpp @@ -89,7 +89,7 @@ void SceneSwitcher::SetStopped() void SceneSwitcher::on_toggleStartButton_clicked() { - if (switcher->th.joinable()) + if (switcher->th && switcher->th->isRunning()) { switcher->Stop(); SetStopped(); diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index e5be0b4c..591e2348 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -120,6 +120,7 @@ public slots: void on_fileScenesList_currentRowChanged(int idx); void on_browseButton_3_clicked(); + void on_mediaSwitches_currentRowChanged(int idx); void on_mediaAdd_clicked(); void on_mediaRemove_clicked(); @@ -129,6 +130,7 @@ public slots: void on_priorityUp_clicked(); void on_priorityDown_clicked(); + void on_threadPriority_currentTextChanged(const QString &text); void updateScreenRegionCursorPos(); diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index b873f7ba..bcdca7b2 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -214,18 +213,20 @@ struct MediaSwitch { time_restriction restriction; bool matched; bool usePreviousScene; + std::string mediaSwitchStr; inline MediaSwitch(OBSWeakSource scene_, OBSWeakSource source_, OBSWeakSource transition_, obs_media_state state_, time_restriction restriction_, uint64_t time_, - bool usePreviousScene_) + bool usePreviousScene_, std::string mediaSwitchStr_) : scene(scene_), source(source_), transition(transition_), state(state_), restriction(restriction_), time(time_), - usePreviousScene(usePreviousScene_) + usePreviousScene(usePreviousScene_), + mediaSwitchStr(mediaSwitchStr_) { } }; @@ -252,11 +253,14 @@ struct TimeSwitch { typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch; +class SwitcherThread; + /******************************************************************************** * SwitcherData ********************************************************************************/ struct SwitcherData { - thread th; + SwitcherThread *th; + condition_variable cv; mutex m; bool transitionActive = false; @@ -311,18 +315,33 @@ struct SwitcherData { DEFAULT_PRIORITY_3, DEFAULT_PRIORITY_4, DEFAULT_PRIORITY_5, DEFAULT_PRIORITY_6, DEFAULT_PRIORITY_7}; - std::vector threadPrioritiesNamesOrderdByPrio{ - "Lowest", "Low", "Normal", "High", "Highest", "Time critical", + struct ThreadPrio { + std::string name; + std::string description; + uint32_t value; }; - std::map threadPriorities = { - {"Lowest", QThread::LowestPriority}, - {"Low", QThread::LowPriority}, - {"Normal", QThread::NormalPriority}, - {"High", QThread::HighPriority}, - {"Highest", QThread::HighestPriority}, - {"Time critical", QThread::TimeCriticalPriority}, + + std::vector threadPriorities{ + {"Idle", + "scheduled only when no other threads are running (lowest CPU load)", + QThread::IdlePriority}, + {"Lowest", "scheduled less often than LowPriority", + QThread::LowestPriority}, + {"Low", "scheduled less often than NormalPriority", + QThread::LowPriority}, + {"Normal", "the default priority of the operating system", + QThread::NormalPriority}, + {"High", "scheduled more often than NormalPriority", + QThread::HighPriority}, + {"Highest", "scheduled more often than HighPriority", + QThread::HighestPriority}, + {"Time critical", + "scheduled as often as possible (highest CPU load)", + QThread::TimeCriticalPriority}, }; + uint32_t threadPriority = QThread::NormalPriority; + void Thread(); void Start(); void Stop(); @@ -442,7 +461,8 @@ struct SwitcherData { for (size_t i = 0; i < timeSwitches.size(); i++) { TimeSwitch &s = timeSwitches[i]; - if (!WeakSourceValid(s.scene) || + if ((!s.usePreviousScene && + !WeakSourceValid(s.scene)) || !WeakSourceValid(s.transition)) timeSwitches.erase(timeSwitches.begin() + i--); } @@ -452,6 +472,23 @@ struct SwitcherData { !WeakSourceValid(idleData.transition)) { idleData.idleEnable = false; } + + for (size_t i = 0; i < mediaSwitches.size(); i++) { + MediaSwitch &s = mediaSwitches[i]; + if ((!s.usePreviousScene && + !WeakSourceValid(s.scene)) || + !WeakSourceValid(s.source) || + !WeakSourceValid(s.transition)) + mediaSwitches.erase(mediaSwitches.begin() + + i--); + } } inline ~SwitcherData() { Stop(); } }; + +extern SwitcherData *switcher; +class SwitcherThread : public QThread { +public: + explicit SwitcherThread(){}; + void run() { switcher->Thread(); }; +}; diff --git a/src/hotkey.cpp b/src/hotkey.cpp index 924eedbb..f95b5773 100644 --- a/src/hotkey.cpp +++ b/src/hotkey.cpp @@ -8,7 +8,7 @@ void startHotkeyFunc(void* data, obs_hotkey_id id, obs_hotkey_t* hotkey, bool pr if (pressed) { - if (!switcher->th.joinable()) + if (!(switcher->th && switcher->th->isRunning())) switcher->Start(); } @@ -43,7 +43,7 @@ void stopHotkeyFunc(void* data, obs_hotkey_id id, obs_hotkey_t* hotkey, bool pre if (pressed) { - if (switcher->th.joinable()) + if (switcher->th && switcher->th->isRunning()) switcher->Stop(); } @@ -78,7 +78,7 @@ void startStopToggleHotkeyFunc(void* data, obs_hotkey_id id, obs_hotkey_t* hotke if (pressed) { - if (switcher->th.joinable()) + if (switcher->th && switcher->th->isRunning()) switcher->Stop(); else switcher->Start(); diff --git a/src/media-switch.cpp b/src/media-switch.cpp index 07dbb3a0..92e8767a 100644 --- a/src/media-switch.cpp +++ b/src/media-switch.cpp @@ -1,6 +1,37 @@ #include #include "headers/advanced-scene-switcher.hpp" +void SceneSwitcher::on_mediaSwitches_currentRowChanged(int idx) +{ + if (loading) + return; + if (idx == -1) + return; + + QListWidgetItem *item = ui->mediaSwitches->item(idx); + + QString mediaSceneStr = item->data(Qt::UserRole).toString(); + + lock_guard lock(switcher->m); + for (auto &s : switcher->mediaSwitches) { + if (mediaSceneStr.compare(s.mediaSwitchStr.c_str()) == 0) { + QString sceneName = GetWeakSourceName(s.scene).c_str(); + QString sourceName = + GetWeakSourceName(s.source).c_str(); + QString transitionName = + GetWeakSourceName(s.transition).c_str(); + ui->mediaScenes->setCurrentText(sceneName); + ui->mediaSources->setCurrentText(sourceName); + ui->mediaTransitions->setCurrentText(transitionName); + ui->mediaStates->setCurrentIndex(s.state); + ui->mediaTimeRestrictions->setCurrentIndex( + s.restriction); + ui->mediaTime->setValue(s.time); + break; + } + } +} + void SceneSwitcher::on_mediaAdd_clicked() { QString sourceName = ui->mediaSources->currentText(); @@ -32,7 +63,8 @@ void SceneSwitcher::on_mediaAdd_clicked() lock_guard lock(switcher->m); switcher->mediaSwitches.emplace_back( scene, source, transition, state, restriction, time, - (sceneName == QString(PREVIOUS_SCENE_NAME))); + (sceneName == QString(PREVIOUS_SCENE_NAME)), + switchText.toUtf8().constData()); } void SceneSwitcher::on_mediaRemove_clicked() @@ -41,17 +73,23 @@ void SceneSwitcher::on_mediaRemove_clicked() if (!item) return; - int idx = ui->mediaSwitches->currentRow(); - if (idx == -1) - return; - + string mediaStr = + item->data(Qt::UserRole).toString().toUtf8().constData(); { lock_guard lock(switcher->m); - auto &switches = switcher->mediaSwitches; - switches.erase(switches.begin() + idx); + + for (auto it = switches.begin(); it != switches.end(); ++it) { + auto &s = *it; + + if (s.mediaSwitchStr == mediaStr) { + switches.erase(it); + break; + } + } } - qDeleteAll(ui->mediaSwitches->selectedItems()); + + delete item; } void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene, @@ -86,5 +124,6 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene, transition = mediaSwitch.transition; } mediaSwitch.matched = matched; + obs_source_release(source); } } diff --git a/src/priority.cpp b/src/priority.cpp index 22019b9c..60f5e30e 100644 --- a/src/priority.cpp +++ b/src/priority.cpp @@ -1,44 +1,68 @@ #include #include "headers/advanced-scene-switcher.hpp" +void SceneSwitcher::on_threadPriority_currentTextChanged(const QString &text) +{ + if (loading || ui->threadPriority->count() != switcher->threadPriorities.size()) + return; + + lock_guard lock(switcher->m); + + for (auto p : switcher->threadPriorities) { + if (p.name == text.toUtf8() + .constData()) { + switcher->threadPriority = p.value; + break; + } + } +} + void SceneSwitcher::on_priorityUp_clicked() { int currentIndex = ui->priorityList->currentRow(); - if (currentIndex != -1 && currentIndex != 0) - { - ui->priorityList->insertItem(currentIndex - 1 ,ui->priorityList->takeItem(currentIndex)); - ui->priorityList->setCurrentRow(currentIndex -1); + if (currentIndex != -1 && currentIndex != 0) { + ui->priorityList->insertItem( + currentIndex - 1, + ui->priorityList->takeItem(currentIndex)); + ui->priorityList->setCurrentRow(currentIndex - 1); lock_guard lock(switcher->m); - iter_swap(switcher->functionNamesByPriority.begin() + currentIndex, switcher->functionNamesByPriority.begin() + currentIndex - 1); + iter_swap(switcher->functionNamesByPriority.begin() + + currentIndex, + switcher->functionNamesByPriority.begin() + + currentIndex - 1); } } void SceneSwitcher::on_priorityDown_clicked() { int currentIndex = ui->priorityList->currentRow(); - if (currentIndex != -1 && currentIndex != ui->priorityList->count() - 1) - { - ui->priorityList->insertItem(currentIndex + 1, ui->priorityList->takeItem(currentIndex)); + if (currentIndex != -1 && + currentIndex != ui->priorityList->count() - 1) { + ui->priorityList->insertItem( + currentIndex + 1, + ui->priorityList->takeItem(currentIndex)); ui->priorityList->setCurrentRow(currentIndex + 1); lock_guard lock(switcher->m); - iter_swap(switcher->functionNamesByPriority.begin() + currentIndex, switcher->functionNamesByPriority.begin() + currentIndex + 1); + iter_swap(switcher->functionNamesByPriority.begin() + + currentIndex, + switcher->functionNamesByPriority.begin() + + currentIndex + 1); } } bool SwitcherData::prioFuncsValid() { - auto it = std::unique(functionNamesByPriority.begin(), functionNamesByPriority.end()); + auto it = std::unique(functionNamesByPriority.begin(), + functionNamesByPriority.end()); bool wasUnique = (it == functionNamesByPriority.end()); if (!wasUnique) return false; - for (int p : functionNamesByPriority) - { + for (int p : functionNamesByPriority) { if (p < 0 || p > 7) return false; } return true; } -