Compare commits

...

39 Commits
1.6.1 ... 1.7

Author SHA1 Message Date
WarmUpTill
8d023ee385 Add audio tab (#36)
* add volumemeter widget

* add audio tab UI

* add checkAudioSwitch functionality

* use constexpr instead of preprocessing
2020-09-21 00:52:42 +02:00
WarmUpTill
3fd3801e56 load libcurl at runtime (#33)
* load libcurl at runtime

* add warning to file tab if curl cant be loaded
2020-09-13 09:13:09 +02:00
WarmUpTill
3ec1e99f4b fix MacOS CI issues due to QT dependency (#35)
* fix MacOS CI issues due to QT deps

* adjust to new name of deps archive

* use MACOS_DEPS_VERSION instead of OSX_DEPS_VERSION

* disable python dependency during build
2020-09-13 01:30:08 +02:00
WarmUpTill
21a788b48b Improve time tab (#34)
* add support for weekdays and allow reordering on time tab

* add option to use time relative to streaming/recording start on time tab
2020-09-13 00:03:38 +02:00
WarmUpTill
a5c8d776f3 only try auto start streaming/recording once on scene chagne 2020-08-24 20:55:54 +02:00
WarmUpTill
95261ce9bb add first draft of auto start of recording / streaming 2020-08-16 19:48:54 +02:00
WarmUpTill
255d10e603 fix auto fill not wokring for modified entries on time tab 2020-08-09 20:48:49 +02:00
WarmUpTill
ab3e93ab84 add fallback to old format for sequence import 2020-08-02 20:22:13 +02:00
WarmUpTill
3b5f88c14f do not sort sequence and region on add 2020-08-02 19:38:32 +02:00
WarmUpTill
2f33b6bdc3 fix data loss for sequence delay 2020-08-02 19:37:37 +02:00
WarmUpTill
3c1e240722 add option to set startup behavior 2020-08-02 18:45:44 +02:00
WarmUpTill
91831a1075 fix autoStop not being persisted 2020-08-02 18:00:50 +02:00
WarmUpTill
ccc824abc1 fix loading of old delay values 2020-08-02 17:46:36 +02:00
WarmUpTill
6f39d4648b add included 'thread' and 'QAction' for Linux and macOS build 2020-08-01 20:34:12 +02:00
WarmUpTill
74cb400426 Merge branch 'master' of https://github.com/WarmUpTill/SceneSwitcher 2020-08-01 20:15:49 +02:00
WarmUpTill
910b1dde99 set sceneRoundTripDelayUnits 2020-08-01 16:19:57 +02:00
WarmUpTill
e462d7f420 fix sequence not stopping when manually switching scene 2020-08-01 16:11:54 +02:00
WarmUpTill
0cdd8bf1d0 move UI init of tabs to respective source files 2020-08-01 11:14:54 +02:00
WarmUpTill
f8ad8c31be scene sequence adjustments
use double internally instead of two ints
rework save / load to file to reuse existing functionality
add option to select between seconds / minutes / hours
2020-08-01 10:12:32 +02:00
WarmUpTill
93c889c215 fix MacOS CI issue 2020-07-31 22:18:52 +02:00
WarmUpTill
f8b505fa4e silence warnings 2020-07-31 22:05:55 +02:00
WarmUpTill
1516263d2a save and load tab order 2020-07-31 21:58:17 +02:00
WarmUpTill
79f5ec340d allow reordering of tabs and silence a few UI warnings 2020-07-29 20:23:03 +02:00
WarmUpTill
cd54cc17c0 fix switch priority falling back to default values on startup 2020-07-09 19:57:55 +02:00
WarmUpTill
098159dc0f increase window size on mac
currently the general tab will be compressed to an almost unusable level due to the differences in Windows and Macintosh styles
2020-06-30 20:11:44 +02:00
WarmUpTill
a4418996f4 disable mediaTime selection if restriction is set to none 2020-06-19 19:52:43 +02:00
WarmUpTill
340cb559ee add option to override transitionOverrides 2020-06-17 22:16:47 +02:00
WarmUpTill
eeefd8279f allow reordering of file/media/sequence/region 2020-06-16 20:25:25 +02:00
WarmUpTill
e62dc91cd7 fix linux/mac build error 2020-06-15 21:46:58 +02:00
WarmUpTill
536041abd2 Merge branch 'master' of https://github.com/WarmUpTill/SceneSwitcher 2020-06-15 21:20:02 +02:00
WarmUpTill
d3ab9e01c5 add support for remote file using libcurl 2020-06-15 21:19:34 +02:00
WarmUpTill
2113025877 undo switch to Visual Studio 15 2017
windows-latest image only supports Visual Studio 16 2019
2020-06-15 18:48:32 +02:00
WarmUpTill
6aaffeb5a4 use CMAKE_GENERATOR "Visual Studio 15 2017"
the 2017 version of the Microsoft Visual C++ Redistributable for Visual Studio is more widespread than the 2019 one and thus is less likely to require users to install the Redistributable manually
2020-06-15 18:32:01 +02:00
WarmUpTill
2a92df4f3c add draft of remote file support 2020-06-14 20:35:02 +02:00
WarmUpTill
560044bbbf implement export/import of settings 2020-06-14 15:22:34 +02:00
WarmUpTill
4bd8cdf5a0 move auto start/stop to gernal.cpp 2020-06-14 14:17:03 +02:00
WarmUpTill
c17cbb66d9 write to OBS log if verbose logging is enabled 2020-06-14 14:14:00 +02:00
WarmUpTill
8d23dd46b0 add export/import button and verbose log checkbox 2020-06-14 13:57:11 +02:00
WarmUpTill
bf70bd7c60 move saving and loading to separate files 2020-06-13 21:56:26 +02:00
27 changed files with 5567 additions and 1700 deletions

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: [macos-latest]
env:
QT_VERSION: 5.14.1
OSX_DEPS_VERSION: '2020-04-07'
MACOS_DEPS_VERSION: '2020-08-30'
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -35,20 +35,29 @@ jobs:
shell: bash
run: |
cd UI/frontend-plugins/${{ env.PLUGIN_NAME }}/CI/macos
brew update
brew bundle
cd -
- name: 'Install prerequisite: Pre-built dependencies'
if: steps.deps-cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.OSX_DEPS_VERSION }}/osx-deps-${{ env.OSX_DEPS_VERSION }}.tar.gz
tar -xf ./osx-deps-${{ env.OSX_DEPS_VERSION }}.tar.gz -C "/tmp"
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz
tar -xf ./macos-deps-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
- name: 'Install prerequisite: Pre-built dependency Qt'
if: steps.deps-qt-cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -L -O https://github.com/obsproject/obs-deps/releases/download/${{ env.MACOS_DEPS_VERSION }}/macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz
tar -xf ./macos-qt-${{ env.QT_VERSION }}-${{ env.MACOS_DEPS_VERSION }}.tar.gz -C "/tmp"
xattr -r -d com.apple.quarantine /tmp/obsdeps
- name: Configure
shell: bash
run: |
echo "add_subdirectory(${{ env.PLUGIN_NAME }})" >> UI/frontend-plugins/CMakeLists.txt
mkdir ./build
cd ./build
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DDepsPath="/tmp/obsdeps" -DQTDIR="/usr/local/Cellar/qt/${{ env.QT_VERSION }}" ..
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DDISABLE_PYTHON=ON -DDepsPath="/tmp/obsdeps" -DQTDIR="/tmp/obsdeps" ..
cd -
- name: Build
shell: bash

View File

@@ -2,5 +2,4 @@ brew "jack"
brew "speexdsp"
brew "cmake"
brew "freetype"
brew "fdk-aac"
brew "https://gist.githubusercontent.com/DDRBoxman/9c7a2b08933166f4b61ed9a44b242609/raw/ef4de6c587c6bd7f50210eccd5bd51ff08e6de13/qt.rb"
brew "fdk-aac"

View File

@@ -13,6 +13,7 @@ if(BUILD_OUT_OF_TREE)
find_package(LibObs REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Libcurl REQUIRED)
find_path(LIBOBS_FRONTEND_INCLUDE_DIR HINTS ${LIBOBS_INCLUDE_DIRS})
find_file(LIBOBS_FRONTEND_API_LIB NAMES libobs-frontend-api.* HINTS ${LIBOBS_LIB} )
@@ -34,6 +35,7 @@ if(BUILD_OUT_OF_TREE)
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/utility.hpp
src/headers/curl-helper.hpp
)
set(advanced-scene-switcher_SOURCES
@@ -54,6 +56,7 @@ if(BUILD_OUT_OF_TREE)
src/pause-switch.cpp
src/random.cpp
src/time-switch.cpp
src/curl-helper.cpp
)
set(advanced-scene-switcher_UI
@@ -96,7 +99,8 @@ if(BUILD_OUT_OF_TREE)
"${LIBOBS_INCLUDE_DIRS}"
"${LIBOBS_FRONTEND_INCLUDE_DIR}"
${Qt5Core_INCLUDES}
${Qt5Widgets_INCLUDES})
${Qt5Widgets_INCLUDES}
${LIBCURL_INCLUDE_DIRS})
target_link_libraries(advanced-scene-switcher
${advanced-scene-switcher_PLATFORM_LIBS}
@@ -164,6 +168,8 @@ else ()
src/headers/advanced-scene-switcher.hpp
src/headers/switcher-data-structs.hpp
src/headers/utility.hpp
src/headers/curl-helper.hpp
src/headers/volume-control.hpp
)
set(advanced-scene-switcher_SOURCES
@@ -184,6 +190,9 @@ else ()
src/pause-switch.cpp
src/random.cpp
src/time-switch.cpp
src/audio-switch.cpp
src/curl-helper.cpp
src/volume-control.cpp
)
set(advanced-scene-switcher_UI

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

327
src/audio-switch.cpp Normal file
View File

@@ -0,0 +1,327 @@
#include "headers/advanced-scene-switcher.hpp"
#include "headers/volume-control.hpp"
void SceneSwitcher::on_audioSwitches_currentRowChanged(int idx)
{
if (loading)
return;
if (idx == -1)
return;
QListWidgetItem *item = ui->audioSwitches->item(idx);
QString audioScenestr = item->data(Qt::UserRole).toString();
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->audioSwitches) {
if (audioScenestr.compare(s.audioSwitchStr.c_str()) == 0) {
QString sceneName = GetWeakSourceName(s.scene).c_str();
QString audioSrouceName =
GetWeakSourceName(s.audioSource).c_str();
QString transitionName =
GetWeakSourceName(s.transition).c_str();
ui->audioScenes->setCurrentText(sceneName);
ui->audioTransitions->setCurrentText(transitionName);
ui->audioSources->setCurrentText(audioSrouceName);
ui->audioVolumeThreshold->setValue(s.volumeThreshold);
volMeter->GetSlider()->setValue(s.volumeThreshold);
break;
}
}
}
void SceneSwitcher::on_audioSources_currentTextChanged(const QString &text)
{
SetAudioVolumeMeter(text);
}
void SceneSwitcher::SetAudioVolumeMeter(const QString &name)
{
obs_source_t *soruce =
obs_get_source_by_name(name.toUtf8().constData());
if (!soruce) {
return;
}
if (volMeter) {
ui->audioControlLayout->removeWidget(volMeter);
delete volMeter;
}
volMeter = new VolControl(soruce);
ui->audioControlLayout->addWidget(volMeter);
obs_source_release(soruce);
QWidget::connect(volMeter->GetSlider(), SIGNAL(valueChanged(int)),
ui->audioVolumeThreshold, SLOT(setValue(int)));
QWidget::connect(ui->audioVolumeThreshold, SIGNAL(valueChanged(int)),
volMeter->GetSlider(), SLOT(setValue(int)));
}
int SceneSwitcher::audioFindByData(const QString &source, const int &volume)
{
QRegExp rx(MakeAudioSwitchName(QStringLiteral(".*"),
QStringLiteral(".*"), source, volume));
int count = ui->audioSwitches->count();
for (int i = 0; i < count; i++) {
QListWidgetItem *item = ui->audioSwitches->item(i);
QString str = item->data(Qt::UserRole).toString();
if (rx.exactMatch(str))
return i;
}
return -1;
}
void SceneSwitcher::on_audioAdd_clicked()
{
QString sceneName = ui->audioScenes->currentText();
QString transitionName = ui->audioTransitions->currentText();
QString audioSourceName = ui->audioSources->currentText();
int vol = ui->audioVolumeThreshold->value();
if (sceneName.isEmpty() || audioSourceName.isEmpty())
return;
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource audioSource = GetWeakSourceByQString(audioSourceName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text = MakeAudioSwitchName(sceneName, transitionName,
audioSourceName, vol);
QVariant v = QVariant::fromValue(text);
int idx = audioFindByData(audioSourceName, vol);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->audioSwitches.emplace_back(
source, transition, audioSource, vol,
(sceneName == QString(previous_scene_name)),
text.toUtf8().constData());
QListWidgetItem *item =
new QListWidgetItem(text, ui->audioSwitches);
item->setData(Qt::UserRole, v);
} else {
QListWidgetItem *item = ui->audioSwitches->item(idx);
item->setText(text);
item->setData(Qt::UserRole, v);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->audioSwitches) {
if (s.audioSource == audioSource &&
s.volumeThreshold == vol) {
s.scene = source;
s.transition = transition;
s.usePreviousScene =
(sceneName ==
QString(previous_scene_name));
s.audioSwitchStr =
text.toUtf8().constData();
break;
}
}
}
}
}
void SceneSwitcher::on_audioRemove_clicked()
{
QListWidgetItem *item = ui->audioSwitches->currentItem();
if (!item)
return;
std::string text =
item->data(Qt::UserRole).toString().toUtf8().constData();
{
std::lock_guard<std::mutex> lock(switcher->m);
auto &switches = switcher->audioSwitches;
for (auto it = switches.begin(); it != switches.end(); ++it) {
auto &s = *it;
if (s.audioSwitchStr == text) {
switches.erase(it);
break;
}
}
}
delete item;
}
void SceneSwitcher::on_audioUp_clicked()
{
int index = ui->audioSwitches->currentRow();
if (index != -1 && index != 0) {
ui->audioSwitches->insertItem(
index - 1, ui->audioSwitches->takeItem(index));
ui->audioSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->audioSwitches.begin() + index,
switcher->audioSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_audioDown_clicked()
{
int index = ui->audioSwitches->currentRow();
if (index != -1 && index != ui->audioSwitches->count() - 1) {
ui->audioSwitches->insertItem(
index + 1, ui->audioSwitches->takeItem(index));
ui->audioSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->audioSwitches.begin() + index,
switcher->audioSwitches.begin() + index + 1);
}
}
void SwitcherData::checkAudioSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
if (audioSwitches.size() == 0)
return;
for (AudioSwitch &s : audioSwitches) {
obs_source_t *as = obs_weak_source_get_source(s.audioSource);
const char *name = obs_source_get_name(as);
bool audioActive = obs_source_active(as);
obs_source_release(as);
// peak will have a value from -60 db to 0 db
bool volumeThresholdreached = (s.peak + 60) * 1.7 >
s.volumeThreshold;
if (volumeThresholdreached && audioActive) {
scene = (s.usePreviousScene) ? previousScene : s.scene;
transition = s.transition;
match = true;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher audio match for %s",
name);
break;
}
}
}
void SwitcherData::saveAudioSwitches(obs_data_t *obj)
{
obs_data_array_t *audioArray = obs_data_array_create();
for (AudioSwitch &s : switcher->audioSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
obs_source_t *audioSrouce =
obs_weak_source_get_source(s.audioSource);
if ((s.usePreviousScene || sceneSource) && transition) {
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
const char *audioSourceName =
obs_source_get_name(audioSrouce);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? previous_scene_name
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "audioSource",
audioSourceName);
obs_data_set_int(array_obj, "volume",
s.volumeThreshold);
obs_data_array_push_back(audioArray, array_obj);
}
obs_source_release(sceneSource);
obs_source_release(transition);
obs_source_release(audioSrouce);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "audioSwitches", audioArray);
obs_data_array_release(audioArray);
}
void SwitcherData::loadAudioSwitches(obs_data_t *obj)
{
switcher->audioSwitches.clear();
obs_data_array_t *audioArray = obs_data_get_array(obj, "audioSwitches");
size_t count = obs_data_array_count(audioArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(audioArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *audioSource =
obs_data_get_string(array_obj, "audioSource");
int vol = obs_data_get_int(array_obj, "volume");
std::string audioSwitchStr =
MakeAudioSwitchName(scene, transition, audioSource, vol)
.toUtf8()
.constData();
switcher->audioSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition),
GetWeakSourceByName(audioSource), vol,
(strcmp(scene, previous_scene_name) == 0),
audioSwitchStr);
obs_data_release(array_obj);
}
obs_data_array_release(audioArray);
}
void SceneSwitcher::setupAudioTab()
{
populateSceneSelection(ui->audioScenes, true);
populateTransitionSelection(ui->audioTransitions);
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
uint32_t flags = obs_source_get_output_flags(source);
if ((flags & OBS_SOURCE_AUDIO) != 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, ui->audioSources);
for (auto &s : switcher->audioSwitches) {
std::string sceneName = (s.usePreviousScene)
? previous_scene_name
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
std::string audioSourceName = GetWeakSourceName(s.audioSource);
QString listText = MakeAudioSwitchName(sceneName.c_str(),
transitionName.c_str(),
audioSourceName.c_str(),
s.volumeThreshold);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->audioSwitches);
item->setData(Qt::UserRole, listText);
}
}

65
src/curl-helper.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include <QDir>
#include <QFileInfo>
#include <curl/curl.h>
#include <obs.hpp>
#include "headers/curl-helper.hpp"
initFunction f_curl_init = nullptr;
setOptFunction f_curl_setopt = nullptr;
performFunction f_curl_perform = nullptr;
cleanupFunction f_curl_cleanup = nullptr;
QLibrary *loaded_curl_lib = nullptr;
bool resolveCurl()
{
f_curl_init = (initFunction)loaded_curl_lib->resolve("curl_easy_init");
f_curl_setopt = (setOptFunction)loaded_curl_lib->resolve("curl_easy_setopt");
f_curl_perform =
(performFunction)loaded_curl_lib->resolve("curl_easy_perform");
f_curl_cleanup =
(cleanupFunction)loaded_curl_lib->resolve("curl_easy_cleanup");
if (f_curl_init && f_curl_setopt && f_curl_perform && f_curl_cleanup) {
blog(LOG_INFO, "curl loaded successfully");
return true;
}
blog(LOG_INFO, "curl symbols not resolved");
return false;
}
bool loadCurl()
{
QStringList locations;
locations << QDir::currentPath();
#if defined(__linux__) || defined(__APPLE__)
locations << "/usr/lib";
locations << "/usr/local/lib";
locations << "/usr/lib/x86_64-linux-gnu";
locations << "/usr/local/opt/curl/lib";
#endif
for (QString path : locations) {
blog(LOG_INFO, "trying '%s'", path.toUtf8().constData());
QFileInfo libPath(QDir(path).absoluteFilePath(CURL_LIBRARY_NAME));
if (libPath.exists() && libPath.isFile()) {
QString libFilePath = libPath.absoluteFilePath();
blog(LOG_INFO, "found curl library at '%s'",
libFilePath.toUtf8().constData());
loaded_curl_lib = new QLibrary(libFilePath, nullptr);
if (resolveCurl())
return true;
else {
delete loaded_curl_lib;
loaded_curl_lib = nullptr;
}
}
}
blog(LOG_ERROR, "can't find the curl library");
return false;
}

View File

@@ -189,7 +189,92 @@ void SwitcherData::checkExeSwitch(bool &match, OBSWeakSource &scene,
scene = s.mScene;
transition = s.mTransition;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher exec match");
break;
}
}
}
void SwitcherData::saveExecutableSwitches(obs_data_t *obj)
{
obs_data_array_t *executableArray = obs_data_array_create();
for (ExecutableSceneSwitch &s : switcher->executableSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.mScene);
obs_source_t *transition =
obs_weak_source_get_source(s.mTransition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "exefile",
s.mExe.toUtf8());
obs_data_set_bool(array_obj, "infocus", s.mInFocus);
obs_data_array_push_back(executableArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "executableSwitches", executableArray);
obs_data_array_release(executableArray);
}
void SwitcherData::loadExecutableSwitches(obs_data_t *obj)
{
switcher->executableSwitches.clear();
obs_data_array_t *executableArray =
obs_data_get_array(obj, "executableSwitches");
size_t count = obs_data_array_count(executableArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(executableArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *exe = obs_data_get_string(array_obj, "exefile");
bool infocus = obs_data_get_bool(array_obj, "infocus");
switcher->executableSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), exe, infocus);
obs_data_release(array_obj);
}
obs_data_array_release(executableArray);
}
void SceneSwitcher::setupExecutableTab()
{
populateSceneSelection(ui->executableScenes, false);
populateTransitionSelection(ui->executableTransitions);
QStringList processes;
GetProcessList(processes);
for (QString &process : processes)
ui->executable->addItem(process);
for (auto &s : switcher->executableSwitches) {
std::string sceneName = GetWeakSourceName(s.mScene);
std::string transitionName = GetWeakSourceName(s.mTransition);
QString text = MakeSwitchNameExecutable(sceneName.c_str(),
s.mExe,
transitionName.c_str(),
s.mInFocus);
QListWidgetItem *item =
new QListWidgetItem(text, ui->executables);
item->setData(Qt::UserRole, s.mExe);
}
}

View File

@@ -2,7 +2,13 @@
#include <QTextStream>
#include <QDateTime>
#include <obs.hpp>
#include <curl/curl.h>
#include "headers/advanced-scene-switcher.hpp"
#include "headers/curl-helper.hpp"
#define LOCAL_FILE_IDX 0
#define REMOTE_FILE_IDX 1
void SceneSwitcher::on_browseButton_clicked()
{
@@ -110,54 +116,110 @@ void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
}
}
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
void *userp)
{
((std::string *)userp)->append((char *)contents, size * nmemb);
return size * nmemb;
}
std::string getRemoteData(std::string &url)
{
std::string readBuffer;
if (switcher->curl && f_curl_setopt && f_curl_perform) {
f_curl_setopt(switcher->curl, CURLOPT_URL, url.c_str());
f_curl_setopt(switcher->curl, CURLOPT_WRITEFUNCTION, WriteCallback);
f_curl_setopt(switcher->curl, CURLOPT_WRITEDATA, &readBuffer);
f_curl_perform(switcher->curl);
}
return readBuffer;
}
bool compareIgnoringLineEnding(QString &s1, QString &s2)
{
/*
Im using QTextStream here so the conversion between different lineendings is done by QT.
QT itself uses only the linefeed internally so the input by the user is always using that,
but the files selected by the user might use different line endings.
If you are reading this and know of a cleaner way to do this, please let me know :)
*/
QTextStream s1stream(&s1);
QTextStream s2stream(&s2);
while (!s1stream.atEnd() && !s2stream.atEnd()) {
QString s1s = s1stream.readLine();
QString s2s = s2stream.readLine();
if (QString::compare(s1s, s2s, Qt::CaseSensitive) != 0) {
return false;
}
}
return true;
}
bool checkRemoteFileContent(FileSwitch &s)
{
std::string data = getRemoteData(s.file);
bool ret = false;
if (s.useRegex) {
ret = QString::fromStdString(data).contains(
QRegularExpression(QString::fromStdString(s.text)));
return ret;
}
QString t = QString::fromStdString(s.text);
QString d = QString::fromStdString(data);
return compareIgnoringLineEnding(t, d);
}
bool checkLocalFileContent(FileSwitch &s)
{
bool equal = false;
QString t = QString::fromStdString(s.text);
QFile file(QString::fromStdString(s.file));
if (!file.open(QIODevice::ReadOnly))
return false;
if (s.useTime) {
QDateTime newLastMod = QFileInfo(file).lastModified();
if (s.lastMod == newLastMod)
return false;
s.lastMod = newLastMod;
}
if (s.useRegex) {
QTextStream in(&file);
QRegExp rx(t);
equal = rx.exactMatch(in.readAll());
} else {
QTextStream in(&file);
QString filedata = in.readAll();
equal = compareIgnoringLineEnding(filedata, t);
}
file.close();
return equal;
}
void SwitcherData::checkFileContent(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
for (FileSwitch &s : fileSwitches) {
bool equal = false;
QString t = QString::fromStdString(s.text);
QFile file(QString::fromStdString(s.file));
if (!file.open(QIODevice::ReadOnly))
continue;
if (s.useTime) {
QDateTime newLastMod = QFileInfo(file).lastModified();
if (s.lastMod == newLastMod)
continue;
s.lastMod = newLastMod;
}
if (s.useRegex) {
QTextStream in(&file);
QRegExp rx(t);
equal = rx.exactMatch(in.readAll());
if (s.remote) {
equal = checkRemoteFileContent(s);
} else {
/*Im using QTextStream here so the conversion between different lineendings is done by QT.
*QT itself uses only the linefeed internally so the input by the user is always using that,
*but the files selected by the user might use different line endings.
*If you are reading this and know of a cleaner way to do this, please let me know :)
*/
QTextStream in(&file);
QTextStream text(&t);
while (!in.atEnd() && !text.atEnd()) {
QString fileLine = in.readLine();
QString textLine = text.readLine();
if (QString::compare(fileLine, textLine,
Qt::CaseSensitive) != 0) {
equal = false;
break;
} else {
equal = true;
}
}
equal = checkLocalFileContent(s);
}
file.close();
if (equal) {
scene = s.scene;
transition = s.transition;
match = true;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher file match");
break;
}
}
@@ -172,12 +234,29 @@ void SceneSwitcher::on_browseButton_3_clicked()
ui->filePathLineEdit->setText(path);
}
void SceneSwitcher::on_fileType_currentIndexChanged(int idx)
{
if (idx == -1)
return;
if (idx == LOCAL_FILE_IDX) {
ui->browseButton_3->setDisabled(false);
ui->fileContentTimeCheckBox->setDisabled(false);
ui->remoteFileWarningLabel->hide();
}
if (idx == REMOTE_FILE_IDX) {
ui->browseButton_3->setDisabled(true);
ui->fileContentTimeCheckBox->setDisabled(true);
ui->remoteFileWarningLabel->show();
}
}
void SceneSwitcher::on_fileAdd_clicked()
{
QString sceneName = ui->fileScenes->currentText();
QString transitionName = ui->fileTransitions->currentText();
QString fileName = ui->filePathLineEdit->text();
QString text = ui->fileTextEdit->toPlainText();
bool remote = (ui->fileType->currentIndex() == REMOTE_FILE_IDX);
bool useRegex = ui->fileContentRegExCheckBox->isChecked();
bool useTime = ui->fileContentTimeCheckBox->isChecked();
@@ -199,8 +278,8 @@ void SceneSwitcher::on_fileAdd_clicked()
std::lock_guard<std::mutex> lock(switcher->m);
switcher->fileSwitches.emplace_back(source, transition,
fileName.toUtf8().constData(),
text.toUtf8().constData(), useRegex,
useTime);
text.toUtf8().constData(), remote,
useRegex, useTime);
}
void SceneSwitcher::on_fileRemove_clicked()
@@ -242,6 +321,155 @@ void SceneSwitcher::on_fileScenesList_currentRowChanged(int idx)
ui->fileTransitions->setCurrentText(transitionName.c_str());
ui->fileTextEdit->setPlainText(s.text.c_str());
ui->filePathLineEdit->setText(s.file.c_str());
if (s.remote)
ui->fileType->setCurrentIndex(REMOTE_FILE_IDX);
else
ui->fileType->setCurrentIndex(LOCAL_FILE_IDX);
ui->fileContentRegExCheckBox->setChecked(s.useRegex);
ui->fileContentTimeCheckBox->setChecked(s.useTime);
}
void SceneSwitcher::on_fileUp_clicked()
{
int index = ui->fileScenesList->currentRow();
if (index != -1 && index != 0) {
ui->fileScenesList->insertItem(
index - 1, ui->fileScenesList->takeItem(index));
ui->fileScenesList->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->fileSwitches.begin() + index,
switcher->fileSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_fileDown_clicked()
{
int index = ui->fileScenesList->currentRow();
if (index != -1 && index != ui->fileScenesList->count() - 1) {
ui->fileScenesList->insertItem(
index + 1, ui->fileScenesList->takeItem(index));
ui->fileScenesList->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->fileSwitches.begin() + index,
switcher->fileSwitches.begin() + index + 1);
}
}
void SwitcherData::saveFileSwitches(obs_data_t *obj)
{
obs_data_array_t *fileArray = obs_data_array_create();
for (FileSwitch &s : switcher->fileSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "file", s.file.c_str());
obs_data_set_string(array_obj, "text", s.text.c_str());
obs_data_set_bool(array_obj, "remote", s.remote);
obs_data_set_bool(array_obj, "useRegex", s.useRegex);
obs_data_set_bool(array_obj, "useTime", s.useTime);
obs_data_array_push_back(fileArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "fileSwitches", fileArray);
obs_data_array_release(fileArray);
obs_data_set_bool(obj, "readEnabled", switcher->fileIO.readEnabled);
obs_data_set_string(obj, "readPath", switcher->fileIO.readPath.c_str());
obs_data_set_bool(obj, "writeEnabled", switcher->fileIO.writeEnabled);
obs_data_set_string(obj, "writePath",
switcher->fileIO.writePath.c_str());
}
void SwitcherData::loadFileSwitches(obs_data_t *obj)
{
switcher->fileSwitches.clear();
obs_data_array_t *fileArray = obs_data_get_array(obj, "fileSwitches");
size_t count = obs_data_array_count(fileArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(fileArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *file = obs_data_get_string(array_obj, "file");
const char *text = obs_data_get_string(array_obj, "text");
bool remote = obs_data_get_bool(array_obj, "remote");
bool useRegex = obs_data_get_bool(array_obj, "useRegex");
bool useTime = obs_data_get_bool(array_obj, "useTime");
switcher->fileSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), file, text, remote,
useRegex, useTime);
obs_data_release(array_obj);
}
obs_data_array_release(fileArray);
obs_data_set_default_bool(obj, "readEnabled", false);
switcher->fileIO.readEnabled = obs_data_get_bool(obj, "readEnabled");
switcher->fileIO.readPath = obs_data_get_string(obj, "readPath");
obs_data_set_default_bool(obj, "writeEnabled", false);
switcher->fileIO.writeEnabled = obs_data_get_bool(obj, "writeEnabled");
switcher->fileIO.writePath = obs_data_get_string(obj, "writePath");
}
void SceneSwitcher::setupFileTab()
{
populateSceneSelection(ui->fileScenes, false);
populateTransitionSelection(ui->fileTransitions);
ui->fileType->addItem("local");
ui->fileType->addItem("remote");
ui->remoteFileWarningLabel->setText(
"Note that the scene switcher will try to access the remote location every " +
QString::number(switcher->interval) + "ms");
if (switcher->curl)
ui->libcurlWarning->setVisible(false);
for (auto &s : switcher->fileSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeFileSwitchName(
sceneName.c_str(), transitionName.c_str(),
s.file.c_str(), s.text.c_str(), s.useRegex, s.useTime);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->fileScenesList);
item->setData(Qt::UserRole, listText);
}
ui->readPathLineEdit->setText(
QString::fromStdString(switcher->fileIO.readPath.c_str()));
ui->readFileCheckBox->setChecked(switcher->fileIO.readEnabled);
ui->writePathLineEdit->setText(
QString::fromStdString(switcher->fileIO.writePath.c_str()));
if (ui->readFileCheckBox->checkState()) {
ui->browseButton_2->setDisabled(false);
ui->readPathLineEdit->setDisabled(false);
} else {
ui->browseButton_2->setDisabled(true);
ui->readPathLineEdit->setDisabled(true);
}
}

View File

@@ -1,20 +1,15 @@
#include "headers/advanced-scene-switcher.hpp"
#include <obs-module.h>
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include "headers/advanced-scene-switcher.hpp"
void SceneSwitcher::on_close_clicked()
{
done(0);
}
void SceneSwitcher::on_startAtLaunch_toggled(bool value)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->startAtLaunch = value;
}
void SceneSwitcher::UpdateNonMatchingScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
@@ -57,6 +52,15 @@ void SceneSwitcher::on_noMatchRandomSwitch_clicked()
ui->noMatchSwitchScene->setEnabled(false);
}
void SceneSwitcher::on_startupBehavior_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->startupBehavior = (StartupBehavior)index;
}
void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text)
{
if (loading)
@@ -102,3 +106,596 @@ void SceneSwitcher::closeEvent(QCloseEvent *)
{
obs_frontend_save();
}
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
UpdateAutoStopScene(text);
}
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
ui->autoStopScenes->setDisabled(true);
switcher->autoStopEnable = false;
} else {
ui->autoStopScenes->setDisabled(false);
switcher->autoStopEnable = true;
if (!switcher->autoStopScene)
UpdateAutoStopScene(ui->autoStopScenes->currentText());
}
}
void SceneSwitcher::UpdateAutoStopScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
switcher->autoStopScene = ws;
obs_weak_source_release(ws);
obs_source_release(scene);
}
void SwitcherData::autoStopStreamAndRecording()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
if (ws && autoStopScene == ws) {
if (obs_frontend_streaming_active())
obs_frontend_streaming_stop();
if (obs_frontend_recording_active())
obs_frontend_recording_stop();
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}
void SceneSwitcher::on_autoStartType_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->autoStartType = (AutoStartType)index;
}
void SceneSwitcher::on_autoStartScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
UpdateAutoStartScene(text);
}
void SceneSwitcher::on_autoStartSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
ui->autoStartScenes->setDisabled(true);
ui->autoStartType->setDisabled(true);
switcher->autoStartEnable = false;
} else {
ui->autoStartScenes->setDisabled(false);
ui->autoStartType->setDisabled(false);
switcher->autoStartEnable = true;
if (!switcher->autoStartScene)
UpdateAutoStartScene(
ui->autoStartScenes->currentText());
}
}
void SceneSwitcher::UpdateAutoStartScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
switcher->autoStartScene = ws;
obs_weak_source_release(ws);
obs_source_release(scene);
}
void SwitcherData::autoStartStreamRecording()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
if (autoStartedRecently)
return;
if (ws && autoStartScene == ws) {
if ((switcher->autoStartType == STREAMING ||
switcher->autoStartType == RECORINDGSTREAMING) &&
!obs_frontend_streaming_active())
obs_frontend_streaming_start();
if ((switcher->autoStartType == RECORDING ||
switcher->autoStartType == RECORINDGSTREAMING) &&
!obs_frontend_recording_active())
obs_frontend_recording_start();
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
autoStartedRecently = true;
}
void SceneSwitcher::on_verboseLogging_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
switcher->verbose = state;
}
void SceneSwitcher::on_exportSettings_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Export Advanced Scene Switcher settings to file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create();
switcher->saveWindowTitleSwitches(obj);
switcher->saveScreenRegionSwitches(obj);
switcher->savePauseSwitches(obj);
switcher->saveSceneRoundTripSwitches(obj);
switcher->saveSceneTransitions(obj);
switcher->saveIdleSwitches(obj);
switcher->saveExecutableSwitches(obj);
switcher->saveRandomSwitches(obj);
switcher->saveFileSwitches(obj);
switcher->saveMediaSwitches(obj);
switcher->saveTimeSwitches(obj);
switcher->saveAudioSwitches(obj);
switcher->saveGeneralSettings(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
void SceneSwitcher::on_importSettings_clicked()
{
std::lock_guard<std::mutex> lock(switcher->m);
QString directory = QFileDialog::getOpenFileName(
this,
tr("Import Advanced Scene Switcher settings from file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create_from_json_file(
file.fileName().toUtf8().constData());
if (!obj) {
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher failed to import settings");
Msgbox.exec();
return;
}
switcher->loadWindowTitleSwitches(obj);
switcher->loadScreenRegionSwitches(obj);
switcher->loadPauseSwitches(obj);
switcher->loadSceneRoundTripSwitches(obj);
switcher->loadSceneTransitions(obj);
switcher->loadIdleSwitches(obj);
switcher->loadExecutableSwitches(obj);
switcher->loadRandomSwitches(obj);
switcher->loadFileSwitches(obj);
switcher->loadMediaSwitches(obj);
switcher->loadTimeSwitches(obj);
switcher->loadAudioSwitches(obj);
switcher->loadGeneralSettings(obj);
obs_data_release(obj);
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher settings imported successfully");
Msgbox.exec();
close();
}
int findTabIndex(QTabBar *bar, int pos)
{
int at = -1;
QString tabName = "";
switch (pos) {
case 0:
tabName = "General";
break;
case 1:
tabName = "Transition";
break;
case 2:
tabName = "Pause";
break;
case 3:
tabName = "Title";
break;
case 4:
tabName = "Executable";
break;
case 5:
tabName = "Region";
break;
case 6:
tabName = "Media";
break;
case 7:
tabName = "File";
break;
case 8:
tabName = "Random";
break;
case 9:
tabName = "Time";
break;
case 10:
tabName = "Idle";
break;
case 11:
tabName = "Sequence";
break;
case 12:
tabName = "Audio";
break;
}
for (int i = 0; i < bar->count(); ++i) {
if (bar->tabText(i).compare(tabName) == 0) {
at = i;
break;
}
}
if (at == -1)
blog(LOG_INFO, "Advanced Scene Switcher failed to find tab %s",
tabName.toUtf8().constData());
return at;
}
void SceneSwitcher::setTabOrder()
{
QTabBar *bar = ui->tabWidget->tabBar();
for (int i = 0; i < bar->count(); ++i) {
int curPos = findTabIndex(bar, switcher->tabOrder[i]);
if (i != curPos && curPos != -1)
bar->moveTab(curPos, i);
}
connect(bar, &QTabBar::tabMoved, this, &SceneSwitcher::on_tabMoved);
}
void SceneSwitcher::on_tabMoved(int from, int to)
{
if (loading)
return;
std::swap(switcher->tabOrder[from], switcher->tabOrder[to]);
}
void SwitcherData::saveGeneralSettings(obs_data_t *obj)
{
obs_data_set_int(obj, "interval", switcher->interval);
std::string nonMatchingSceneName =
GetWeakSourceName(switcher->nonMatchingScene);
obs_data_set_string(obj, "non_matching_scene",
nonMatchingSceneName.c_str());
obs_data_set_int(obj, "switch_if_not_matching",
switcher->switchIfNotMatching);
obs_data_set_bool(obj, "active", !switcher->stop);
obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior);
std::string autoStopSceneName =
GetWeakSourceName(switcher->autoStopScene);
obs_data_set_bool(obj, "autoStopEnable", switcher->autoStopEnable);
obs_data_set_string(obj, "autoStopSceneName",
autoStopSceneName.c_str());
std::string autoStartSceneName =
GetWeakSourceName(switcher->autoStartScene);
obs_data_set_bool(obj, "autoStartEnable", switcher->autoStartEnable);
obs_data_set_int(obj, "autoStartType", switcher->autoStartType);
obs_data_set_string(obj, "autoStartSceneName",
autoStartSceneName.c_str());
obs_data_set_bool(obj, "verbose", switcher->verbose);
obs_data_set_int(obj, "priority0",
switcher->functionNamesByPriority[0]);
obs_data_set_int(obj, "priority1",
switcher->functionNamesByPriority[1]);
obs_data_set_int(obj, "priority2",
switcher->functionNamesByPriority[2]);
obs_data_set_int(obj, "priority3",
switcher->functionNamesByPriority[3]);
obs_data_set_int(obj, "priority4",
switcher->functionNamesByPriority[4]);
obs_data_set_int(obj, "priority5",
switcher->functionNamesByPriority[5]);
obs_data_set_int(obj, "priority6",
switcher->functionNamesByPriority[6]);
obs_data_set_int(obj, "priority7",
switcher->functionNamesByPriority[7]);
obs_data_set_int(obj, "priority8",
switcher->functionNamesByPriority[8]);
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
obs_data_set_int(obj, "transitionTabPos", switcher->tabOrder[1]);
obs_data_set_int(obj, "pauseTabPos", switcher->tabOrder[2]);
obs_data_set_int(obj, "titleTabPos", switcher->tabOrder[3]);
obs_data_set_int(obj, "exeTabPos", switcher->tabOrder[4]);
obs_data_set_int(obj, "regionTabPos", switcher->tabOrder[5]);
obs_data_set_int(obj, "mediaTabPos", switcher->tabOrder[6]);
obs_data_set_int(obj, "fileTabPos", switcher->tabOrder[7]);
obs_data_set_int(obj, "randomTabPos", switcher->tabOrder[8]);
obs_data_set_int(obj, "timeTabPos", switcher->tabOrder[9]);
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
}
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
{
obs_data_set_default_int(obj, "interval", default_interval);
switcher->interval = obs_data_get_int(obj, "interval");
obs_data_set_default_int(obj, "switch_if_not_matching", NO_SWITCH);
switcher->switchIfNotMatching =
(NoMatch)obs_data_get_int(obj, "switch_if_not_matching");
std::string nonMatchingScene =
obs_data_get_string(obj, "non_matching_scene");
switcher->nonMatchingScene =
GetWeakSourceByName(nonMatchingScene.c_str());
switcher->stop = !obs_data_get_bool(obj, "active");
switcher->startupBehavior =
(StartupBehavior)obs_data_get_int(obj, "startup_behavior");
if (switcher->startupBehavior == START)
switcher->stop = false;
if (switcher->startupBehavior == STOP)
switcher->stop = true;
std::string autoStopScene =
obs_data_get_string(obj, "autoStopSceneName");
switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable");
switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str());
std::string autoStartScene =
obs_data_get_string(obj, "autoStartSceneName");
switcher->autoStartEnable = obs_data_get_bool(obj, "autoStartEnable");
switcher->autoStartType =
(AutoStartType)obs_data_get_int(obj, "autoStartType");
switcher->autoStartScene = GetWeakSourceByName(autoStartScene.c_str());
switcher->verbose = obs_data_get_bool(obj, "verbose");
obs_data_set_default_int(obj, "priority0", default_priority_0);
obs_data_set_default_int(obj, "priority1", default_priority_1);
obs_data_set_default_int(obj, "priority2", default_priority_2);
obs_data_set_default_int(obj, "priority3", default_priority_3);
obs_data_set_default_int(obj, "priority4", default_priority_4);
obs_data_set_default_int(obj, "priority5", default_priority_5);
obs_data_set_default_int(obj, "priority6", default_priority_6);
obs_data_set_default_int(obj, "priority7", default_priority_7);
obs_data_set_default_int(obj, "priority8", default_priority_8);
switcher->functionNamesByPriority[0] =
(obs_data_get_int(obj, "priority0"));
switcher->functionNamesByPriority[1] =
(obs_data_get_int(obj, "priority1"));
switcher->functionNamesByPriority[2] =
(obs_data_get_int(obj, "priority2"));
switcher->functionNamesByPriority[3] =
(obs_data_get_int(obj, "priority3"));
switcher->functionNamesByPriority[4] =
(obs_data_get_int(obj, "priority4"));
switcher->functionNamesByPriority[5] =
(obs_data_get_int(obj, "priority5"));
switcher->functionNamesByPriority[6] =
(obs_data_get_int(obj, "priority6"));
switcher->functionNamesByPriority[7] =
(obs_data_get_int(obj, "priority7"));
switcher->functionNamesByPriority[8] =
(obs_data_get_int(obj, "priority8"));
if (!switcher->prioFuncsValid()) {
switcher->functionNamesByPriority[0] = (default_priority_0);
switcher->functionNamesByPriority[1] = (default_priority_1);
switcher->functionNamesByPriority[2] = (default_priority_2);
switcher->functionNamesByPriority[3] = (default_priority_3);
switcher->functionNamesByPriority[4] = (default_priority_4);
switcher->functionNamesByPriority[5] = (default_priority_5);
switcher->functionNamesByPriority[6] = (default_priority_6);
switcher->functionNamesByPriority[7] = (default_priority_7);
switcher->functionNamesByPriority[8] = (default_priority_8);
}
obs_data_set_default_int(obj, "threadPriority",
QThread::NormalPriority);
switcher->threadPriority = obs_data_get_int(obj, "threadPriority");
obs_data_set_default_int(obj, "generalTabPos", 0);
obs_data_set_default_int(obj, "transitionTabPos", 1);
obs_data_set_default_int(obj, "pauseTabPos", 2);
obs_data_set_default_int(obj, "titleTabPos", 3);
obs_data_set_default_int(obj, "exeTabPos", 4);
obs_data_set_default_int(obj, "regionTabPos", 5);
obs_data_set_default_int(obj, "mediaTabPos", 6);
obs_data_set_default_int(obj, "fileTabPos", 7);
obs_data_set_default_int(obj, "randomTabPos", 8);
obs_data_set_default_int(obj, "timeTabPos", 9);
obs_data_set_default_int(obj, "idleTabPos", 10);
obs_data_set_default_int(obj, "sequenceTabPos", 11);
obs_data_set_default_int(obj, "audioTabPos", 12);
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "generalTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "transitionTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "pauseTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "titleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "exeTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "regionTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "mediaTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "fileTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "randomTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "timeTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "idleTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "sequenceTabPos")));
switcher->tabOrder.emplace_back(
(int)(obs_data_get_int(obj, "audioTabPos")));
}
void SceneSwitcher::setupGeneralTab()
{
populateSceneSelection(ui->noMatchSwitchScene, false);
populateSceneSelection(ui->autoStopScenes, false);
populateSceneSelection(ui->autoStartScenes, false);
if (switcher->switchIfNotMatching == SWITCH) {
ui->noMatchSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(true);
} else if (switcher->switchIfNotMatching == NO_SWITCH) {
ui->noMatchDontSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(false);
} else {
ui->noMatchRandomSwitch->setChecked(true);
ui->noMatchSwitchScene->setEnabled(false);
}
ui->noMatchSwitchScene->setCurrentText(
GetWeakSourceName(switcher->nonMatchingScene).c_str());
ui->checkInterval->setValue(switcher->interval);
ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable);
ui->autoStopScenes->setCurrentText(
GetWeakSourceName(switcher->autoStopScene).c_str());
if (ui->autoStopSceneCheckBox->checkState()) {
ui->autoStopScenes->setDisabled(false);
} else {
ui->autoStopScenes->setDisabled(true);
}
ui->autoStartType->addItem("Recording");
ui->autoStartType->addItem("Streaming");
ui->autoStartType->addItem("Recording and Streaming");
ui->autoStartSceneCheckBox->setChecked(switcher->autoStartEnable);
ui->autoStartScenes->setCurrentText(
GetWeakSourceName(switcher->autoStartScene).c_str());
ui->autoStartType->setCurrentIndex(switcher->autoStartType);
if (ui->autoStartSceneCheckBox->checkState()) {
ui->autoStartScenes->setDisabled(false);
ui->autoStartType->setDisabled(false);
} else {
ui->autoStartScenes->setDisabled(true);
ui->autoStartType->setDisabled(true);
}
ui->verboseLogging->setChecked(switcher->verbose);
for (int p : switcher->functionNamesByPriority) {
std::string s = "";
switch (p) {
case read_file_func:
s = "File Content";
break;
case round_trip_func:
s = "Scene Sequence";
break;
case idle_func:
s = "Idle Detection";
break;
case exe_func:
s = "Executable";
break;
case screen_region_func:
s = "Screen Region";
break;
case window_title_func:
s = "Window Title";
break;
case media_func:
s = "Media";
break;
case time_func:
s = "Time";
break;
case audio_func:
s = "Audio";
break;
}
QString text(s.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->priorityList);
item->setData(Qt::UserRole, text);
}
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());
}
}
ui->startupBehavior->addItem(
"Start the scene switcher if it was running");
ui->startupBehavior->addItem("Always start the scene switcher");
ui->startupBehavior->addItem("Do not start the scene switcher");
ui->startupBehavior->setCurrentIndex(switcher->startupBehavior);
if (switcher->th && switcher->th->isRunning())
SetStarted();
else
SetStopped();
}

View File

@@ -10,6 +10,7 @@
#include "ui_advanced-scene-switcher.h"
#endif
#include "switcher-data-structs.hpp"
#include "volume-control.hpp"
class QCloseEvent;
@@ -21,6 +22,7 @@ class SceneSwitcher : public QDialog {
public:
std::unique_ptr<Ui_SceneSwitcher> ui;
VolControl *volMeter = nullptr;
bool loading = true;
SceneSwitcher(QWidget *parent);
@@ -42,12 +44,34 @@ public:
int executableFindByData(const QString &exe);
int IgnoreIdleWindowsFindByData(const QString &window);
int randomFindByData(const QString &scene);
int timeFindByData(const QString &timeStr);
int timeFindByData(const timeTrigger &trigger, const QTime &time);
int audioFindByData(const QString &source, const int &volume);
void UpdateNonMatchingScene(const QString &name);
void UpdateAutoStopScene(const QString &name);
void UpdateAutoStartScene(const QString &name);
void UpdateIdleDataTransition(const QString &name);
void UpdateIdleDataScene(const QString &name);
void SetAudioVolumeMeter(const QString &name);
void loadUI();
void populateSceneSelection(QComboBox *sel, bool addPrevious);
void populateTransitionSelection(QComboBox *sel);
void populateWindowSelection(QComboBox *sel);
void setupGeneralTab();
void setupTitleTab();
void setupExecutableTab();
void setupRegionTab();
void setupPauseTab();
void setupSequenceTab();
void setupTransitionsTab();
void setupIdleTab();
void setupRandomTab();
void setupMediaTab();
void setupFileTab();
void setupTimeTab();
void setupAudioTab();
void setTabOrder();
public slots:
void on_switches_currentRowChanged(int idx);
@@ -58,14 +82,17 @@ public slots:
void on_noMatchDontSwitch_clicked();
void on_noMatchSwitch_clicked();
void on_noMatchRandomSwitch_clicked();
void on_startAtLaunch_toggled(bool value);
void on_startupBehavior_currentIndexChanged(int index);
void on_noMatchSwitchScene_currentTextChanged(const QString &text);
void on_checkInterval_valueChanged(int value);
void on_toggleStartButton_clicked();
void on_tabMoved(int from, int to);
void on_screenRegions_currentRowChanged(int idx);
void on_screenRegionAdd_clicked();
void on_screenRegionRemove_clicked();
void on_screenRegionUp_clicked();
void on_screenRegionDown_clicked();
void on_pauseScenes_currentRowChanged(int idx);
void on_pauseScenesAdd_clicked();
@@ -84,19 +111,33 @@ public slots:
void on_sceneRoundTripRemove_clicked();
void on_sceneRoundTripSave_clicked();
void on_sceneRoundTripLoad_clicked();
void on_sceneRoundTripUp_clicked();
void on_sceneRoundTripDown_clicked();
void on_sceneRoundTripDelayUnits_currentIndexChanged(int index);
void on_autoStopSceneCheckBox_stateChanged(int state);
void on_autoStopScenes_currentTextChanged(const QString &text);
void on_autoStartSceneCheckBox_stateChanged(int state);
void on_autoStartType_currentIndexChanged(int index);
void on_autoStartScenes_currentTextChanged(const QString &text);
void on_verboseLogging_stateChanged(int state);
void on_exportSettings_clicked();
void on_importSettings_clicked();
void on_sceneTransitions_currentRowChanged(int idx);
void on_transitionsAdd_clicked();
void on_transitionsRemove_clicked();
void on_defaultTransitions_currentRowChanged(int idx);
void on_defaultTransitionsAdd_clicked();
void on_defaultTransitionsRemove_clicked();
void on_transitionOverridecheckBox_stateChanged(int state);
void on_browseButton_clicked();
void on_readFileCheckBox_stateChanged(int state);
void on_fileType_currentIndexChanged(int idx);
void on_readPathLineEdit_textChanged(const QString &text);
void on_writePathLineEdit_textChanged(const QString &text);
void on_browseButton_2_clicked();
@@ -123,14 +164,28 @@ public slots:
void on_fileRemove_clicked();
void on_fileScenesList_currentRowChanged(int idx);
void on_browseButton_3_clicked();
void on_fileUp_clicked();
void on_fileDown_clicked();
void on_mediaSwitches_currentRowChanged(int idx);
void on_mediaAdd_clicked();
void on_mediaRemove_clicked();
void on_mediaUp_clicked();
void on_mediaDown_clicked();
void on_mediaTimeRestrictions_currentIndexChanged(int idx);
void on_timeSwitches_currentRowChanged(int idx);
void on_timeAdd_clicked();
void on_timeRemove_clicked();
void on_timeUp_clicked();
void on_timeDown_clicked();
void on_audioAdd_clicked();
void on_audioRemove_clicked();
void on_audioUp_clicked();
void on_audioDown_clicked();
void on_audioSwitches_currentRowChanged(int idx);
void on_audioSources_currentTextChanged(const QString &text);
void on_priorityUp_clicked();
void on_priorityDown_clicked();
@@ -139,6 +194,8 @@ public slots:
void updateScreenRegionCursorPos();
void on_close_clicked();
private:
};
/********************************************************************************
@@ -171,18 +228,29 @@ bool isInFocus(const QString &executable);
struct obs_weak_source;
typedef struct obs_weak_source obs_weak_source_t;
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *scene2);
typedef struct transitionData {
std::string name = "";
int duration = 0;
} transitionData;
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool &transitionOverrideOverride, transitionData &td);
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td);
void restoreTransitionOverride(obs_source_t *scene, transitionData td);
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
bool &transitionOverrideOverride,
std::unique_lock<std::mutex> &lock);
/********************************************************************************
* Hotkey helper
********************************************************************************/
#define TOGGLE_HOTKEY_PATH "hotkey_toggle.txt"
#define STOP_HOTKEY_PATH "hotkey_stop.txt"
#define START_HOTKEY_PATH "hotkey_start.txt"
constexpr auto toggle_hotkey_path = "hotkey_toggle.txt";
constexpr auto stop_hotkey_path = "hotkey_stop.txt";
constexpr auto start_hotkey_path = "hotkey_start.txt";
void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed);

View File

@@ -0,0 +1,26 @@
#pragma once
#include <curl/curl.h>
#include <QLibrary>
#if defined(WIN32)
#define CURL_LIBRARY_NAME "libcurl.dll"
#elif __APPLE__
#define CURL_LIBRARY_NAME "libcurl.4.dylib"
#else
#define CURL_LIBRARY_NAME "libcurl.so.4"
#endif
typedef CURL *(*initFunction)(void);
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
extern initFunction f_curl_init;
extern setOptFunction f_curl_setopt;
extern performFunction f_curl_perform;
extern cleanupFunction f_curl_cleanup;
extern QLibrary *loaded_curl_lib;
bool resolveCurl();
bool loadCurl();

View File

@@ -8,31 +8,34 @@
#include <fstream>
#include <QDateTime>
#include <QThread>
#include <curl/curl.h>
#include "utility.hpp"
#define DEFAULT_INTERVAL 300
constexpr auto default_interval = 300;
#define DEFAULT_IDLE_TIME 60
constexpr auto default_idle_time = 60;
#define PREVIOUS_SCENE_NAME "Previous Scene"
constexpr auto previous_scene_name = "Previous Scene";
#define READ_FILE_FUNC 0
#define ROUND_TRIP_FUNC 1
#define IDLE_FUNC 2
#define EXE_FUNC 3
#define SCREEN_REGION_FUNC 4
#define WINDOW_TITLE_FUNC 5
#define MEDIA_FUNC 6
#define TIME_FUNC 7
constexpr auto read_file_func = 0;
constexpr auto round_trip_func = 1;
constexpr auto idle_func = 2;
constexpr auto exe_func = 3;
constexpr auto screen_region_func = 4;
constexpr auto window_title_func = 5;
constexpr auto media_func = 6;
constexpr auto time_func = 7;
constexpr auto audio_func = 8;
#define DEFAULT_PRIORITY_0 READ_FILE_FUNC
#define DEFAULT_PRIORITY_1 ROUND_TRIP_FUNC
#define DEFAULT_PRIORITY_2 IDLE_FUNC
#define DEFAULT_PRIORITY_3 EXE_FUNC
#define DEFAULT_PRIORITY_4 SCREEN_REGION_FUNC
#define DEFAULT_PRIORITY_5 WINDOW_TITLE_FUNC
#define DEFAULT_PRIORITY_6 MEDIA_FUNC
#define DEFAULT_PRIORITY_7 TIME_FUNC
constexpr auto default_priority_0 = read_file_func;
constexpr auto default_priority_1 = round_trip_func;
constexpr auto default_priority_2 = idle_func;
constexpr auto default_priority_3 = exe_func;
constexpr auto default_priority_4 = screen_region_func;
constexpr auto default_priority_5 = window_title_func;
constexpr auto default_priority_6 = media_func;
constexpr auto default_priority_7 = time_func;
constexpr auto default_priority_8 = audio_func;
/********************************************************************************
* Data structs for each scene switching method
@@ -98,13 +101,13 @@ struct SceneRoundTripSwitch {
OBSWeakSource scene1;
OBSWeakSource scene2;
OBSWeakSource transition;
int delay;
double delay;
bool usePreviousScene;
std::string sceneRoundTripStr;
inline SceneRoundTripSwitch(OBSWeakSource scene1_,
OBSWeakSource scene2_,
OBSWeakSource transition_, int delay_,
OBSWeakSource transition_, double delay_,
bool usePreviousScene_, std::string str)
: scene1(scene1_),
scene2(scene2_),
@@ -169,17 +172,19 @@ struct FileSwitch {
OBSWeakSource transition;
std::string file;
std::string text;
bool remote = false;
bool useRegex = false;
bool useTime = false;
QDateTime lastMod;
inline FileSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
const char *file_, const char *text_, bool useRegex_,
bool useTime_)
const char *file_, const char *text_, bool remote_,
bool useRegex_, bool useTime_)
: scene(scene_),
transition(transition_),
file(file_),
text(text_),
remote(remote_),
useRegex(useRegex_),
useTime(useTime_),
lastMod()
@@ -196,7 +201,7 @@ struct FileIOData {
struct IdleData {
bool idleEnable = false;
int time = DEFAULT_IDLE_TIME;
int time = default_idle_time;
OBSWeakSource scene;
OBSWeakSource transition;
bool usePreviousScene;
@@ -208,7 +213,7 @@ struct MediaSwitch {
OBSWeakSource source;
OBSWeakSource transition;
obs_media_state state;
uint64_t time;
int64_t time;
time_restriction restriction;
bool matched;
bool usePreviousScene;
@@ -233,16 +238,17 @@ struct MediaSwitch {
struct TimeSwitch {
OBSWeakSource scene;
OBSWeakSource transition;
timeTrigger trigger;
QTime time;
bool matched;
bool usePreviousScene;
std::string timeSwitchStr;
inline TimeSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
QTime time_, bool usePreviousScene_,
std::string timeSwitchStr_)
timeTrigger trigger_, QTime time_,
bool usePreviousScene_, std::string timeSwitchStr_)
: scene(scene_),
transition(transition_),
trigger(trigger_),
time(time_),
usePreviousScene(usePreviousScene_),
timeSwitchStr(timeSwitchStr_)
@@ -250,7 +256,114 @@ struct TimeSwitch {
}
};
struct AudioSwitch {
OBSWeakSource scene;
OBSWeakSource audioSource;
OBSWeakSource transition;
int volumeThreshold;
float peak;
bool usePreviousScene;
obs_volmeter_t *volmeter;
std::string audioSwitchStr;
static void setVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS])
{
UNUSED_PARAMETER(magnitude);
UNUSED_PARAMETER(inputPeak);
AudioSwitch *s = static_cast<AudioSwitch *>(data);
s->peak = peak[0];
for (int i = 1; i < MAX_AUDIO_CHANNELS; i++)
if (peak[i] > s->peak)
s->peak = peak[i];
}
inline AudioSwitch(OBSWeakSource scene_, OBSWeakSource transition_,
OBSWeakSource audioSource_, int volumeThreshold_,
bool usePreviousScene_, std::string audioSwitchStr_)
: scene(scene_),
transition(transition_),
audioSource(audioSource_),
volumeThreshold(volumeThreshold_),
usePreviousScene(usePreviousScene_),
audioSwitchStr(audioSwitchStr_)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as = obs_weak_source_get_source(audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING,
"failed to attach volmeter to source %s", name);
}
obs_source_release(as);
}
AudioSwitch(const AudioSwitch &other)
: scene(other.scene),
transition(other.transition),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold),
usePreviousScene(other.usePreviousScene),
audioSwitchStr(other.audioSwitchStr)
{
volmeter = obs_volmeter_create(OBS_FADER_LOG);
obs_volmeter_add_callback(volmeter, setVolumeLevel, this);
obs_source_t *as =
obs_weak_source_get_source(other.audioSource);
if (!obs_volmeter_attach_source(volmeter, as)) {
const char *name = obs_source_get_name(as);
blog(LOG_WARNING,
"failed to attach volmeter to source %s", name);
}
obs_source_release(as);
}
AudioSwitch(AudioSwitch &&other)
: scene(other.scene),
transition(other.transition),
audioSource(other.audioSource),
volumeThreshold(other.volumeThreshold),
usePreviousScene(other.usePreviousScene),
audioSwitchStr(other.audioSwitchStr),
volmeter(other.volmeter)
{
other.volmeter = nullptr;
}
inline ~AudioSwitch()
{
obs_volmeter_remove_callback(volmeter, setVolumeLevel, this);
obs_volmeter_destroy(volmeter);
}
AudioSwitch &operator=(const AudioSwitch &other)
{
return *this = AudioSwitch(other);
}
AudioSwitch &operator=(AudioSwitch &&other) noexcept
{
if (this == &other) {
return *this;
}
obs_volmeter_destroy(volmeter);
volmeter = other.volmeter;
other.volmeter = nullptr;
return *this;
}
};
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
typedef enum {
RECORDING = 0,
STREAMING = 1,
RECORINDGSTREAMING = 2
} AutoStartType;
class SwitcherThread;
@@ -265,10 +378,11 @@ struct SwitcherData {
bool transitionActive = false;
bool waitForTransition = false;
std::condition_variable transitionCv;
bool startAtLaunch = false;
bool stop = false;
bool verbose = false;
bool tansitionOverrideOverride = false;
int interval = DEFAULT_INTERVAL;
int interval = default_interval;
obs_source_t *waitScene = NULL; //scene during which wait started
OBSWeakSource previousScene = NULL;
@@ -276,6 +390,7 @@ struct SwitcherData {
OBSWeakSource lastRandomScene;
OBSWeakSource nonMatchingScene;
NoMatch switchIfNotMatching = NO_SWITCH;
StartupBehavior startupBehavior = PERSIST;
std::vector<WindowSceneSwitch> windowSwitches;
std::vector<std::string> ignoreIdleWindows;
@@ -290,29 +405,39 @@ struct SwitcherData {
std::vector<std::string> ignoreWindowsSwitches;
std::vector<SceneRoundTripSwitch> sceneRoundTripSwitches;
int sceneRoundTripUnitMultiplier = 1;
std::vector<RandomSwitch> randomSwitches;
FileIOData fileIO;
IdleData idleData;
std::vector<FileSwitch> fileSwitches;
CURL *curl = nullptr;
std::vector<ExecutableSceneSwitch> executableSwitches;
bool autoStopEnable = false;
OBSWeakSource autoStopScene;
bool autoStartEnable = false;
AutoStartType autoStartType = RECORDING;
OBSWeakSource autoStartScene;
bool autoStartedRecently = false;
std::vector<SceneTransition> sceneTransitions;
std::vector<DefaultSceneTransition> defaultSceneTransitions;
std::vector<MediaSwitch> mediaSwitches;
std::vector<TimeSwitch> timeSwitches;
QDateTime liveTime;
std::vector<AudioSwitch> audioSwitches;
std::vector<int> functionNamesByPriority = std::vector<int>{
DEFAULT_PRIORITY_0, DEFAULT_PRIORITY_1, DEFAULT_PRIORITY_2,
DEFAULT_PRIORITY_3, DEFAULT_PRIORITY_4, DEFAULT_PRIORITY_5,
DEFAULT_PRIORITY_6, DEFAULT_PRIORITY_7};
default_priority_0, default_priority_1, default_priority_2,
default_priority_3, default_priority_4, default_priority_5,
default_priority_6, default_priority_7, default_priority_8};
struct ThreadPrio {
std::string name;
@@ -341,6 +466,8 @@ struct SwitcherData {
uint32_t threadPriority = QThread::NormalPriority;
std::vector<int> tabOrder;
void Thread();
void Start();
void Stop();
@@ -350,6 +477,7 @@ struct SwitcherData {
void writeSceneInfoToFile();
void setDefaultSceneTransitions();
void autoStopStreamAndRecording();
void autoStartStreamRecording();
bool checkPause();
void checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition,
@@ -372,6 +500,36 @@ struct SwitcherData {
OBSWeakSource &transition);
void checkTimeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void checkAudioSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition);
void saveWindowTitleSwitches(obs_data_t *obj);
void saveScreenRegionSwitches(obs_data_t *obj);
void savePauseSwitches(obs_data_t *obj);
void saveSceneRoundTripSwitches(obs_data_t *obj);
void saveSceneTransitions(obs_data_t *obj);
void saveIdleSwitches(obs_data_t *obj);
void saveExecutableSwitches(obs_data_t *obj);
void saveRandomSwitches(obs_data_t *obj);
void saveFileSwitches(obs_data_t *obj);
void saveMediaSwitches(obs_data_t *obj);
void saveTimeSwitches(obs_data_t *obj);
void saveAudioSwitches(obs_data_t *obj);
void saveGeneralSettings(obs_data_t *obj);
void loadWindowTitleSwitches(obs_data_t *obj);
void loadScreenRegionSwitches(obs_data_t *obj);
void loadPauseSwitches(obs_data_t *obj);
void loadSceneRoundTripSwitches(obs_data_t *obj);
void loadSceneTransitions(obs_data_t *obj);
void loadIdleSwitches(obs_data_t *obj);
void loadExecutableSwitches(obs_data_t *obj);
void loadRandomSwitches(obs_data_t *obj);
void loadFileSwitches(obs_data_t *obj);
void loadMediaSwitches(obs_data_t *obj);
void loadTimeSwitches(obs_data_t *obj);
void loadAudioSwitches(obs_data_t *obj);
void loadGeneralSettings(obs_data_t *obj);
void Prune()
{
@@ -481,6 +639,15 @@ struct SwitcherData {
mediaSwitches.erase(mediaSwitches.begin() +
i--);
}
for (size_t i = 0; i < audioSwitches.size(); i++) {
AudioSwitch &s = audioSwitches[i];
if ((!s.usePreviousScene &&
!WeakSourceValid(s.scene)) ||
!WeakSourceValid(s.transition))
audioSwitches.erase(audioSwitches.begin() +
i--);
}
}
inline ~SwitcherData() { Stop(); }
};

View File

@@ -2,7 +2,6 @@
#include <obs.hpp>
#include <QString>
#include <obs-frontend-api.h>
#include "switcher-data-structs.hpp"
static inline bool WeakSourceValid(obs_weak_source_t *ws)
{
@@ -170,11 +169,70 @@ MakeMediaSwitchName(const QString &source, const QString &scene,
return switchName;
}
typedef enum {
ANY_DAY = 0,
MONDAY = 1,
TUSEDAY = 2,
WEDNESDAY = 3,
THURSDAY = 4,
FRIDAY = 5,
SATURDAY = 6,
SUNDAY = 7,
LIVE = 8
} timeTrigger;
static inline QString MakeTimeSwitchName(const QString &scene,
const QString &transition, QTime &time)
const QString &transition,
const timeTrigger &trigger,
const QTime &time)
{
QString switchName = QStringLiteral("At ") + time.toString() +
QStringLiteral(" switch to ") + scene +
QString switchName;
switch (trigger) {
case ANY_DAY:
switchName = QStringLiteral("On any weekday");
break;
case MONDAY:
switchName = QStringLiteral("Mondays");
break;
case TUSEDAY:
switchName = QStringLiteral("Tusedays");
break;
case WEDNESDAY:
switchName = QStringLiteral("Wednesdays");
break;
case THURSDAY:
switchName = QStringLiteral("Thursdays");
break;
case FRIDAY:
switchName = QStringLiteral("Fridays");
break;
case SATURDAY:
switchName = QStringLiteral("Saturdays");
break;
case SUNDAY:
switchName = QStringLiteral("Sundays");
break;
case LIVE:
switchName = QStringLiteral(
"Relative to starting streaming / recording");
break;
}
switchName += QStringLiteral(" at ") + time.toString() +
QStringLiteral(" switch to ") + scene +
QStringLiteral(" using ") + transition;
return switchName;
}
static inline QString MakeAudioSwitchName(const QString &scene,
const QString &transition,
const QString &audioSrouce,
const int &volume)
{
QString switchName = QStringLiteral("When volume of ") + audioSrouce +
QStringLiteral(" is above ") +
QString::number(volume) +
QStringLiteral("% switch to ") + scene +
QStringLiteral(" using ") + transition;
return switchName;
}

View File

@@ -0,0 +1,245 @@
#pragma once
#include <obs.hpp>
#include <QWidget>
#include <QPaintEvent>
#include <QSharedPointer>
#include <QTimer>
#include <QMutex>
#include <QList>
class QPushButton;
class VolumeMeterTimer;
class VolumeMeter : public QWidget {
Q_OBJECT
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor
WRITE setBackgroundNominalColor DESIGNABLE true)
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor
WRITE setBackgroundWarningColor DESIGNABLE true)
Q_PROPERTY(QColor backgroundErrorColor READ getBackgroundErrorColor
WRITE setBackgroundErrorColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor
WRITE setForegroundNominalColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor
WRITE setForegroundWarningColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundErrorColor READ getForegroundErrorColor
WRITE setForegroundErrorColor DESIGNABLE true)
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor
DESIGNABLE true)
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE
setMagnitudeColor DESIGNABLE true)
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE
setMajorTickColor DESIGNABLE true)
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE
setMinorTickColor DESIGNABLE true)
// Levels are denoted in dBFS.
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel
DESIGNABLE true)
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel
DESIGNABLE true)
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel
DESIGNABLE true)
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel
DESIGNABLE true)
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE
setMinimumInputLevel DESIGNABLE true)
// Rates are denoted in dB/second.
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE
setPeakDecayRate DESIGNABLE true)
// Time in seconds for the VU meter to integrate over.
Q_PROPERTY(
qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime
WRITE setMagnitudeIntegrationTime DESIGNABLE true)
// Duration is denoted in seconds.
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE
setPeakHoldDuration DESIGNABLE true)
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration
WRITE setInputPeakHoldDuration DESIGNABLE true)
private slots:
void ClipEnding();
private:
obs_volmeter_t *obs_volmeter;
static QWeakPointer<VolumeMeterTimer> updateTimer;
QSharedPointer<VolumeMeterTimer> updateTimerRef;
inline void resetLevels();
inline void handleChannelCofigurationChange();
inline bool detectIdle(uint64_t ts);
inline void calculateBallistics(uint64_t ts,
qreal timeSinceLastRedraw = 0.0);
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts,
qreal timeSinceLastRedraw);
void paintInputMeter(QPainter &painter, int x, int y, int width,
int height, float peakHold);
void paintHMeter(QPainter &painter, int x, int y, int width, int height,
float magnitude, float peak, float peakHold);
void paintHTicks(QPainter &painter, int x, int y, int width,
int height);
void paintVMeter(QPainter &painter, int x, int y, int width, int height,
float magnitude, float peak, float peakHold);
void paintVTicks(QPainter &painter, int x, int y, int height);
QMutex dataMutex;
uint64_t currentLastUpdateTime = 0;
float currentMagnitude[MAX_AUDIO_CHANNELS];
float currentPeak[MAX_AUDIO_CHANNELS];
float currentInputPeak[MAX_AUDIO_CHANNELS];
QPixmap *tickPaintCache = nullptr;
int displayNrAudioChannels = 0;
float displayMagnitude[MAX_AUDIO_CHANNELS];
float displayPeak[MAX_AUDIO_CHANNELS];
float displayPeakHold[MAX_AUDIO_CHANNELS];
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
QFont tickFont;
QColor backgroundNominalColor;
QColor backgroundWarningColor;
QColor backgroundErrorColor;
QColor foregroundNominalColor;
QColor foregroundWarningColor;
QColor foregroundErrorColor;
QColor clipColor;
QColor magnitudeColor;
QColor majorTickColor;
QColor minorTickColor;
qreal minimumLevel;
qreal warningLevel;
qreal errorLevel;
qreal clipLevel;
qreal minimumInputLevel;
qreal peakDecayRate;
qreal magnitudeIntegrationTime;
qreal peakHoldDuration;
qreal inputPeakHoldDuration;
uint64_t lastRedrawTime = 0;
int channels = 0;
bool clipping = false;
bool vertical;
public:
explicit VolumeMeter(QWidget *parent = nullptr,
obs_volmeter_t *obs_volmeter = nullptr,
bool vertical = false);
~VolumeMeter();
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
QColor getBackgroundNominalColor() const;
void setBackgroundNominalColor(QColor c);
QColor getBackgroundWarningColor() const;
void setBackgroundWarningColor(QColor c);
QColor getBackgroundErrorColor() const;
void setBackgroundErrorColor(QColor c);
QColor getForegroundNominalColor() const;
void setForegroundNominalColor(QColor c);
QColor getForegroundWarningColor() const;
void setForegroundWarningColor(QColor c);
QColor getForegroundErrorColor() const;
void setForegroundErrorColor(QColor c);
QColor getClipColor() const;
void setClipColor(QColor c);
QColor getMagnitudeColor() const;
void setMagnitudeColor(QColor c);
QColor getMajorTickColor() const;
void setMajorTickColor(QColor c);
QColor getMinorTickColor() const;
void setMinorTickColor(QColor c);
qreal getMinimumLevel() const;
void setMinimumLevel(qreal v);
qreal getWarningLevel() const;
void setWarningLevel(qreal v);
qreal getErrorLevel() const;
void setErrorLevel(qreal v);
qreal getClipLevel() const;
void setClipLevel(qreal v);
qreal getMinimumInputLevel() const;
void setMinimumInputLevel(qreal v);
qreal getPeakDecayRate() const;
void setPeakDecayRate(qreal v);
qreal getMagnitudeIntegrationTime() const;
void setMagnitudeIntegrationTime(qreal v);
qreal getPeakHoldDuration() const;
void setPeakHoldDuration(qreal v);
qreal getInputPeakHoldDuration() const;
void setInputPeakHoldDuration(qreal v);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
virtual void mousePressEvent(QMouseEvent *event) override;
protected:
void paintEvent(QPaintEvent *event) override;
};
class VolumeMeterTimer : public QTimer {
Q_OBJECT
public:
inline VolumeMeterTimer() : QTimer() {}
void AddVolControl(VolumeMeter *meter);
void RemoveVolControl(VolumeMeter *meter);
protected:
void timerEvent(QTimerEvent *event) override;
QList<VolumeMeter *> volumeMeters;
};
class QLabel;
class QSlider;
class MuteCheckBox;
class VolControl : public QWidget {
Q_OBJECT
private:
OBSSource source;
QLabel *nameLabel;
QLabel *volLabel;
VolumeMeter *volMeter;
QSlider *slider;
float levelTotal;
float levelCount;
obs_fader_t *obs_fader;
obs_volmeter_t *obs_volmeter;
bool vertical;
static void OBSVolumeChanged(void *param, float db);
static void OBSVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
private slots:
void SliderChanged(int vol);
void updateText();
public:
explicit VolControl(OBSSource source, bool vertical = false);
~VolControl();
inline obs_source_t *GetSource() const { return source; }
QString GetName() const;
void SetName(const QString &newName);
void SetMeterDecayRate(qreal q);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
void EnableSlider(bool enable);
QSlider *GetSlider() const;
};

View File

@@ -17,7 +17,7 @@ void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
if (hotkeyData != NULL) {
char *path = obs_module_config_path("");
std::ofstream file;
file.open(std::string(path).append(START_HOTKEY_PATH),
file.open(std::string(path).append(start_hotkey_path),
std::ofstream::trunc);
if (file.is_open()) {
size_t num = obs_data_array_count(hotkeyData);
@@ -51,7 +51,7 @@ void stopHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
if (hotkeyData != NULL) {
char *path = obs_module_config_path("");
std::ofstream file;
file.open(std::string(path).append(STOP_HOTKEY_PATH),
file.open(std::string(path).append(stop_hotkey_path),
std::ofstream::trunc);
if (file.is_open()) {
size_t num = obs_data_array_count(hotkeyData);
@@ -87,7 +87,7 @@ void startStopToggleHotkeyFunc(void *data, obs_hotkey_id id,
if (hotkeyData != NULL) {
char *path = obs_module_config_path("");
std::ofstream file;
file.open(std::string(path).append(TOGGLE_HOTKEY_PATH),
file.open(std::string(path).append(toggle_hotkey_path),
std::ofstream::trunc);
if (file.is_open()) {
size_t num = obs_data_array_count(hotkeyData);

View File

@@ -41,6 +41,9 @@ void SwitcherData::checkIdleSwitch(bool &match, OBSWeakSource &scene,
transition = idleData.transition;
match = true;
idleData.alreadySwitched = true;
if (verbose)
blog(LOG_INFO, "Advanced Scene Switcher idle match");
} else
idleData.alreadySwitched = false;
}
@@ -77,7 +80,7 @@ void SceneSwitcher::UpdateIdleDataTransition(const QString &name)
void SceneSwitcher::UpdateIdleDataScene(const QString &name)
{
switcher->idleData.usePreviousScene = (name == PREVIOUS_SCENE_NAME);
switcher->idleData.usePreviousScene = (name == previous_scene_name);
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
@@ -199,3 +202,96 @@ int SceneSwitcher::IgnoreIdleWindowsFindByData(const QString &window)
return idx;
}
void SwitcherData::saveIdleSwitches(obs_data_t *obj)
{
obs_data_array_t *ignoreIdleWindowsArray = obs_data_array_create();
for (std::string &window : switcher->ignoreIdleWindows) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "window", window.c_str());
obs_data_array_push_back(ignoreIdleWindowsArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "ignoreIdleWindows", ignoreIdleWindowsArray);
obs_data_array_release(ignoreIdleWindowsArray);
std::string idleSceneName = GetWeakSourceName(switcher->idleData.scene);
std::string idleTransitionName =
GetWeakSourceName(switcher->idleData.transition);
obs_data_set_bool(obj, "idleEnable", switcher->idleData.idleEnable);
obs_data_set_string(obj, "idleSceneName",
switcher->idleData.usePreviousScene
? previous_scene_name
: idleSceneName.c_str());
obs_data_set_string(obj, "idleTransitionName",
idleTransitionName.c_str());
obs_data_set_int(obj, "idleTime", switcher->idleData.time);
}
void SwitcherData::loadIdleSwitches(obs_data_t *obj)
{
switcher->ignoreIdleWindows.clear();
obs_data_array_t *ignoreIdleWindowsArray =
obs_data_get_array(obj, "ignoreIdleWindows");
size_t count = obs_data_array_count(ignoreIdleWindowsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(ignoreIdleWindowsArray, i);
const char *window = obs_data_get_string(array_obj, "window");
switcher->ignoreIdleWindows.emplace_back(window);
obs_data_release(array_obj);
}
obs_data_array_release(ignoreIdleWindowsArray);
std::string idleSceneName = obs_data_get_string(obj, "idleSceneName");
std::string idleTransitionName =
obs_data_get_string(obj, "idleTransitionName");
switcher->idleData.scene = GetWeakSourceByName(idleSceneName.c_str());
switcher->idleData.transition =
GetWeakTransitionByName(idleTransitionName.c_str());
obs_data_set_default_bool(obj, "idleEnable", false);
switcher->idleData.idleEnable = obs_data_get_bool(obj, "idleEnable");
obs_data_set_default_int(obj, "idleTime", default_idle_time);
switcher->idleData.time = obs_data_get_int(obj, "idleTime");
switcher->idleData.usePreviousScene =
(idleSceneName == previous_scene_name);
}
void SceneSwitcher::setupIdleTab()
{
populateSceneSelection(ui->idleScenes, true);
populateTransitionSelection(ui->idleTransitions);
populateWindowSelection(ui->ignoreIdleWindowsWindows);
for (auto &window : switcher->ignoreIdleWindows) {
QString text = QString::fromStdString(window);
QListWidgetItem *item =
new QListWidgetItem(text, ui->ignoreIdleWindows);
item->setData(Qt::UserRole, text);
}
ui->idleCheckBox->setChecked(switcher->idleData.idleEnable);
ui->idleScenes->setCurrentText(
switcher->idleData.usePreviousScene
? previous_scene_name
: GetWeakSourceName(switcher->idleData.scene).c_str());
ui->idleTransitions->setCurrentText(
GetWeakSourceName(switcher->idleData.transition).c_str());
ui->idleSpinBox->setValue(switcher->idleData.time);
if (ui->idleCheckBox->checkState()) {
ui->idleScenes->setDisabled(false);
ui->idleSpinBox->setDisabled(false);
ui->idleTransitions->setDisabled(false);
} else {
ui->idleScenes->setDisabled(true);
ui->idleSpinBox->setDisabled(true);
ui->idleTransitions->setDisabled(true);
}
}

View File

@@ -63,7 +63,7 @@ void SceneSwitcher::on_mediaAdd_clicked()
std::lock_guard<std::mutex> 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());
}
@@ -92,6 +92,49 @@ void SceneSwitcher::on_mediaRemove_clicked()
delete item;
}
void SceneSwitcher::on_mediaUp_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != 0) {
ui->mediaSwitches->insertItem(
index - 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_mediaDown_clicked()
{
int index = ui->mediaSwitches->currentRow();
if (index != -1 && index != ui->mediaSwitches->count() - 1) {
ui->mediaSwitches->insertItem(
index + 1, ui->mediaSwitches->takeItem(index));
ui->mediaSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->mediaSwitches.begin() + index,
switcher->mediaSwitches.begin() + index + 1);
}
}
void SceneSwitcher::on_mediaTimeRestrictions_currentIndexChanged(int idx)
{
if (idx == -1)
return;
if ((time_restriction)ui->mediaTimeRestrictions->currentIndex() ==
TIME_RESTRICTION_NONE) {
ui->mediaTime->setDisabled(true);
} else {
ui->mediaTime->setDisabled(false);
}
}
void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
@@ -122,8 +165,137 @@ void SwitcherData::checkMediaSwitch(bool &match, OBSWeakSource &scene,
? previousScene
: mediaSwitch.scene;
transition = mediaSwitch.transition;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher meida match");
}
mediaSwitch.matched = matched;
obs_source_release(source);
}
}
void SwitcherData::saveMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_array_create();
for (MediaSwitch &s : switcher->mediaSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.source);
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if ((s.usePreviousScene || sceneSource) && source &&
transition) {
const char *sourceName = obs_source_get_name(source);
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "source", sourceName);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? previous_scene_name
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "state", s.state);
obs_data_set_int(array_obj, "restriction",
s.restriction);
obs_data_set_int(array_obj, "time", s.time);
obs_data_array_push_back(mediaArray, array_obj);
}
obs_source_release(source);
obs_source_release(sceneSource);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "mediaSwitches", mediaArray);
obs_data_array_release(mediaArray);
}
void SwitcherData::loadMediaSwitches(obs_data_t *obj)
{
obs_data_array_t *mediaArray = obs_data_get_array(obj, "mediaSwitches");
switcher->mediaSwitches.clear();
size_t count = obs_data_array_count(mediaArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(mediaArray, i);
const char *source = obs_data_get_string(array_obj, "source");
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
obs_media_state state =
(obs_media_state)obs_data_get_int(array_obj, "state");
time_restriction restriction =
(time_restriction)obs_data_get_int(array_obj,
"restriction");
uint64_t time = obs_data_get_int(array_obj, "time");
std::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),
mediaStr);
obs_data_release(array_obj);
}
obs_data_array_release(mediaArray);
}
void SceneSwitcher::setupMediaTab()
{
populateSceneSelection(ui->mediaScenes, true);
populateTransitionSelection(ui->mediaTransitions);
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
{
QComboBox *combo = reinterpret_cast<QComboBox *>(data);
if (strcmp(obs_source_get_id(source), "ffmpeg_source") == 0) {
const char *name = obs_source_get_name(source);
combo->addItem(name);
}
return true;
};
obs_enum_sources(sourceEnum, ui->mediaSources);
ui->mediaStates->addItem("None");
ui->mediaStates->addItem("Playing");
ui->mediaStates->addItem("Opening");
ui->mediaStates->addItem("Buffering");
ui->mediaStates->addItem("Paused");
ui->mediaStates->addItem("Stopped");
ui->mediaStates->addItem("Ended");
ui->mediaStates->addItem("Error");
ui->mediaTimeRestrictions->addItem("None");
ui->mediaTimeRestrictions->addItem("Time shorter");
ui->mediaTimeRestrictions->addItem("Time longer");
ui->mediaTimeRestrictions->addItem("Time remaining shorter");
ui->mediaTimeRestrictions->addItem("Time remaining longer");
for (auto &s : switcher->mediaSwitches) {
std::string sourceName = GetWeakSourceName(s.source);
std::string sceneName = (s.usePreviousScene)
? previous_scene_name
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeMediaSwitchName(
sourceName.c_str(), sceneName.c_str(),
transitionName.c_str(), s.state, s.restriction, s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->mediaSwitches);
item->setData(Qt::UserRole, listText);
}
}

View File

@@ -219,5 +219,104 @@ bool SwitcherData::checkPause()
}
}
}
if (verbose && pause)
blog(LOG_INFO, "Advanced Scene Switcher pause match");
return pause;
}
void SwitcherData::savePauseSwitches(obs_data_t *obj)
{
obs_data_array_t *pauseScenesArray = obs_data_array_create();
for (OBSWeakSource &scene : switcher->pauseScenesSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(scene);
if (source) {
const char *n = obs_source_get_name(source);
obs_data_set_string(array_obj, "pauseScene", n);
obs_data_array_push_back(pauseScenesArray, array_obj);
obs_source_release(source);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseScenes", pauseScenesArray);
obs_data_array_release(pauseScenesArray);
obs_data_array_t *pauseWindowsArray = obs_data_array_create();
for (std::string &window : switcher->pauseWindowsSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "pauseWindow", window.c_str());
obs_data_array_push_back(pauseWindowsArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "pauseWindows", pauseWindowsArray);
obs_data_array_release(pauseWindowsArray);
}
void SwitcherData::loadPauseSwitches(obs_data_t *obj)
{
switcher->pauseScenesSwitches.clear();
obs_data_array_t *pauseScenesArray =
obs_data_get_array(obj, "pauseScenes");
size_t count = obs_data_array_count(pauseScenesArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(pauseScenesArray, i);
const char *scene =
obs_data_get_string(array_obj, "pauseScene");
switcher->pauseScenesSwitches.emplace_back(
GetWeakSourceByName(scene));
obs_data_release(array_obj);
}
obs_data_array_release(pauseScenesArray);
switcher->pauseWindowsSwitches.clear();
obs_data_array_t *pauseWindowsArray =
obs_data_get_array(obj, "pauseWindows");
count = obs_data_array_count(pauseWindowsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(pauseWindowsArray, i);
const char *window =
obs_data_get_string(array_obj, "pauseWindow");
switcher->pauseWindowsSwitches.emplace_back(window);
obs_data_release(array_obj);
}
obs_data_array_release(pauseWindowsArray);
}
void SceneSwitcher::setupPauseTab()
{
populateSceneSelection(ui->pauseScenesScenes, false);
populateWindowSelection(ui->pauseWindowsWindows);
for (auto &scene : switcher->pauseScenesSwitches) {
std::string sceneName = GetWeakSourceName(scene);
QString text = QString::fromStdString(sceneName);
QListWidgetItem *item =
new QListWidgetItem(text, ui->pauseScenes);
item->setData(Qt::UserRole, text);
}
for (auto &window : switcher->pauseWindowsSwitches) {
QString text = QString::fromStdString(window);
QListWidgetItem *item =
new QListWidgetItem(text, ui->pauseWindows);
item->setData(Qt::UserRole, text);
}
}

View File

@@ -61,7 +61,7 @@ bool SwitcherData::prioFuncsValid()
return false;
for (int p : functionNamesByPriority) {
if (p < 0 || p > 7)
if (p < 0 || p > 8)
return false;
}
return true;

View File

@@ -132,6 +132,84 @@ void SwitcherData::checkRandom(bool &match, OBSWeakSource &scene,
delay = (int)r.delay * 1000;
match = true;
lastRandomScene = r.scene;
if (verbose)
blog(LOG_INFO, "Advanced Scene Switcher random match");
break;
}
}
void SwitcherData::saveRandomSwitches(obs_data_t *obj)
{
obs_data_array_t *randomArray = obs_data_array_create();
for (RandomSwitch &s : switcher->randomSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_double(array_obj, "delay", s.delay);
obs_data_set_string(array_obj, "str",
s.randomSwitchStr.c_str());
obs_data_array_push_back(randomArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "randomSwitches", randomArray);
obs_data_array_release(randomArray);
}
void SwitcherData::loadRandomSwitches(obs_data_t *obj)
{
switcher->randomSwitches.clear();
obs_data_array_t *randomArray =
obs_data_get_array(obj, "randomSwitches");
size_t count = obs_data_array_count(randomArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(randomArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
double delay = obs_data_get_double(array_obj, "delay");
const char *str = obs_data_get_string(array_obj, "str");
switcher->randomSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), delay, str);
obs_data_release(array_obj);
}
obs_data_array_release(randomArray);
}
void SceneSwitcher::setupRandomTab()
{
populateSceneSelection(ui->randomScenes, false);
populateTransitionSelection(ui->randomTransitions);
for (auto &s : switcher->randomSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeRandomSwitchName(
sceneName.c_str(), transitionName.c_str(), s.delay);
QListWidgetItem *item =
new QListWidgetItem(text, ui->randomScenesList);
item->setData(Qt::UserRole, text);
}
}

View File

@@ -1,9 +1,50 @@
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include <obs.hpp>
#include "headers/advanced-scene-switcher.hpp"
#define SECONDS_INDEX 0
#define MINUTES_INDEX 1
#define HOURS_INDEX 2
void SceneSwitcher::on_sceneRoundTripDelayUnits_currentIndexChanged(int index)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
double val = ui->sceneRoundTripSpinBox->value();
switch (switcher->sceneRoundTripUnitMultiplier) {
case 1:
val /= pow(60, index);
break;
case 60:
val /= pow(60, index - 1);
break;
case 3600:
val /= pow(60, index - 2);
break;
}
ui->sceneRoundTripSpinBox->setValue(val);
switch (index) {
case SECONDS_INDEX:
switcher->sceneRoundTripUnitMultiplier = 1;
break;
case MINUTES_INDEX:
switcher->sceneRoundTripUnitMultiplier = 60;
break;
case HOURS_INDEX:
switcher->sceneRoundTripUnitMultiplier = 3600;
break;
}
}
void SceneSwitcher::on_sceneRoundTripAdd_clicked()
{
QString scene1Name = ui->sceneRoundTripScenes1->currentText();
@@ -13,7 +54,8 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
if (scene1Name.isEmpty() || scene2Name.isEmpty())
return;
double delay = ui->sceneRoundTripSpinBox->value();
double delay = ui->sceneRoundTripSpinBox->value() *
switcher->sceneRoundTripUnitMultiplier;
if (scene1Name == scene2Name)
return;
@@ -35,8 +77,8 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
std::lock_guard<std::mutex> lock(switcher->m);
switcher->sceneRoundTripSwitches.emplace_back(
source1, source2, transition, int(delay * 1000),
(scene2Name == QString(PREVIOUS_SCENE_NAME)),
source1, source2, transition, delay,
(scene2Name == QString(previous_scene_name)),
text.toUtf8().constData());
} else {
QListWidgetItem *item = ui->sceneRoundTrips->item(idx);
@@ -47,19 +89,17 @@ void SceneSwitcher::on_sceneRoundTripAdd_clicked()
for (auto &s : switcher->sceneRoundTripSwitches) {
if (s.scene1 == source1) {
s.scene2 = source2;
s.delay = int(delay * 1000);
s.delay = delay;
s.transition = transition;
s.usePreviousScene =
(scene2Name ==
QString(PREVIOUS_SCENE_NAME));
QString(previous_scene_name));
s.sceneRoundTripStr =
text.toUtf8().constData();
break;
}
}
}
ui->sceneRoundTrips->sortItems();
}
}
@@ -89,70 +129,65 @@ void SceneSwitcher::on_sceneRoundTripRemove_clicked()
delete item;
}
void SceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
ui->autoStopScenes->setDisabled(true);
switcher->autoStopEnable = false;
} else {
ui->autoStopScenes->setDisabled(false);
switcher->autoStopEnable = true;
}
}
void SceneSwitcher::UpdateAutoStopScene(const QString &name)
{
obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData());
obs_weak_source_t *ws = obs_source_get_weak_source(scene);
switcher->autoStopScene = ws;
obs_weak_source_release(ws);
obs_source_release(scene);
}
void SceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
UpdateAutoStopScene(text);
}
void SceneSwitcher::on_sceneRoundTripSave_clicked()
{
QString directory = QFileDialog::getSaveFileName(
this, tr("Save Scene Round Trip to file ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (!directory.isEmpty()) {
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream out(&file);
for (SceneRoundTripSwitch s :
switcher->sceneRoundTripSwitches) {
out << QString::fromStdString(
GetWeakSourceName(s.scene1))
<< "\n";
if (s.usePreviousScene)
out << (PREVIOUS_SCENE_NAME) << "\n";
else
out << QString::fromStdString(
GetWeakSourceName(s.scene2))
<< "\n";
out << s.delay << "\n";
out << QString::fromStdString(s.sceneRoundTripStr)
<< "\n";
out << QString::fromStdString(
GetWeakSourceName(s.transition))
<< "\n";
if (directory.isEmpty())
return;
QFile file(directory);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
obs_data_t *obj = obs_data_create();
switcher->saveSceneRoundTripSwitches(obj);
obs_data_save_json(obj, file.fileName().toUtf8().constData());
obs_data_release(obj);
}
// to be removed once support for old file format is dropped
bool oldSceneRoundTripLoad(QFile *file)
{
QTextStream in(file);
std::vector<QString> lines;
std::vector<SceneRoundTripSwitch> newSceneRoundTripSwitch;
while (!in.atEnd()) {
QString line = in.readLine();
lines.push_back(line);
if (lines.size() == 5) {
OBSWeakSource scene1 = GetWeakSourceByQString(lines[0]);
OBSWeakSource scene2 = GetWeakSourceByQString(lines[1]);
OBSWeakSource transition =
GetWeakTransitionByQString(lines[4]);
if (WeakSourceValid(scene1) &&
((lines[1] == QString(previous_scene_name)) ||
(WeakSourceValid(scene2))) &&
WeakSourceValid(transition)) {
newSceneRoundTripSwitch.emplace_back(
SceneRoundTripSwitch(
GetWeakSourceByQString(lines[0]),
GetWeakSourceByQString(lines[1]),
GetWeakTransitionByQString(
lines[4]),
lines[2].toDouble() / 1000.,
(lines[1] ==
QString(previous_scene_name)),
lines[3].toStdString()));
}
lines.clear();
}
}
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
return false;
switcher->sceneRoundTripSwitches.clear();
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
return true;
}
void SceneSwitcher::on_sceneRoundTripLoad_clicked()
@@ -162,65 +197,45 @@ void SceneSwitcher::on_sceneRoundTripLoad_clicked()
QString directory = QFileDialog::getOpenFileName(
this, tr("Select a file to read Scene Round Trip from ..."),
QDir::currentPath(), tr("Text files (*.txt)"));
if (!directory.isEmpty()) {
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
if (directory.isEmpty())
return;
QTextStream in(&file);
std::vector<QString> lines;
QFile file(directory);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
std::vector<SceneRoundTripSwitch> newSceneRoundTripSwitch;
obs_data_t *obj = obs_data_create_from_json_file(
file.fileName().toUtf8().constData());
while (!in.atEnd()) {
QString line = in.readLine();
lines.push_back(line);
if (lines.size() == 5) {
OBSWeakSource scene1 =
GetWeakSourceByQString(lines[0]);
OBSWeakSource scene2 =
GetWeakSourceByQString(lines[1]);
OBSWeakSource transition =
GetWeakTransitionByQString(lines[4]);
if (WeakSourceValid(scene1) &&
((lines[1] ==
QString(PREVIOUS_SCENE_NAME)) ||
(WeakSourceValid(scene2))) &&
WeakSourceValid(transition)) {
newSceneRoundTripSwitch.emplace_back(
SceneRoundTripSwitch(
GetWeakSourceByQString(
lines[0]),
GetWeakSourceByQString(
lines[1]),
GetWeakTransitionByQString(
lines[4]),
lines[2].toInt(),
(lines[1] ==
QString(PREVIOUS_SCENE_NAME)),
lines[3].toStdString()));
}
lines.clear();
}
}
if (lines.size() != 0 || newSceneRoundTripSwitch.size() == 0)
return;
switcher->sceneRoundTripSwitches.clear();
ui->sceneRoundTrips->clear();
switcher->sceneRoundTripSwitches = newSceneRoundTripSwitch;
for (SceneRoundTripSwitch s :
switcher->sceneRoundTripSwitches) {
QListWidgetItem *item = new QListWidgetItem(
QString::fromStdString(s.sceneRoundTripStr),
ui->sceneRoundTrips);
item->setData(
Qt::UserRole,
QString::fromStdString(s.sceneRoundTripStr));
if (!obj) {
QMessageBox Msgbox;
QString txt =
"Advanced Scene Switcher failed to import settings!\n";
txt += "Falling back to old format.";
Msgbox.setText(txt);
Msgbox.exec();
if (oldSceneRoundTripLoad(&file)) {
QString txt =
"Advanced Scene Switcher settings imported successfully!\n";
txt += "Please resave settings as support for old format will be dropped in next version.";
Msgbox.setText(txt);
Msgbox.exec();
close();
} else {
Msgbox.setText(
"Advanced Scene Switcher failed to import settings!");
Msgbox.exec();
}
return;
}
switcher->loadSceneRoundTripSwitches(obj);
obs_data_release(obj);
QMessageBox Msgbox;
Msgbox.setText(
"Advanced Scene Switcher settings imported successfully!");
Msgbox.exec();
close();
}
void SwitcherData::checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
@@ -234,11 +249,18 @@ void SwitcherData::checkSceneRoundTrip(bool &match, OBSWeakSource &scene,
for (SceneRoundTripSwitch &s : sceneRoundTripSwitches) {
if (s.scene1 == ws) {
sceneRoundTripActive = true;
int dur = s.delay - interval;
int dur = s.delay * 1000 - interval;
if (dur > 0) {
waitScene = currentSource;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher sequence sleep %d",
dur);
cv.wait_for(lock,
std::chrono::milliseconds(dur));
waitScene = nullptr;
}
obs_source_t *currentSource2 =
obs_frontend_get_current_scene();
@@ -276,15 +298,15 @@ void SceneSwitcher::on_sceneRoundTrips_currentRowChanged(int idx)
std::string scene2 = GetWeakSourceName(s.scene2);
std::string transitionName =
GetWeakSourceName(s.transition);
int delay = s.delay;
double delay = s.delay /
switcher->sceneRoundTripUnitMultiplier;
ui->sceneRoundTripScenes1->setCurrentText(
scene1.c_str());
ui->sceneRoundTripScenes2->setCurrentText(
scene2.c_str());
ui->sceneRoundTripTransitions->setCurrentText(
transitionName.c_str());
ui->sceneRoundTripSpinBox->setValue((double)delay /
1000);
ui->sceneRoundTripSpinBox->setValue(delay);
break;
}
}
@@ -309,17 +331,170 @@ int SceneSwitcher::SceneRoundTripFindByData(const QString &scene1)
return idx;
}
void SwitcherData::autoStopStreamAndRecording()
void SceneSwitcher::on_sceneRoundTripUp_clicked()
{
obs_source_t *currentSource = obs_frontend_get_current_scene();
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
int index = ui->sceneRoundTrips->currentRow();
if (index != -1 && index != 0) {
ui->sceneRoundTrips->insertItem(
index - 1, ui->sceneRoundTrips->takeItem(index));
ui->sceneRoundTrips->setCurrentRow(index - 1);
if (ws && autoStopScene == ws) {
if (obs_frontend_streaming_active())
obs_frontend_streaming_stop();
if (obs_frontend_recording_active())
obs_frontend_recording_stop();
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
switcher->sceneRoundTripSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_sceneRoundTripDown_clicked()
{
int index = ui->sceneRoundTrips->currentRow();
if (index != -1 && index != ui->sceneRoundTrips->count() - 1) {
ui->sceneRoundTrips->insertItem(
index + 1, ui->sceneRoundTrips->takeItem(index));
ui->sceneRoundTrips->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->sceneRoundTripSwitches.begin() + index,
switcher->sceneRoundTripSwitches.begin() + index + 1);
}
}
void SwitcherData::saveSceneRoundTripSwitches(obs_data_t *obj)
{
obs_data_array_t *sceneRoundTripArray = obs_data_array_create();
for (SceneRoundTripSwitch &s : switcher->sceneRoundTripSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && (s.usePreviousScene || source2) && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "sceneRoundTripScene1",
sceneName1);
obs_data_set_string(array_obj, "sceneRoundTripScene2",
s.usePreviousScene
? previous_scene_name
: sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_double(array_obj, "delay", s.delay);
obs_data_set_string(array_obj, "sceneRoundTripStr",
s.sceneRoundTripStr.c_str());
obs_data_array_push_back(sceneRoundTripArray,
array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneRoundTrip", sceneRoundTripArray);
obs_data_array_release(sceneRoundTripArray);
}
void SwitcherData::loadSceneRoundTripSwitches(obs_data_t *obj)
{
switcher->sceneRoundTripSwitches.clear();
obs_data_array_t *sceneRoundTripArray =
obs_data_get_array(obj, "sceneRoundTrip");
size_t count = obs_data_array_count(sceneRoundTripArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneRoundTripArray, i);
const char *scene1 =
obs_data_get_string(array_obj, "sceneRoundTripScene1");
const char *scene2 =
obs_data_get_string(array_obj, "sceneRoundTripScene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
double delay = 0;
// To be removed in future version
// to be compatible with older versions
if (!obs_data_has_user_value(array_obj, "delay")) {
delay = double(obs_data_get_int(array_obj,
"sceneRoundTripDelay"));
delay = delay * 1000. +
double(obs_data_get_int(
array_obj, "sceneRoundTripDelayMs"));
delay /= 1000.;
} else {
delay = obs_data_get_double(array_obj, "delay");
}
std::string str = MakeSceneRoundTripSwitchName(
scene1, scene2, transition, delay)
.toUtf8()
.constData();
const char *sceneRoundTripStr = str.c_str();
switcher->sceneRoundTripSwitches.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition), delay,
(strcmp(scene2, previous_scene_name) == 0),
sceneRoundTripStr);
obs_data_release(array_obj);
}
obs_data_array_release(sceneRoundTripArray);
}
void SceneSwitcher::setupSequenceTab()
{
populateSceneSelection(ui->sceneRoundTripScenes1, false);
populateSceneSelection(ui->sceneRoundTripScenes2, true);
populateTransitionSelection(ui->sceneRoundTripTransitions);
double smallestDelay = double(switcher->interval) / 1000;
for (auto &s : switcher->sceneRoundTripSwitches) {
std::string sceneName1 = GetWeakSourceName(s.scene1);
std::string sceneName2 = (s.usePreviousScene)
? previous_scene_name
: GetWeakSourceName(s.scene2);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSceneRoundTripSwitchName(
sceneName1.c_str(), sceneName2.c_str(),
transitionName.c_str(), s.delay);
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneRoundTrips);
item->setData(Qt::UserRole, text);
if (s.delay < smallestDelay)
smallestDelay = s.delay;
}
(smallestDelay * 1000 < switcher->interval)
? ui->intervalWarning->setVisible(true)
: ui->intervalWarning->setVisible(false);
ui->sceneRoundTripDelayUnits->addItem("seconds");
ui->sceneRoundTripDelayUnits->addItem("minutes");
ui->sceneRoundTripDelayUnits->addItem("hours");
switch (switcher->sceneRoundTripUnitMultiplier) {
case 1:
ui->sceneRoundTripDelayUnits->setCurrentIndex(0);
break;
case 60:
ui->sceneRoundTripDelayUnits->setCurrentIndex(1);
break;
case 3600:
ui->sceneRoundTripDelayUnits->setCurrentIndex(2);
break;
default:
break;
}
obs_source_release(currentSource);
obs_weak_source_release(ws);
}

View File

@@ -162,6 +162,11 @@ void SwitcherData::setDefaultSceneTransitions()
//This might cancel the current transition
//There is no way to be sure when the previous transition finished
obs_frontend_set_current_transition(transition);
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher default transition set");
obs_source_release(transition);
break;
}
@@ -264,6 +269,19 @@ void SceneSwitcher::on_defaultTransitions_currentRowChanged(int idx)
}
}
void SceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
{
if (loading)
return;
std::lock_guard<std::mutex> lock(switcher->m);
if (!state) {
switcher->tansitionOverrideOverride = false;
} else {
switcher->tansitionOverrideOverride = true;
}
}
obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
obs_weak_source_t *scene2)
{
@@ -277,3 +295,221 @@ obs_weak_source_t *getNextTransition(obs_weak_source_t *scene1,
}
return ws;
}
void overwriteTransitionOverride(obs_weak_source_t *sceneWs,
obs_source_t *transition, transitionData &td)
{
obs_source_t *scene = obs_weak_source_get_source(sceneWs);
obs_data_t *data = obs_source_get_private_settings(scene);
td.name = obs_data_get_string(data, "transition");
td.duration = obs_data_get_int(data, "duration");
const char *name = obs_source_get_name(transition);
int duration = obs_frontend_get_transition_duration();
obs_data_set_string(data, "transition", name);
obs_data_set_int(data, "transition_duration", duration);
obs_data_release(data);
obs_source_release(scene);
}
void restoreTransitionOverride(obs_source_t *scene, transitionData td)
{
obs_data_t *data = obs_source_get_private_settings(scene);
obs_data_set_string(data, "transition", td.name.c_str());
obs_data_set_int(data, "transition_duration", td.duration);
obs_data_release(data);
}
void setNextTransition(OBSWeakSource &targetScene, obs_source_t *currentSource,
OBSWeakSource &transition,
bool &transitionOverrideOverride, transitionData &td)
{
obs_weak_source_t *currentScene =
obs_source_get_weak_source(currentSource);
obs_weak_source_t *nextTransitionWs =
getNextTransition(currentScene, targetScene);
obs_weak_source_release(currentScene);
obs_source_t *nextTransition = nullptr;
if (nextTransitionWs) {
nextTransition = obs_weak_source_get_source(nextTransitionWs);
} else if (transition) {
nextTransition = obs_weak_source_get_source(transition);
}
if (nextTransition) {
obs_frontend_set_current_transition(nextTransition);
}
if (transitionOverrideOverride)
overwriteTransitionOverride(targetScene, nextTransition, td);
obs_weak_source_release(nextTransitionWs);
obs_source_release(nextTransition);
}
void SwitcherData::saveSceneTransitions(obs_data_t *obj)
{
obs_data_array_t *sceneTransitionsArray = obs_data_array_create();
for (SceneTransition &s : switcher->sceneTransitions) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source1 = obs_weak_source_get_source(s.scene1);
obs_source_t *source2 = obs_weak_source_get_source(s.scene2);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source1 && source2 && transition) {
const char *sceneName1 = obs_source_get_name(source1);
const char *sceneName2 = obs_source_get_name(source2);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "Scene1", sceneName1);
obs_data_set_string(array_obj, "Scene2", sceneName2);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "Str",
s.sceneTransitionStr.c_str());
obs_data_array_push_back(sceneTransitionsArray,
array_obj);
obs_source_release(source1);
obs_source_release(source2);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "sceneTransitions", sceneTransitionsArray);
obs_data_array_release(sceneTransitionsArray);
obs_data_array_t *defaultTransitionsArray = obs_data_array_create();
for (DefaultSceneTransition &s : switcher->defaultSceneTransitions) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "Scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "Str",
s.sceneTransitionStr.c_str());
obs_data_array_push_back(defaultTransitionsArray,
array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "defaultTransitions", defaultTransitionsArray);
obs_data_array_release(defaultTransitionsArray);
obs_data_set_bool(obj, "tansitionOverrideOverride",
switcher->tansitionOverrideOverride);
}
void SwitcherData::loadSceneTransitions(obs_data_t *obj)
{
switcher->sceneTransitions.clear();
obs_data_array_t *sceneTransitionsArray =
obs_data_get_array(obj, "sceneTransitions");
size_t count = obs_data_array_count(sceneTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(sceneTransitionsArray, i);
const char *scene1 = obs_data_get_string(array_obj, "Scene1");
const char *scene2 = obs_data_get_string(array_obj, "Scene2");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *sceneTransitionsStr =
obs_data_get_string(array_obj, "Str");
switcher->sceneTransitions.emplace_back(
GetWeakSourceByName(scene1),
GetWeakSourceByName(scene2),
GetWeakTransitionByName(transition),
sceneTransitionsStr);
obs_data_release(array_obj);
}
obs_data_array_release(sceneTransitionsArray);
switcher->defaultSceneTransitions.clear();
obs_data_array_t *defaultTransitionsArray =
obs_data_get_array(obj, "defaultTransitions");
count = obs_data_array_count(defaultTransitionsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(defaultTransitionsArray, i);
const char *scene = obs_data_get_string(array_obj, "Scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *sceneTransitionsStr =
obs_data_get_string(array_obj, "Str");
switcher->defaultSceneTransitions.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition),
sceneTransitionsStr);
obs_data_release(array_obj);
}
obs_data_array_release(defaultTransitionsArray);
switcher->tansitionOverrideOverride =
obs_data_get_bool(obj, "tansitionOverrideOverride");
}
void SceneSwitcher::setupTransitionsTab()
{
populateSceneSelection(ui->transitionsScene1, false);
populateSceneSelection(ui->transitionsScene2, false);
populateSceneSelection(ui->defaultTransitionsScene, false);
populateTransitionSelection(ui->transitionsTransitions);
populateTransitionSelection(ui->defaultTransitionsTransitions);
for (auto &s : switcher->sceneTransitions) {
std::string sceneName1 = GetWeakSourceName(s.scene1);
std::string sceneName2 = GetWeakSourceName(s.scene2);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSceneTransitionName(sceneName1.c_str(),
sceneName2.c_str(),
transitionName.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->sceneTransitions);
item->setData(Qt::UserRole, text);
}
//(transitionDurationLongerThanInterval(switcher->interval))
// ? ui->transitionWarning->setVisible(true)
// : ui->transitionWarning->setVisible(false);
for (auto &s : switcher->defaultSceneTransitions) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeDefaultSceneTransitionName(
sceneName.c_str(), transitionName.c_str());
QListWidgetItem *item =
new QListWidgetItem(text, ui->defaultTransitions);
item->setData(Qt::UserRole, text);
}
ui->transitionOverridecheckBox->setChecked(
switcher->tansitionOverrideOverride);
}

View File

@@ -1,3 +1,5 @@
#include <QTimer>
#include "headers/advanced-scene-switcher.hpp"
void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
@@ -15,6 +17,12 @@ void SwitcherData::checkScreenRegionSwitch(bool &match, OBSWeakSource &scene,
scene = s.scene;
transition = s.transition;
minRegionSize = regionSize;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher region match");
break;
}
}
}
@@ -78,8 +86,6 @@ void SceneSwitcher::on_screenRegionAdd_clicked()
}
}
}
ui->screenRegions->sortItems();
}
}
@@ -155,3 +161,124 @@ int SceneSwitcher::ScreenRegionFindByData(const QString &region)
return idx;
}
void SceneSwitcher::on_screenRegionUp_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != 0) {
ui->screenRegions->insertItem(
index - 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_screenRegionDown_clicked()
{
int index = ui->screenRegions->currentRow();
if (index != -1 && index != ui->screenRegions->count() - 1) {
ui->screenRegions->insertItem(
index + 1, ui->screenRegions->takeItem(index));
ui->screenRegions->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->screenRegionSwitches.begin() + index,
switcher->screenRegionSwitches.begin() + index + 1);
}
}
void SwitcherData::saveScreenRegionSwitches(obs_data_t *obj)
{
obs_data_array_t *screenRegionArray = obs_data_array_create();
for (ScreenRegionSwitch &s : switcher->screenRegionSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "screenRegionScene",
sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "minX", s.minX);
obs_data_set_int(array_obj, "minY", s.minY);
obs_data_set_int(array_obj, "maxX", s.maxX);
obs_data_set_int(array_obj, "maxY", s.maxY);
obs_data_set_string(array_obj, "screenRegionStr",
s.regionStr.c_str());
obs_data_array_push_back(screenRegionArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "screenRegion", screenRegionArray);
obs_data_array_release(screenRegionArray);
}
void SwitcherData::loadScreenRegionSwitches(obs_data_t *obj)
{
switcher->screenRegionSwitches.clear();
obs_data_array_t *screenRegionArray =
obs_data_get_array(obj, "screenRegion");
size_t count = obs_data_array_count(screenRegionArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(screenRegionArray, i);
const char *scene =
obs_data_get_string(array_obj, "screenRegionScene");
const char *transition =
obs_data_get_string(array_obj, "transition");
int minX = obs_data_get_int(array_obj, "minX");
int minY = obs_data_get_int(array_obj, "minY");
int maxX = obs_data_get_int(array_obj, "maxX");
int maxY = obs_data_get_int(array_obj, "maxY");
std::string regionStr =
obs_data_get_string(array_obj, "screenRegionStr");
switcher->screenRegionSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), minX, minY, maxX,
maxY, regionStr);
obs_data_release(array_obj);
}
obs_data_array_release(screenRegionArray);
}
void SceneSwitcher::setupRegionTab()
{
populateSceneSelection(ui->screenRegionScenes, false);
populateTransitionSelection(ui->screenRegionsTransitions);
for (auto &s : switcher->screenRegionSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeScreenRegionSwitchName(
sceneName.c_str(), transitionName.c_str(), s.minX,
s.minY, s.maxX, s.maxY);
QListWidgetItem *item =
new QListWidgetItem(text, ui->screenRegions);
item->setData(Qt::UserRole, s.regionStr.c_str());
}
// screen region cursor position
QTimer *screenRegionTimer = new QTimer(this);
connect(screenRegionTimer, SIGNAL(timeout()), this,
SLOT(updateScreenRegionCursorPos()));
screenRegionTimer->start(1000);
}

View File

@@ -1,5 +1,16 @@
#include "headers/advanced-scene-switcher.hpp"
static std::vector<std::pair<std::string, timeTrigger>> triggerTable = {
{"On any day", timeTrigger::ANY_DAY},
{"Mondays", timeTrigger::MONDAY},
{"Tuesdays", timeTrigger::TUSEDAY},
{"Wednesdays", timeTrigger::WEDNESDAY},
{"Thursdays", timeTrigger::THURSDAY},
{"Fridays", timeTrigger::FRIDAY},
{"Saturdays", timeTrigger::SATURDAY},
{"Sundays", timeTrigger::SUNDAY},
{"Atfer streaming/recording start", timeTrigger::LIVE}};
void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx)
{
if (loading)
@@ -18,6 +29,7 @@ void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx)
QString transitionName =
GetWeakSourceName(s.transition).c_str();
ui->timeScenes->setCurrentText(sceneName);
ui->timeTrigger->setCurrentIndex(s.trigger);
ui->timeEdit->setTime(s.time);
ui->timeTransitions->setCurrentText(transitionName);
break;
@@ -25,9 +37,10 @@ void SceneSwitcher::on_timeSwitches_currentRowChanged(int idx)
}
}
int SceneSwitcher::timeFindByData(const QString &timeStr)
int SceneSwitcher::timeFindByData(const timeTrigger &trigger, const QTime &time)
{
QRegExp rx("At " + timeStr + " switch to .*");
QRegExp rx(MakeTimeSwitchName(QStringLiteral(".*"),
QStringLiteral(".*"), trigger, time));
int count = ui->timeSwitches->count();
for (int i = 0; i < count; i++) {
@@ -53,16 +66,26 @@ void SceneSwitcher::on_timeAdd_clicked()
OBSWeakSource source = GetWeakSourceByQString(sceneName);
OBSWeakSource transition = GetWeakTransitionByQString(transitionName);
QString text = MakeTimeSwitchName(sceneName, transitionName, time);
std::string triggerStr = ui->timeTrigger->currentText().toStdString();
auto it = std::find_if(
triggerTable.begin(), triggerTable.end(),
[&triggerStr](
const std::pair<std::string, timeTrigger> &element) {
return element.first == triggerStr;
});
timeTrigger trigger = it->second;
QString text =
MakeTimeSwitchName(sceneName, transitionName, trigger, time);
QVariant v = QVariant::fromValue(text);
int idx = timeFindByData(time.toString());
int idx = timeFindByData(trigger, time);
if (idx == -1) {
std::lock_guard<std::mutex> lock(switcher->m);
switcher->timeSwitches.emplace_back(
source, transition, time,
(sceneName == QString(PREVIOUS_SCENE_NAME)),
source, transition, trigger, time,
(sceneName == QString(previous_scene_name)),
text.toUtf8().constData());
QListWidgetItem *item =
@@ -71,25 +94,23 @@ void SceneSwitcher::on_timeAdd_clicked()
} else {
QListWidgetItem *item = ui->timeSwitches->item(idx);
item->setText(text);
item->setData(Qt::UserRole, v);
{
std::lock_guard<std::mutex> lock(switcher->m);
for (auto &s : switcher->timeSwitches) {
if (s.time == time) {
if (s.trigger == trigger && s.time == time) {
s.scene = source;
s.transition = transition;
s.usePreviousScene =
(sceneName ==
QString(PREVIOUS_SCENE_NAME));
QString(previous_scene_name));
s.timeSwitchStr =
text.toUtf8().constData();
;
break;
}
}
}
ui->timeSwitches->sortItems();
}
}
@@ -119,30 +140,192 @@ void SceneSwitcher::on_timeRemove_clicked()
delete item;
}
void SceneSwitcher::on_timeUp_clicked()
{
int index = ui->timeSwitches->currentRow();
if (index != -1 && index != 0) {
ui->timeSwitches->insertItem(index - 1,
ui->timeSwitches->takeItem(index));
ui->timeSwitches->setCurrentRow(index - 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->timeSwitches.begin() + index,
switcher->timeSwitches.begin() + index - 1);
}
}
void SceneSwitcher::on_timeDown_clicked()
{
int index = ui->timeSwitches->currentRow();
if (index != -1 && index != ui->timeSwitches->count() - 1) {
ui->timeSwitches->insertItem(index + 1,
ui->timeSwitches->takeItem(index));
ui->timeSwitches->setCurrentRow(index + 1);
std::lock_guard<std::mutex> lock(switcher->m);
iter_swap(switcher->timeSwitches.begin() + index,
switcher->timeSwitches.begin() + index + 1);
}
}
bool timesAreInInterval(QTime &time1, QTime &time2, int &interval)
{
bool ret = false;
QTime validSwitchTimeWindow = time1.addMSecs(interval);
ret = time1 <= time2 && time2 <= validSwitchTimeWindow;
// check for overflow
if (!ret && validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
ret = time2 >= time1 || time2 <= validSwitchTimeWindow;
}
return ret;
}
bool checkLiveTime(TimeSwitch &s, QDateTime &start, int &interval)
{
if (start.isNull())
return false;
QDateTime now = QDateTime::currentDateTime();
QTime timePassed = QTime(0, 0).addMSecs(start.msecsTo(now));
return timesAreInInterval(s.time, timePassed, interval);
}
bool checkRegularTime(TimeSwitch &s, int &interval)
{
if (s.trigger != ANY_DAY &&
s.trigger != QDate::currentDate().dayOfWeek())
return false;
QTime now = QTime::currentTime();
return timesAreInInterval(s.time, now, interval);
}
void SwitcherData::checkTimeSwitch(bool &match, OBSWeakSource &scene,
OBSWeakSource &transition)
{
if (timeSwitches.size() == 0)
return;
QTime now = QTime::currentTime();
for (TimeSwitch &s : timeSwitches) {
QTime validSwitchTimeWindow = s.time.addMSecs(interval);
match = s.time <= now && now <= validSwitchTimeWindow;
if (!match &&
validSwitchTimeWindow.msecsSinceStartOfDay() < interval) {
// check for overflow
match = now >= s.time || now <= validSwitchTimeWindow;
}
if (s.trigger == LIVE)
match = checkLiveTime(s, liveTime, interval);
else
match = checkRegularTime(s, interval);
if (match) {
scene = (s.usePreviousScene) ? previousScene : s.scene;
transition = s.transition;
match = true;
if (verbose)
blog(LOG_INFO,
"Advanced Scene Switcher time match");
break;
}
}
}
void SwitcherData::saveTimeSwitches(obs_data_t *obj)
{
obs_data_array_t *timeArray = obs_data_array_create();
for (TimeSwitch &s : switcher->timeSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *sceneSource = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if ((s.usePreviousScene || sceneSource) && transition) {
const char *sceneName =
obs_source_get_name(sceneSource);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene",
s.usePreviousScene
? previous_scene_name
: sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_int(array_obj, "trigger", s.trigger);
obs_data_set_string(
array_obj, "time",
s.time.toString().toStdString().c_str());
obs_data_array_push_back(timeArray, array_obj);
}
obs_source_release(sceneSource);
obs_source_release(transition);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "timeSwitches", timeArray);
obs_data_array_release(timeArray);
}
void SwitcherData::loadTimeSwitches(obs_data_t *obj)
{
switcher->timeSwitches.clear();
obs_data_array_t *timeArray = obs_data_get_array(obj, "timeSwitches");
size_t count = obs_data_array_count(timeArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj = obs_data_array_item(timeArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
timeTrigger trigger =
(timeTrigger)obs_data_get_int(array_obj, "trigger");
QTime time = QTime::fromString(
obs_data_get_string(array_obj, "time"));
std::string timeSwitchStr =
MakeTimeSwitchName(scene, transition, trigger, time)
.toUtf8()
.constData();
switcher->timeSwitches.emplace_back(
GetWeakSourceByName(scene),
GetWeakTransitionByName(transition), trigger, time,
(strcmp(scene, previous_scene_name) == 0),
timeSwitchStr);
obs_data_release(array_obj);
}
obs_data_array_release(timeArray);
}
void SceneSwitcher::setupTimeTab()
{
populateSceneSelection(ui->timeScenes, true);
populateTransitionSelection(ui->timeTransitions);
for (auto t : triggerTable) {
ui->timeTrigger->addItem(t.first.c_str());
}
// assuming the streaming / recording entry is always last
ui->timeTrigger->setItemData(
(int)triggerTable.size() - 1,
"The time relative to the start of streaming / recording will be used",
Qt::ToolTipRole);
for (auto &s : switcher->timeSwitches) {
std::string sceneName = (s.usePreviousScene)
? previous_scene_name
: GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString listText = MakeTimeSwitchName(sceneName.c_str(),
transitionName.c_str(),
s.trigger, s.time);
QListWidgetItem *item =
new QListWidgetItem(listText, ui->timeSwitches);
item->setData(Qt::UserRole, listText);
}
}

1002
src/volume-control.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -312,3 +312,121 @@ void SwitcherData::checkWindowTitleSwitch(bool &match, OBSWeakSource &scene,
}
}
}
void SwitcherData::saveWindowTitleSwitches(obs_data_t *obj)
{
obs_data_array_t *windowTitleArray = obs_data_array_create();
for (WindowSceneSwitch &s : switcher->windowSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_source_t *source = obs_weak_source_get_source(s.scene);
obs_source_t *transition =
obs_weak_source_get_source(s.transition);
if (source && transition) {
const char *sceneName = obs_source_get_name(source);
const char *transitionName =
obs_source_get_name(transition);
obs_data_set_string(array_obj, "scene", sceneName);
obs_data_set_string(array_obj, "transition",
transitionName);
obs_data_set_string(array_obj, "window_title",
s.window.c_str());
obs_data_set_bool(array_obj, "fullscreen",
s.fullscreen);
obs_data_set_bool(array_obj, "focus", s.focus);
obs_data_array_push_back(windowTitleArray, array_obj);
obs_source_release(source);
obs_source_release(transition);
}
obs_data_release(array_obj);
}
obs_data_set_array(obj, "switches", windowTitleArray);
obs_data_array_release(windowTitleArray);
obs_data_array_t *ignoreWindowsArray = obs_data_array_create();
for (std::string &window : switcher->ignoreWindowsSwitches) {
obs_data_t *array_obj = obs_data_create();
obs_data_set_string(array_obj, "ignoreWindow", window.c_str());
obs_data_array_push_back(ignoreWindowsArray, array_obj);
obs_data_release(array_obj);
}
obs_data_set_array(obj, "ignoreWindows", ignoreWindowsArray);
obs_data_array_release(ignoreWindowsArray);
}
void SwitcherData::loadWindowTitleSwitches(obs_data_t *obj)
{
switcher->windowSwitches.clear();
obs_data_array_t *windowTitleArray =
obs_data_get_array(obj, "switches");
size_t count = obs_data_array_count(windowTitleArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(windowTitleArray, i);
const char *scene = obs_data_get_string(array_obj, "scene");
const char *transition =
obs_data_get_string(array_obj, "transition");
const char *window =
obs_data_get_string(array_obj, "window_title");
bool fullscreen = obs_data_get_bool(array_obj, "fullscreen");
bool focus = obs_data_get_bool(array_obj, "focus") ||
!obs_data_has_user_value(array_obj, "focus");
switcher->windowSwitches.emplace_back(
GetWeakSourceByName(scene), window,
GetWeakTransitionByName(transition), fullscreen, focus);
obs_data_release(array_obj);
}
obs_data_array_release(windowTitleArray);
switcher->ignoreWindowsSwitches.clear();
obs_data_array_t *ignoreWindowsArray =
obs_data_get_array(obj, "ignoreWindows");
count = obs_data_array_count(ignoreWindowsArray);
for (size_t i = 0; i < count; i++) {
obs_data_t *array_obj =
obs_data_array_item(ignoreWindowsArray, i);
const char *window =
obs_data_get_string(array_obj, "ignoreWindow");
switcher->ignoreWindowsSwitches.emplace_back(window);
obs_data_release(array_obj);
}
obs_data_array_release(ignoreWindowsArray);
}
void SceneSwitcher::setupTitleTab()
{
populateSceneSelection(ui->scenes, false);
populateTransitionSelection(ui->transitions);
populateWindowSelection(ui->windows);
populateWindowSelection(ui->ignoreWindowsWindows);
for (auto &s : switcher->windowSwitches) {
std::string sceneName = GetWeakSourceName(s.scene);
std::string transitionName = GetWeakSourceName(s.transition);
QString text = MakeSwitchName(sceneName.c_str(),
s.window.c_str(),
transitionName.c_str(),
s.fullscreen, s.focus);
QListWidgetItem *item = new QListWidgetItem(text, ui->switches);
item->setData(Qt::UserRole, s.window.c_str());
}
for (auto &window : switcher->ignoreWindowsSwitches) {
QString text = QString::fromStdString(window);
QListWidgetItem *item =
new QListWidgetItem(text, ui->ignoreWindows);
item->setData(Qt::UserRole, text);
}
}