mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4bb034303 | ||
|
|
78e14df620 | ||
|
|
34f07bfb07 | ||
|
|
ee74d39424 | ||
|
|
33667a1f23 | ||
|
|
7947ad2608 | ||
|
|
920c188213 | ||
|
|
28fbff1adc | ||
|
|
326617aad7 | ||
|
|
499ba7dd21 | ||
|
|
f4902933ab | ||
|
|
9aa0b31bff | ||
|
|
6d5086b971 | ||
|
|
f32f47db4b | ||
|
|
74c0cd1736 | ||
|
|
a74a952b1d | ||
|
|
5bbfef81e0 | ||
|
|
c31d1cd975 | ||
|
|
81d91a9139 |
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -161,6 +161,7 @@ jobs:
|
|||||||
pkg-config \
|
pkg-config \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
qtbase5-dev \
|
qtbase5-dev \
|
||||||
|
qtbase5-private-dev \
|
||||||
libqt5svg5-dev \
|
libqt5svg5-dev \
|
||||||
swig \
|
swig \
|
||||||
libxss-dev
|
libxss-dev
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
|||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
||||||
|
if(WIN32)
|
||||||
|
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_PLUGIN_FILES_DIR}" ISS_PLUGIN_FILES_DIR)
|
||||||
|
get_filename_component(ISS_MSVC_REDIST_HELPER_DIR "${CMAKE_PACKAGE_PREFIX}\\.." ABSOLUTE)
|
||||||
|
file(TO_NATIVE_PATH "${ISS_MSVC_REDIST_HELPER_DIR}" ISS_MSVC_REDIST_HELPER_DIR)
|
||||||
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CI/windows/setup.iss.in" "${CMAKE_CURRENT_BINARY_DIR}/CI/windows/setup.iss" @ONLY)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
get_filename_component(ISS_PLUGIN_FILES_DIR "${CMAKE_BINARY_DIR}\\..\\package" ABSOLUTE)
|
||||||
@@ -66,6 +73,7 @@ set(advanced-scene-switcher_HEADERS
|
|||||||
src/headers/switch-transitions.hpp
|
src/headers/switch-transitions.hpp
|
||||||
src/headers/switch-window.hpp
|
src/headers/switch-window.hpp
|
||||||
src/headers/switch-sequence.hpp
|
src/headers/switch-sequence.hpp
|
||||||
|
src/headers/switch-video.hpp
|
||||||
src/headers/switch-generic.hpp
|
src/headers/switch-generic.hpp
|
||||||
src/headers/version.h
|
src/headers/version.h
|
||||||
)
|
)
|
||||||
@@ -92,6 +100,7 @@ set(advanced-scene-switcher_SOURCES
|
|||||||
src/switch-random.cpp
|
src/switch-random.cpp
|
||||||
src/switch-time.cpp
|
src/switch-time.cpp
|
||||||
src/switch-audio.cpp
|
src/switch-audio.cpp
|
||||||
|
src/switch-video.cpp
|
||||||
src/switch-generic.cpp
|
src/switch-generic.cpp
|
||||||
src/curl-helper.cpp
|
src/curl-helper.cpp
|
||||||
src/volume-control.cpp
|
src/volume-control.cpp
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ AdvSceneSwitcher.generalTab.priority.windowTitle="Fenstername"
|
|||||||
AdvSceneSwitcher.generalTab.priority.media="Medien"
|
AdvSceneSwitcher.generalTab.priority.media="Medien"
|
||||||
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
||||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||||
|
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||||
|
|
||||||
; Transition Tab
|
; Transition Tab
|
||||||
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
AdvSceneSwitcher.transitionTab.title="Szenenübergänge"
|
||||||
@@ -185,6 +186,8 @@ AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Laden der Einstellungen war erfol
|
|||||||
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Szenenfolge erweitern"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEntry="Nach {{delay}} {{delayUnits}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
|
AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.help="Dieser Tab ermöglicht es automatisiert zu einer anderen Szene zu wechseln, nachdem eine Szene für die konfigurierte Zeit aktiv war.\nZum Beispiel kann so automatisch zwischen zwei Szenen hin und her gewechselt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
AdvSceneSwitcher.sceneSequenceTab.help="Dieser Tab ermöglicht es automatisiert zu einer anderen Szene zu wechseln, nachdem eine Szene für die konfigurierte Zeit aktiv war.\nZum Beispiel kann so automatisch zwischen zwei Szenen hin und her gewechselt werden.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
@@ -197,6 +200,18 @@ AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="Wenn mehrere Einträge zu
|
|||||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
|
AdvSceneSwitcher.audioTab.multiMatchfallback="... für {{duration}} wechsle zu {{scenes}} mit {{transitions}}"
|
||||||
AdvSceneSwitcher.audioTab.help="Dieser Tab ermöglicht es basierend auf der Lautstärke von Audioquellen Szenen zu wechseln.\nSo kann zum Beispiel automatisch zu einer Szene gewechselt werden, wenn die Lautstärke eines Mikrofons eine konfigurierte Schwelle überschreitet.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
AdvSceneSwitcher.audioTab.help="Dieser Tab ermöglicht es basierend auf der Lautstärke von Audioquellen Szenen zu wechseln.\nSo kann zum Beispiel automatisch zu einer Szene gewechselt werden, wenn die Lautstärke eines Mikrofons eine konfigurierte Schwelle überschreitet.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||||
|
|
||||||
|
; Video Tab
|
||||||
|
AdvSceneSwitcher.videoTab.title="Video"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshot="Screenshot für ausgewählten Eintrag erstellen"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshotHelp="Erstellt einen Screenshot für die Video Quelle des ausgewählten Eintrag und setzt diesen als Ziel Bild"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match="genau übereinstimmt mit"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match.tooltip="Eine genaue Übereinstimmung setzt voraus, dass sowohl Zielquelle als auch das Bild die gleiche Auflösung besitzen.\nZusätzlich muss jeder einzelne Pixel zwischen beiden Bildquellen übereinstimmen weshalb Bildformate, welche das Bild komprimieren (z. B. .JPG) nicht zu empfehlen sind."
|
||||||
|
AdvSceneSwitcher.videoTab.condition.differ="nicht übereinstimmt mit"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.hasNotChanged="sich nicht verändert"
|
||||||
|
AdvSceneSwitcher.videoTab.ignoreInactiveSource="außer Video Quelle ist inaktiv"
|
||||||
|
AdvSceneSwitcher.videoTab.entry="Wenn {{videoSources}} {{condition}} {{filePath}} {{browseButton}} für {{duration}} wechsle zu {{scenes}} mit {{transitions}} {{ignoreInactiveSource}}"
|
||||||
|
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>Dieser Tab ermöglicht es basierend auf der Videoausgabe von Quellen scenen zu wechseln.<br/>Für eine noch bessere Implementierung dieser Funktionalität siehe <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a>.<br/><br/>Klicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen..</p></body></html>"
|
||||||
|
|
||||||
; Scene Group Tab
|
; Scene Group Tab
|
||||||
AdvSceneSwitcher.sceneGroupTab.title="Szenengruppe"
|
AdvSceneSwitcher.sceneGroupTab.title="Szenengruppe"
|
||||||
AdvSceneSwitcher.sceneGroupTab.list="Szenengruppen"
|
AdvSceneSwitcher.sceneGroupTab.list="Szenengruppen"
|
||||||
@@ -232,7 +247,10 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Streamen stop
|
|||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Quelle stumm schalten"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Audio Quelle stumm schalten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="Audio Quelle aktiv schalten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="Szenenwechsler starten"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="Szenenwechsler stoppen"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
|
AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} "
|
||||||
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."
|
AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen."
|
||||||
|
|
||||||
@@ -249,9 +267,11 @@ AdvSceneSwitcher.browse="Öffnen"
|
|||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--Szene auswählen--"
|
AdvSceneSwitcher.selectScene="--Szene auswählen--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
|
AdvSceneSwitcher.selectPreviousScene="Vorhergehende Szene"
|
||||||
|
AdvSceneSwitcher.currentTransition="Aktueller Szenenübergang"
|
||||||
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
|
AdvSceneSwitcher.selectTransition="--Szenenübergang auswählen--"
|
||||||
AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
|
AdvSceneSwitcher.selectWindow="--Fenster auswählen--"
|
||||||
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"
|
AdvSceneSwitcher.selectAudioSource="--Audio Quelle auswählen--"
|
||||||
|
AdvSceneSwitcher.selectVideoSource="--Video Quelle auswählen--"
|
||||||
AdvSceneSwitcher.selectMediaSource="--Medien Quelle auswählen--"
|
AdvSceneSwitcher.selectMediaSource="--Medien Quelle auswählen--"
|
||||||
AdvSceneSwitcher.selectProcess="--Prozess auswählen--"
|
AdvSceneSwitcher.selectProcess="--Prozess auswählen--"
|
||||||
AdvSceneSwitcher.enterPath="--Pfad eingeben--"
|
AdvSceneSwitcher.enterPath="--Pfad eingeben--"
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ AdvSceneSwitcher.generalTab.priority.windowTitle="Window Title"
|
|||||||
AdvSceneSwitcher.generalTab.priority.media="Media"
|
AdvSceneSwitcher.generalTab.priority.media="Media"
|
||||||
AdvSceneSwitcher.generalTab.priority.time="Time"
|
AdvSceneSwitcher.generalTab.priority.time="Time"
|
||||||
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
AdvSceneSwitcher.generalTab.priority.audio="Audio"
|
||||||
|
AdvSceneSwitcher.generalTab.priority.video="Video"
|
||||||
|
|
||||||
; Transition Tab
|
; Transition Tab
|
||||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||||
@@ -186,6 +187,8 @@ AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
|
|||||||
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
|
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
|
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
|
||||||
|
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} {{delayUnits}} switch to {{scenes}} using {{transitions}}"
|
||||||
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
; Audio Tab
|
; Audio Tab
|
||||||
@@ -197,6 +200,18 @@ AdvSceneSwitcher.audioTab.multiMatchfallbackCondition="If multiple entries match
|
|||||||
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
|
AdvSceneSwitcher.audioTab.multiMatchfallback="... for {{duration}} switch to {{scenes}} using {{transitions}}"
|
||||||
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
|
AdvSceneSwitcher.audioTab.help="This tab will allow you to switch scenes based on the volume of sources.\nFor example, you could automatically switch to a different scene if the volume of your microphone reaches a certain threshold.\n\nClick on the highlighted plus symbol to continue."
|
||||||
|
|
||||||
|
; Video Tab
|
||||||
|
AdvSceneSwitcher.videoTab.title="Video"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshot="Get screenshot for selected entry"
|
||||||
|
AdvSceneSwitcher.videoTab.getScreenshotHelp="Get Screenshot of the currently selected entry's video source and automatically set it as the target image"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match="exactly matches"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.match.tooltip="An exact match requires the target and the source image to be of the same resolution.\nAdditionally every single pixel needs to match, which is why use of image formats which use compression (e.g. .JPG) is not recommended!"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.differ="does not match"
|
||||||
|
AdvSceneSwitcher.videoTab.condition.hasNotChanged="has not changed"
|
||||||
|
AdvSceneSwitcher.videoTab.ignoreInactiveSource="unless source is inactive"
|
||||||
|
AdvSceneSwitcher.videoTab.entry="When {{videoSources}} {{condition}} {{filePath}} {{browseButton}} for {{duration}} switch to {{scenes}} using {{transitions}} {{ignoreInactiveSource}}"
|
||||||
|
AdvSceneSwitcher.videoTab.help="<html><head/><body><p>This tab will allow you to switch scenes based on the current video output of selected sources.<br/>Make sure to check out <a href=\"https://obsproject.com/forum/resources/pixel-match-switcher.1202/\"><span style=\" text-decoration: underline; color:#268bd2;\">Pixel Match Switcher</span></a> for an even better implementation of this functionality.<br/><br/> Click on the highlighted plus symbol to continue.</p></body></html>"
|
||||||
|
|
||||||
; Scene Group Tab
|
; Scene Group Tab
|
||||||
AdvSceneSwitcher.sceneGroupTab.title="Scene Group"
|
AdvSceneSwitcher.sceneGroupTab.title="Scene Group"
|
||||||
AdvSceneSwitcher.sceneGroupTab.list="Scene Groups"
|
AdvSceneSwitcher.sceneGroupTab.list="Scene Groups"
|
||||||
@@ -233,6 +248,9 @@ AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="start stream
|
|||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="start replay buffer"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="start replay buffer"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="stop replay buffer"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="stop replay buffer"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source"
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource="unmute source"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher="start the scene switcher"
|
||||||
|
AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher="stop the scene switcher"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}"
|
AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}"
|
||||||
AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming."
|
AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming."
|
||||||
|
|
||||||
@@ -248,9 +266,11 @@ AdvSceneSwitcher.browse="Browse"
|
|||||||
|
|
||||||
AdvSceneSwitcher.selectScene="--select scene--"
|
AdvSceneSwitcher.selectScene="--select scene--"
|
||||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||||
|
AdvSceneSwitcher.currentTransition="Current Transition"
|
||||||
AdvSceneSwitcher.selectTransition="--select transition--"
|
AdvSceneSwitcher.selectTransition="--select transition--"
|
||||||
AdvSceneSwitcher.selectWindow="--select window--"
|
AdvSceneSwitcher.selectWindow="--select window--"
|
||||||
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
AdvSceneSwitcher.selectAudioSource="--select audio source--"
|
||||||
|
AdvSceneSwitcher.selectVideoSource="--select video source--"
|
||||||
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
AdvSceneSwitcher.selectMediaSource="--select media source--"
|
||||||
AdvSceneSwitcher.selectProcess="--select process--"
|
AdvSceneSwitcher.selectProcess="--select process--"
|
||||||
AdvSceneSwitcher.enterPath="--enter path--"
|
AdvSceneSwitcher.enterPath="--enter path--"
|
||||||
|
|||||||
@@ -2824,6 +2824,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="sequenceEdit">
|
||||||
|
<property name="text">
|
||||||
|
<string>AdvSceneSwitcher.sceneSequenceTab.extendEdit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_8">
|
<spacer name="horizontalSpacer_8">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
@@ -3070,6 +3084,191 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="videoTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>AdvSceneSwitcher.videoTab.title</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_29">
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_22">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QListWidget" name="videoSwitches">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="videoHelp">
|
||||||
|
<property name="text">
|
||||||
|
<string>AdvSceneSwitcher.videoTab.help</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_58">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="videoAdd">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="themeID" stdset="0">
|
||||||
|
<string notr="true">addIconSmall</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="videoRemove">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="themeID" stdset="0">
|
||||||
|
<string notr="true">removeIconSmall</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_124">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>5</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_43">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_125">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>5</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="videoUp">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>../../../forms/images/up.svg</normaloff>../../../forms/images/up.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="themeID" stdset="0">
|
||||||
|
<string>upArrowIconSmall</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="videoDown">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>22</width>
|
||||||
|
<height>22</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>../../../forms/images/down.svg</normaloff>../../../forms/images/down.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="themeID" stdset="0">
|
||||||
|
<string>downArrowIconSmall</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="getScreenshot">
|
||||||
|
<property name="text">
|
||||||
|
<string>AdvSceneSwitcher.videoTab.getScreenshot</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_62">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="sceneGroupTab">
|
<widget class="QWidget" name="sceneGroupTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>AdvSceneSwitcher.sceneGroupTab.title</string>
|
<string>AdvSceneSwitcher.sceneGroupTab.title</string>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ void AdvSceneSwitcher::loadUI()
|
|||||||
setupFileTab();
|
setupFileTab();
|
||||||
setupTimeTab();
|
setupTimeTab();
|
||||||
setupAudioTab();
|
setupAudioTab();
|
||||||
|
setupVideoTab();
|
||||||
setupSceneGroupTab();
|
setupSceneGroupTab();
|
||||||
setupTriggerTab();
|
setupTriggerTab();
|
||||||
|
|
||||||
@@ -92,8 +93,9 @@ bool AdvSceneSwitcher::DisplayMessage(QString msg, bool question)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSelectionEntry(QComboBox *sel, const char *description,
|
void AdvSceneSwitcher::addSelectionEntry(QComboBox *sel,
|
||||||
const char *tooltip = "")
|
const char *description,
|
||||||
|
bool selectable, const char *tooltip)
|
||||||
{
|
{
|
||||||
sel->addItem(description);
|
sel->addItem(description);
|
||||||
|
|
||||||
@@ -106,26 +108,28 @@ void addSelectionEntry(QComboBox *sel, const char *description,
|
|||||||
QModelIndex firstIndex =
|
QModelIndex firstIndex =
|
||||||
model->index(0, sel->modelColumn(), sel->rootModelIndex());
|
model->index(0, sel->modelColumn(), sel->rootModelIndex());
|
||||||
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
|
QStandardItem *firstItem = model->itemFromIndex(firstIndex);
|
||||||
firstItem->setSelectable(false);
|
if (!selectable) {
|
||||||
firstItem->setEnabled(false);
|
firstItem->setSelectable(false);
|
||||||
|
firstItem->setEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
||||||
bool addSceneGroup,
|
bool addSceneGroup,
|
||||||
bool addSelect,
|
bool addSelect,
|
||||||
std::string selectText)
|
std::string selectText,
|
||||||
|
bool selectable)
|
||||||
{
|
{
|
||||||
sel->clear();
|
|
||||||
|
|
||||||
if (addSelect) {
|
if (addSelect) {
|
||||||
if (selectText.empty()) {
|
if (selectText.empty()) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectScene"),
|
obs_module_text("AdvSceneSwitcher.selectScene"),
|
||||||
|
selectable,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
} else {
|
} else {
|
||||||
addSelectionEntry(sel, selectText.c_str());
|
addSelectionEntry(sel, selectText.c_str(), selectable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,12 +154,20 @@ void AdvSceneSwitcher::populateSceneSelection(QComboBox *sel, bool addPrevious,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
|
void AdvSceneSwitcher::populateTransitionSelection(QComboBox *sel,
|
||||||
bool addSelect)
|
bool addCurrent,
|
||||||
|
bool addSelect,
|
||||||
|
bool selectable)
|
||||||
{
|
{
|
||||||
if (addSelect) {
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectTransition"));
|
obs_module_text("AdvSceneSwitcher.selectTransition"),
|
||||||
|
selectable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addCurrent) {
|
||||||
|
sel->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.currentTransition"));
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
obs_frontend_source_list *transitions = new obs_frontend_source_list();
|
||||||
@@ -197,6 +209,7 @@ void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
|
|||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
|
obs_module_text("AdvSceneSwitcher.selectAudioSource"),
|
||||||
|
false,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
}
|
}
|
||||||
@@ -221,12 +234,49 @@ void AdvSceneSwitcher::populateAudioSelection(QComboBox *sel, bool addSelect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::populateVideoSelection(QComboBox *sel, bool addScenes,
|
||||||
|
bool addSelect)
|
||||||
|
{
|
||||||
|
if (addSelect) {
|
||||||
|
addSelectionEntry(
|
||||||
|
sel,
|
||||||
|
obs_module_text("AdvSceneSwitcher.selectVideoSource"),
|
||||||
|
false,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sourceEnum = [](void *data, obs_source_t *source) -> bool /* -- */
|
||||||
|
{
|
||||||
|
std::vector<std::string> *list =
|
||||||
|
reinterpret_cast<std::vector<std::string> *>(data);
|
||||||
|
uint32_t flags = obs_source_get_output_flags(source);
|
||||||
|
std::string test = obs_source_get_name(source);
|
||||||
|
if ((flags & (OBS_SOURCE_VIDEO | OBS_SOURCE_ASYNC)) != 0) {
|
||||||
|
list->push_back(obs_source_get_name(source));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> videoSources;
|
||||||
|
obs_enum_sources(sourceEnum, &videoSources);
|
||||||
|
sort(videoSources.begin(), videoSources.end());
|
||||||
|
for (std::string &source : videoSources) {
|
||||||
|
sel->addItem(source.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addScenes) {
|
||||||
|
populateSceneSelection(sel, false, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
|
void AdvSceneSwitcher::populateMediaSelection(QComboBox *sel, bool addSelect)
|
||||||
{
|
{
|
||||||
if (addSelect) {
|
if (addSelect) {
|
||||||
addSelectionEntry(
|
addSelectionEntry(
|
||||||
sel,
|
sel,
|
||||||
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
|
obs_module_text("AdvSceneSwitcher.selectMediaSource"),
|
||||||
|
false,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
"AdvSceneSwitcher.invaildEntriesWillNotBeSaved"));
|
||||||
}
|
}
|
||||||
@@ -428,9 +478,6 @@ void SwitcherData::Thread()
|
|||||||
OBSWeakSource scene;
|
OBSWeakSource scene;
|
||||||
OBSWeakSource transition;
|
OBSWeakSource transition;
|
||||||
|
|
||||||
bool defTransitionMatch = false;
|
|
||||||
OBSWeakSource defTransition;
|
|
||||||
|
|
||||||
endTime = std::chrono::high_resolution_clock::now();
|
endTime = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
auto runTime =
|
auto runTime =
|
||||||
@@ -449,7 +496,7 @@ void SwitcherData::Thread()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vblog(LOG_INFO, "sleep for %ld", duration.count());
|
vblog(LOG_INFO, "try to sleep for %ld", duration.count());
|
||||||
cv.wait_for(lock, duration);
|
cv.wait_for(lock, duration);
|
||||||
|
|
||||||
startTime = std::chrono::high_resolution_clock::now();
|
startTime = std::chrono::high_resolution_clock::now();
|
||||||
@@ -457,63 +504,17 @@ void SwitcherData::Thread()
|
|||||||
linger = 0;
|
linger = 0;
|
||||||
|
|
||||||
switcher->Prune();
|
switcher->Prune();
|
||||||
|
|
||||||
if (switcher->stop) {
|
if (switcher->stop) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkPause()) {
|
if (checkPause()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
match = checkForMatch(scene, transition, linger);
|
||||||
checkDefaultSceneTransitions(defTransitionMatch, defTransition);
|
if (switcher->stop) {
|
||||||
|
break;
|
||||||
for (int switchFuncName : functionNamesByPriority) {
|
|
||||||
switch (switchFuncName) {
|
|
||||||
case read_file_func:
|
|
||||||
checkSwitchInfoFromFile(match, scene,
|
|
||||||
transition);
|
|
||||||
checkFileContent(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case idle_func:
|
|
||||||
checkIdleSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case exe_func:
|
|
||||||
checkExeSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case screen_region_func:
|
|
||||||
checkScreenRegionSwitch(match, scene,
|
|
||||||
transition);
|
|
||||||
break;
|
|
||||||
case window_title_func:
|
|
||||||
checkWindowTitleSwitch(match, scene,
|
|
||||||
transition);
|
|
||||||
break;
|
|
||||||
case round_trip_func:
|
|
||||||
checkSceneSequence(match, scene, transition,
|
|
||||||
linger);
|
|
||||||
break;
|
|
||||||
case media_func:
|
|
||||||
checkMediaSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case time_func:
|
|
||||||
checkTimeSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
case audio_func:
|
|
||||||
checkAudioSwitch(match, scene, transition);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (switcher->stop) {
|
|
||||||
goto endLoop;
|
|
||||||
}
|
|
||||||
if (match) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkNoMatchSwitch(match, scene, transition, sleep);
|
checkNoMatchSwitch(match, scene, transition, sleep);
|
||||||
|
|
||||||
checkSwitchCooldown(match);
|
checkSwitchCooldown(match);
|
||||||
|
|
||||||
if (linger) {
|
if (linger) {
|
||||||
@@ -544,10 +545,6 @@ void SwitcherData::Thread()
|
|||||||
// leading to a deadlock, so we have to unlock()
|
// leading to a deadlock, so we have to unlock()
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
if (!match && defTransitionMatch) {
|
|
||||||
setCurrentDefTransition(defTransition);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
switchScene(scene, transition,
|
switchScene(scene, transition,
|
||||||
tansitionOverrideOverride);
|
tansitionOverrideOverride);
|
||||||
@@ -555,10 +552,67 @@ void SwitcherData::Thread()
|
|||||||
|
|
||||||
writeSceneInfoToFile();
|
writeSceneInfoToFile();
|
||||||
}
|
}
|
||||||
endLoop:
|
|
||||||
blog(LOG_INFO, "stopped");
|
blog(LOG_INFO, "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition, int &linger)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
if (uninterruptibleSceneSequenceActive) {
|
||||||
|
checkSceneSequence(match, scene, transition, linger);
|
||||||
|
if (match) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int switchFuncName : functionNamesByPriority) {
|
||||||
|
switch (switchFuncName) {
|
||||||
|
case read_file_func:
|
||||||
|
checkSwitchInfoFromFile(match, scene, transition);
|
||||||
|
checkFileContent(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case idle_func:
|
||||||
|
checkIdleSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case exe_func:
|
||||||
|
checkExeSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case screen_region_func:
|
||||||
|
checkScreenRegionSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case window_title_func:
|
||||||
|
checkWindowTitleSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case round_trip_func:
|
||||||
|
checkSceneSequence(match, scene, transition, linger);
|
||||||
|
break;
|
||||||
|
case media_func:
|
||||||
|
checkMediaSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case time_func:
|
||||||
|
checkTimeSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case audio_func:
|
||||||
|
checkAudioSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
case video_func:
|
||||||
|
checkVideoSwitch(match, scene, transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->stop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
bool &transitionOverrideOverride)
|
bool &transitionOverrideOverride)
|
||||||
{
|
{
|
||||||
@@ -643,7 +697,7 @@ extern "C" void FreeSceneSwitcher()
|
|||||||
|
|
||||||
void handleSceneChange(SwitcherData *s)
|
void handleSceneChange(SwitcherData *s)
|
||||||
{
|
{
|
||||||
// Stop waiting if scene was manually changed
|
// Stop waiting if scene was changed
|
||||||
if (s->sceneChangedDuringWait()) {
|
if (s->sceneChangedDuringWait()) {
|
||||||
s->cv.notify_one();
|
s->cv.notify_one();
|
||||||
}
|
}
|
||||||
@@ -658,12 +712,8 @@ void handleSceneChange(SwitcherData *s)
|
|||||||
s->previousSceneHelper = ws;
|
s->previousSceneHelper = ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
switcher->checkTriggers();
|
s->checkTriggers();
|
||||||
}
|
s->checkDefaultSceneTransitions();
|
||||||
|
|
||||||
void handleTransitionStop(SwitcherData *s)
|
|
||||||
{
|
|
||||||
s->checkedDefTransition = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLiveTime(SwitcherData *s)
|
void setLiveTime(SwitcherData *s)
|
||||||
@@ -702,9 +752,6 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
|||||||
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
case OBS_FRONTEND_EVENT_SCENE_CHANGED:
|
||||||
handleSceneChange((SwitcherData *)switcher);
|
handleSceneChange((SwitcherData *)switcher);
|
||||||
break;
|
break;
|
||||||
case OBS_FRONTEND_EVENT_TRANSITION_STOPPED:
|
|
||||||
handleTransitionStop((SwitcherData *)switcher);
|
|
||||||
break;
|
|
||||||
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
|
case OBS_FRONTEND_EVENT_RECORDING_STARTED:
|
||||||
setLiveTime((SwitcherData *)switcher);
|
setLiveTime((SwitcherData *)switcher);
|
||||||
checkAutoStartRecording((SwitcherData *)switcher);
|
checkAutoStartRecording((SwitcherData *)switcher);
|
||||||
|
|||||||
109
src/general.cpp
109
src/general.cpp
@@ -4,7 +4,7 @@
|
|||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
#include "headers/version.h"
|
#include "headers/version.h"
|
||||||
|
|
||||||
constexpr auto tab_count = 15;
|
constexpr auto tab_count = 16;
|
||||||
|
|
||||||
QMetaObject::Connection inactivePluse;
|
QMetaObject::Connection inactivePluse;
|
||||||
|
|
||||||
@@ -129,6 +129,7 @@ void AdvSceneSwitcher::SetStarted()
|
|||||||
ui->pluginRunningText->setText(
|
ui->pluginRunningText->setText(
|
||||||
obs_module_text("AdvSceneSwitcher.status.active"));
|
obs_module_text("AdvSceneSwitcher.status.active"));
|
||||||
ui->pluginRunningText->disconnect(inactivePluse);
|
ui->pluginRunningText->disconnect(inactivePluse);
|
||||||
|
currentStatusActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetStopped()
|
void AdvSceneSwitcher::SetStopped()
|
||||||
@@ -139,6 +140,7 @@ void AdvSceneSwitcher::SetStopped()
|
|||||||
obs_module_text("AdvSceneSwitcher.status.inactive"));
|
obs_module_text("AdvSceneSwitcher.status.inactive"));
|
||||||
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
|
inactivePluse = PulseWidget(ui->pluginRunningText, QColor(Qt::red),
|
||||||
QColor(0, 0, 0, 0), "QLabel ");
|
QColor(0, 0, 0, 0), "QLabel ");
|
||||||
|
currentStatusActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
||||||
@@ -324,9 +326,12 @@ int findTabIndex(QTabWidget *tabWidget, int pos)
|
|||||||
tabName = "audioTab";
|
tabName = "audioTab";
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
tabName = "sceneGroupTab";
|
tabName = "videoTab";
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
|
tabName = "sceneGroupTab";
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
tabName = "sceneTriggerTab";
|
tabName = "sceneTriggerTab";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -381,22 +386,23 @@ void SwitcherData::loadSettings(obs_data_t *obj)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switcher->loadSceneGroups(obj);
|
loadSceneGroups(obj);
|
||||||
switcher->loadWindowTitleSwitches(obj);
|
loadWindowTitleSwitches(obj);
|
||||||
switcher->loadScreenRegionSwitches(obj);
|
loadScreenRegionSwitches(obj);
|
||||||
switcher->loadPauseSwitches(obj);
|
loadPauseSwitches(obj);
|
||||||
switcher->loadSceneSequenceSwitches(obj);
|
loadSceneSequenceSwitches(obj);
|
||||||
switcher->loadSceneTransitions(obj);
|
loadSceneTransitions(obj);
|
||||||
switcher->loadIdleSwitches(obj);
|
loadIdleSwitches(obj);
|
||||||
switcher->loadExecutableSwitches(obj);
|
loadExecutableSwitches(obj);
|
||||||
switcher->loadRandomSwitches(obj);
|
loadRandomSwitches(obj);
|
||||||
switcher->loadFileSwitches(obj);
|
loadFileSwitches(obj);
|
||||||
switcher->loadMediaSwitches(obj);
|
loadMediaSwitches(obj);
|
||||||
switcher->loadTimeSwitches(obj);
|
loadTimeSwitches(obj);
|
||||||
switcher->loadAudioSwitches(obj);
|
loadAudioSwitches(obj);
|
||||||
switcher->loadSceneTriggers(obj);
|
loadVideoSwitches(obj);
|
||||||
switcher->loadGeneralSettings(obj);
|
loadSceneTriggers(obj);
|
||||||
switcher->loadHotkeys(obj);
|
loadGeneralSettings(obj);
|
||||||
|
loadHotkeys(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::saveSettings(obs_data_t *obj)
|
void SwitcherData::saveSettings(obs_data_t *obj)
|
||||||
@@ -418,6 +424,7 @@ void SwitcherData::saveSettings(obs_data_t *obj)
|
|||||||
saveMediaSwitches(obj);
|
saveMediaSwitches(obj);
|
||||||
saveTimeSwitches(obj);
|
saveTimeSwitches(obj);
|
||||||
saveAudioSwitches(obj);
|
saveAudioSwitches(obj);
|
||||||
|
saveVideoSwitches(obj);
|
||||||
saveSceneTriggers(obj);
|
saveSceneTriggers(obj);
|
||||||
saveGeneralSettings(obj);
|
saveGeneralSettings(obj);
|
||||||
saveHotkeys(obj);
|
saveHotkeys(obj);
|
||||||
@@ -465,15 +472,15 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
|||||||
switcher->functionNamesByPriority[7]);
|
switcher->functionNamesByPriority[7]);
|
||||||
obs_data_set_int(obj, "priority8",
|
obs_data_set_int(obj, "priority8",
|
||||||
switcher->functionNamesByPriority[8]);
|
switcher->functionNamesByPriority[8]);
|
||||||
|
obs_data_set_int(obj, "priority9",
|
||||||
|
switcher->functionNamesByPriority[9]);
|
||||||
|
|
||||||
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
obs_data_set_int(obj, "threadPriority", switcher->threadPriority);
|
||||||
|
|
||||||
// After fresh install of OBS the vector can be empty
|
// After fresh install of OBS the vector can be empty
|
||||||
// as save() might be called before first load()
|
// as save() might be called before first load()
|
||||||
if (switcher->tabOrder.size() < tab_count) {
|
if (tabOrder.size() < tab_count) {
|
||||||
switcher->tabOrder = std::vector<int>(tab_count);
|
resetTabOrder();
|
||||||
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(),
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
obs_data_set_int(obj, "generalTabPos", switcher->tabOrder[0]);
|
||||||
@@ -489,8 +496,9 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
obs_data_set_int(obj, "idleTabPos", switcher->tabOrder[10]);
|
||||||
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]);
|
||||||
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
|
obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]);
|
||||||
obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[13]);
|
obs_data_set_int(obj, "videoTabPos", switcher->tabOrder[13]);
|
||||||
obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[14]);
|
obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[14]);
|
||||||
|
obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[15]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
||||||
@@ -553,6 +561,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
(obs_data_get_int(obj, "priority7"));
|
(obs_data_get_int(obj, "priority7"));
|
||||||
switcher->functionNamesByPriority[8] =
|
switcher->functionNamesByPriority[8] =
|
||||||
(obs_data_get_int(obj, "priority8"));
|
(obs_data_get_int(obj, "priority8"));
|
||||||
|
switcher->functionNamesByPriority[9] =
|
||||||
|
(obs_data_get_int(obj, "priority9"));
|
||||||
if (!switcher->prioFuncsValid()) {
|
if (!switcher->prioFuncsValid()) {
|
||||||
switcher->functionNamesByPriority[0] = (default_priority_0);
|
switcher->functionNamesByPriority[0] = (default_priority_0);
|
||||||
switcher->functionNamesByPriority[1] = (default_priority_1);
|
switcher->functionNamesByPriority[1] = (default_priority_1);
|
||||||
@@ -563,6 +573,7 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
switcher->functionNamesByPriority[6] = (default_priority_6);
|
switcher->functionNamesByPriority[6] = (default_priority_6);
|
||||||
switcher->functionNamesByPriority[7] = (default_priority_7);
|
switcher->functionNamesByPriority[7] = (default_priority_7);
|
||||||
switcher->functionNamesByPriority[8] = (default_priority_8);
|
switcher->functionNamesByPriority[8] = (default_priority_8);
|
||||||
|
switcher->functionNamesByPriority[9] = (default_priority_9);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_set_default_int(obj, "threadPriority",
|
obs_data_set_default_int(obj, "threadPriority",
|
||||||
@@ -582,8 +593,9 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
obs_data_set_default_int(obj, "idleTabPos", 10);
|
obs_data_set_default_int(obj, "idleTabPos", 10);
|
||||||
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
obs_data_set_default_int(obj, "sequenceTabPos", 11);
|
||||||
obs_data_set_default_int(obj, "audioTabPos", 12);
|
obs_data_set_default_int(obj, "audioTabPos", 12);
|
||||||
obs_data_set_default_int(obj, "sceneGroupTabPos", 13);
|
obs_data_set_default_int(obj, "videoTabPos", 13);
|
||||||
obs_data_set_default_int(obj, "triggerTabPos", 14);
|
obs_data_set_default_int(obj, "sceneGroupTabPos", 14);
|
||||||
|
obs_data_set_default_int(obj, "triggerTabPos", 15);
|
||||||
|
|
||||||
switcher->tabOrder.emplace_back(
|
switcher->tabOrder.emplace_back(
|
||||||
(int)(obs_data_get_int(obj, "generalTabPos")));
|
(int)(obs_data_get_int(obj, "generalTabPos")));
|
||||||
@@ -611,10 +623,30 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj)
|
|||||||
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
(int)(obs_data_get_int(obj, "sequenceTabPos")));
|
||||||
switcher->tabOrder.emplace_back(
|
switcher->tabOrder.emplace_back(
|
||||||
(int)(obs_data_get_int(obj, "audioTabPos")));
|
(int)(obs_data_get_int(obj, "audioTabPos")));
|
||||||
|
switcher->tabOrder.emplace_back(
|
||||||
|
(int)(obs_data_get_int(obj, "videoTabPos")));
|
||||||
switcher->tabOrder.emplace_back(
|
switcher->tabOrder.emplace_back(
|
||||||
(int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
(int)(obs_data_get_int(obj, "sceneGroupTabPos")));
|
||||||
switcher->tabOrder.emplace_back(
|
switcher->tabOrder.emplace_back(
|
||||||
(int)(obs_data_get_int(obj, "triggerTabPos")));
|
(int)(obs_data_get_int(obj, "triggerTabPos")));
|
||||||
|
|
||||||
|
if (!tabOrderValid()) {
|
||||||
|
resetTabOrder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SwitcherData::tabOrderValid()
|
||||||
|
{
|
||||||
|
auto tmp = tabOrder;
|
||||||
|
std::sort(tmp.begin(), tmp.end());
|
||||||
|
auto it = std::unique(tmp.begin(), tmp.end());
|
||||||
|
return it == tmp.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::resetTabOrder()
|
||||||
|
{
|
||||||
|
tabOrder = std::vector<int>(tab_count);
|
||||||
|
std::iota(switcher->tabOrder.begin(), switcher->tabOrder.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||||
@@ -681,6 +713,21 @@ void populateAutoStartEventSelection(QComboBox *cb)
|
|||||||
"AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming"));
|
"AdvSceneSwitcher.generalTab.status.autoStart.recordingAndStreaming"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::updateStatus()
|
||||||
|
{
|
||||||
|
if (switcher->th && switcher->th->isRunning()) {
|
||||||
|
if (currentStatusActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStarted();
|
||||||
|
} else {
|
||||||
|
if (!currentStatusActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStopped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::setupGeneralTab()
|
void AdvSceneSwitcher::setupGeneralTab()
|
||||||
{
|
{
|
||||||
populateSceneSelection(ui->noMatchSwitchScene, false);
|
populateSceneSelection(ui->noMatchSwitchScene, false);
|
||||||
@@ -748,6 +795,10 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
s = obs_module_text(
|
s = obs_module_text(
|
||||||
"AdvSceneSwitcher.generalTab.priority.audio");
|
"AdvSceneSwitcher.generalTab.priority.audio");
|
||||||
break;
|
break;
|
||||||
|
case video_func:
|
||||||
|
s = obs_module_text(
|
||||||
|
"AdvSceneSwitcher.generalTab.priority.video");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
QString text(s.c_str());
|
QString text(s.c_str());
|
||||||
QListWidgetItem *item =
|
QListWidgetItem *item =
|
||||||
@@ -780,4 +831,10 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
} else {
|
} else {
|
||||||
SetStopped();
|
SetStopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Updates the UI status element if the status changed externally
|
||||||
|
// (e.g. via hotkeys)
|
||||||
|
QTimer *statusTimer = new QTimer(this);
|
||||||
|
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||||
|
statusTimer->start(1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
#include "switcher-data-structs.hpp"
|
#include "switcher-data-structs.hpp"
|
||||||
|
|
||||||
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
#define blog(level, msg, ...) blog(level, "[adv-ss] " msg, ##__VA_ARGS__)
|
||||||
#define vblog(level, msg, ...) \
|
#define vblog(level, msg, ...) \
|
||||||
if (verbose) { \
|
if (verbose) { \
|
||||||
blog(level, "[adv-ss] " msg, ##__VA_ARGS__); \
|
blog(level, msg, ##__VA_ARGS__); \
|
||||||
}
|
}
|
||||||
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
@@ -23,6 +23,7 @@ class AdvSceneSwitcher : public QDialog {
|
|||||||
public:
|
public:
|
||||||
std::unique_ptr<Ui_AdvSceneSwitcher> ui;
|
std::unique_ptr<Ui_AdvSceneSwitcher> ui;
|
||||||
bool loading = true;
|
bool loading = true;
|
||||||
|
bool currentStatusActive = false;
|
||||||
|
|
||||||
AdvSceneSwitcher(QWidget *parent);
|
AdvSceneSwitcher(QWidget *parent);
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ public:
|
|||||||
int IgnoreIdleWindowsFindByData(const QString &window);
|
int IgnoreIdleWindowsFindByData(const QString &window);
|
||||||
|
|
||||||
void UpdateNonMatchingScene(const QString &name);
|
void UpdateNonMatchingScene(const QString &name);
|
||||||
|
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||||
void SetEditSceneGroup(SceneGroup &sg);
|
void SetEditSceneGroup(SceneGroup &sg);
|
||||||
|
|
||||||
void loadUI();
|
void loadUI();
|
||||||
@@ -57,23 +59,33 @@ public:
|
|||||||
void setupAudioTab();
|
void setupAudioTab();
|
||||||
void setupSceneGroupTab();
|
void setupSceneGroupTab();
|
||||||
void setupTriggerTab();
|
void setupTriggerTab();
|
||||||
|
void setupVideoTab();
|
||||||
void setTabOrder();
|
void setTabOrder();
|
||||||
|
|
||||||
static bool DisplayMessage(QString msg, bool question = false);
|
static bool DisplayMessage(QString msg, bool question = false);
|
||||||
static void AskBackup(obs_data_t *obj);
|
static void AskBackup(obs_data_t *obj);
|
||||||
|
|
||||||
|
static void addSelectionEntry(QComboBox *sel, const char *description,
|
||||||
|
bool selectable = false,
|
||||||
|
const char *tooltip = "");
|
||||||
static void populateSceneSelection(QComboBox *sel,
|
static void populateSceneSelection(QComboBox *sel,
|
||||||
bool addPrevious = false,
|
bool addPrevious = false,
|
||||||
bool addSceneGroup = false,
|
bool addSceneGroup = false,
|
||||||
bool addSelect = true,
|
bool addSelect = true,
|
||||||
std::string selectText = "");
|
std::string selectText = "",
|
||||||
|
bool selectable = false);
|
||||||
|
|
||||||
static void populateTransitionSelection(QComboBox *sel,
|
static void populateTransitionSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addCurrent = true,
|
||||||
|
bool addSelect = true,
|
||||||
|
bool selectable = false);
|
||||||
static void populateWindowSelection(QComboBox *sel,
|
static void populateWindowSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addSelect = true);
|
||||||
static void populateAudioSelection(QComboBox *sel,
|
static void populateAudioSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addSelect = true);
|
||||||
|
static void populateVideoSelection(QComboBox *sel,
|
||||||
|
bool addScenes = true,
|
||||||
|
bool addSelect = true);
|
||||||
static void populateMediaSelection(QComboBox *sel,
|
static void populateMediaSelection(QComboBox *sel,
|
||||||
bool addSelect = true);
|
bool addSelect = true);
|
||||||
static void populateProcessSelection(QComboBox *sel,
|
static void populateProcessSelection(QComboBox *sel,
|
||||||
@@ -129,10 +141,12 @@ public slots:
|
|||||||
|
|
||||||
void on_sceneSequenceAdd_clicked();
|
void on_sceneSequenceAdd_clicked();
|
||||||
void on_sceneSequenceRemove_clicked();
|
void on_sceneSequenceRemove_clicked();
|
||||||
void on_sceneSequenceSave_clicked();
|
|
||||||
void on_sceneSequenceLoad_clicked();
|
|
||||||
void on_sceneSequenceUp_clicked();
|
void on_sceneSequenceUp_clicked();
|
||||||
void on_sceneSequenceDown_clicked();
|
void on_sceneSequenceDown_clicked();
|
||||||
|
void on_sceneSequenceSave_clicked();
|
||||||
|
void on_sceneSequenceLoad_clicked();
|
||||||
|
void on_sequenceEdit_clicked();
|
||||||
|
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
|
||||||
|
|
||||||
void on_verboseLogging_stateChanged(int state);
|
void on_verboseLogging_stateChanged(int state);
|
||||||
void on_uiHintsDisable_stateChanged(int state);
|
void on_uiHintsDisable_stateChanged(int state);
|
||||||
@@ -191,6 +205,12 @@ public slots:
|
|||||||
void on_audioDown_clicked();
|
void on_audioDown_clicked();
|
||||||
void on_audioFallback_toggled(bool on);
|
void on_audioFallback_toggled(bool on);
|
||||||
|
|
||||||
|
void on_videoAdd_clicked();
|
||||||
|
void on_videoRemove_clicked();
|
||||||
|
void on_videoUp_clicked();
|
||||||
|
void on_videoDown_clicked();
|
||||||
|
void on_getScreenshot_clicked();
|
||||||
|
|
||||||
void on_sceneGroupAdd_clicked();
|
void on_sceneGroupAdd_clicked();
|
||||||
void on_sceneGroupRemove_clicked();
|
void on_sceneGroupRemove_clicked();
|
||||||
void on_sceneGroupUp_clicked();
|
void on_sceneGroupUp_clicked();
|
||||||
@@ -213,6 +233,7 @@ public slots:
|
|||||||
void on_threadPriority_currentTextChanged(const QString &text);
|
void on_threadPriority_currentTextChanged(const QString &text);
|
||||||
|
|
||||||
void updateScreenRegionCursorPos();
|
void updateScreenRegionCursorPos();
|
||||||
|
void updateStatus();
|
||||||
|
|
||||||
void on_close_clicked();
|
void on_close_clicked();
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct SceneGroup {
|
|||||||
|
|
||||||
int currentCount = -1;
|
int currentCount = -1;
|
||||||
std::chrono::high_resolution_clock::time_point lastAdvTime;
|
std::chrono::high_resolution_clock::time_point lastAdvTime;
|
||||||
OBSWeakSource lastRandomScene = nullptr;
|
int lastRandomScene = -1;
|
||||||
|
|
||||||
inline SceneGroup(){};
|
inline SceneGroup(){};
|
||||||
inline SceneGroup(std::string name_) : name(name_){};
|
inline SceneGroup(std::string name_) : name(name_){};
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ enum class sceneTriggerAction {
|
|||||||
STOP_REPLAY_BUFFER,
|
STOP_REPLAY_BUFFER,
|
||||||
|
|
||||||
MUTE_SOURCE,
|
MUTE_SOURCE,
|
||||||
|
UNMUTE_SOURCE,
|
||||||
|
|
||||||
|
START_SWITCHER,
|
||||||
|
STOP_SWITCHER,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SceneTrigger : SceneSwitcherEntry {
|
struct SceneTrigger : SceneSwitcherEntry {
|
||||||
@@ -36,7 +40,8 @@ struct SceneTrigger : SceneSwitcherEntry {
|
|||||||
void save(obs_data_t *obj);
|
void save(obs_data_t *obj);
|
||||||
void load(obs_data_t *obj);
|
void load(obs_data_t *obj);
|
||||||
|
|
||||||
bool checkMatch(OBSWeakSource previousScene);
|
bool checkMatch(OBSWeakSource currentScene,
|
||||||
|
OBSWeakSource previousScene);
|
||||||
void performAction();
|
void performAction();
|
||||||
void logMatch();
|
void logMatch();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ struct SceneSwitcherEntry {
|
|||||||
OBSWeakSource scene = nullptr;
|
OBSWeakSource scene = nullptr;
|
||||||
OBSWeakSource transition = nullptr;
|
OBSWeakSource transition = nullptr;
|
||||||
bool usePreviousScene = false;
|
bool usePreviousScene = false;
|
||||||
|
bool useCurrentTransition = false;
|
||||||
|
|
||||||
virtual const char *getType() = 0;
|
virtual const char *getType() = 0;
|
||||||
virtual bool initialized();
|
virtual bool initialized();
|
||||||
@@ -64,14 +65,15 @@ class SwitchWidget : public QWidget {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
||||||
bool usePreviousScene = true, bool addSceneGroup = false);
|
bool usePreviousScene = true, bool addSceneGroup = false,
|
||||||
|
bool addCurrentTransition = true);
|
||||||
virtual SceneSwitcherEntry *getSwitchData();
|
virtual SceneSwitcherEntry *getSwitchData();
|
||||||
virtual void setSwitchData(SceneSwitcherEntry *s);
|
virtual void setSwitchData(SceneSwitcherEntry *s);
|
||||||
void showSwitchData();
|
void showSwitchData();
|
||||||
|
|
||||||
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
|
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
|
||||||
|
|
||||||
private slots:
|
protected slots:
|
||||||
void SceneChanged(const QString &text);
|
void SceneChanged(const QString &text);
|
||||||
void TransitionChanged(const QString &text);
|
void TransitionChanged(const QString &text);
|
||||||
void SceneGroupAdd(const QString &name);
|
void SceneGroupAdd(const QString &name);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ enum class PauseTarget {
|
|||||||
Idle,
|
Idle,
|
||||||
Sequence,
|
Sequence,
|
||||||
Audio,
|
Audio,
|
||||||
|
Video,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PauseEntry : SceneSwitcherEntry {
|
struct PauseEntry : SceneSwitcherEntry {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "switch-generic.hpp"
|
#include "switch-generic.hpp"
|
||||||
|
|
||||||
constexpr auto round_trip_func = 1;
|
constexpr auto round_trip_func = 1;
|
||||||
@@ -12,42 +14,77 @@ typedef enum {
|
|||||||
|
|
||||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||||
static bool pause;
|
static bool pause;
|
||||||
|
SwitchTargetType startTargetType = SwitchTargetType::Scene;
|
||||||
OBSWeakSource startScene = nullptr;
|
OBSWeakSource startScene = nullptr;
|
||||||
double delay = 0;
|
double delay = 0;
|
||||||
int delayMultiplier = 1;
|
int delayMultiplier = 1;
|
||||||
bool interruptible = false;
|
bool interruptible = false;
|
||||||
unsigned int matchCount = 0;
|
unsigned int matchCount = 0;
|
||||||
|
|
||||||
|
// nullptr marks start point and reaching end of extended sequence
|
||||||
|
SceneSequenceSwitch *activeSequence = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<SceneSequenceSwitch> extendedSequence = nullptr;
|
||||||
|
|
||||||
const char *getType() { return "sequence"; }
|
const char *getType() { return "sequence"; }
|
||||||
bool initialized();
|
bool initialized();
|
||||||
bool valid();
|
bool valid();
|
||||||
void save(obs_data_t *obj);
|
void save(obs_data_t *obj, bool saveExt = true);
|
||||||
void load(obs_data_t *obj);
|
void load(obs_data_t *obj, bool saveExt = true);
|
||||||
|
|
||||||
|
bool reduce();
|
||||||
|
SceneSequenceSwitch *extend();
|
||||||
|
|
||||||
|
bool checkMatch(OBSWeakSource currentScene, int &linger,
|
||||||
|
SceneSequenceSwitch *root = nullptr);
|
||||||
|
bool checkDurationMatchInterruptible();
|
||||||
|
void prepareUninterruptibleMatch(OBSWeakSource currentScene,
|
||||||
|
int &linger);
|
||||||
|
void advanceActiveSequence();
|
||||||
|
void logAdvanceSequence();
|
||||||
|
void logSequenceCanceled();
|
||||||
};
|
};
|
||||||
|
|
||||||
class SequenceWidget : public SwitchWidget {
|
class SequenceWidget : public SwitchWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s);
|
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||||
|
bool extendSequence = false, bool editExtendMode = false,
|
||||||
|
bool showExtendText = true);
|
||||||
SceneSequenceSwitch *getSwitchData();
|
SceneSequenceSwitch *getSwitchData();
|
||||||
void setSwitchData(SceneSequenceSwitch *s);
|
void setSwitchData(SceneSequenceSwitch *s);
|
||||||
|
|
||||||
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
||||||
|
|
||||||
void UpdateDelay();
|
void UpdateDelay();
|
||||||
|
void UpdateWidgetStatus(bool showExtendText);
|
||||||
|
void setExtendedSequenceStartScene();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void SceneChanged(const QString &text);
|
||||||
void DelayChanged(double delay);
|
void DelayChanged(double delay);
|
||||||
void DelayUnitsChanged(int idx);
|
void DelayUnitsChanged(int idx);
|
||||||
void StartSceneChanged(const QString &text);
|
void StartSceneChanged(const QString &text);
|
||||||
void InterruptibleChanged(int state);
|
void InterruptibleChanged(int state);
|
||||||
|
void ExtendClicked();
|
||||||
|
void ReduceClicked();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
QDoubleSpinBox *delay;
|
QDoubleSpinBox *delay;
|
||||||
QComboBox *delayUnits;
|
QComboBox *delayUnits;
|
||||||
QComboBox *startScenes;
|
QComboBox *startScenes;
|
||||||
QCheckBox *interruptible;
|
QCheckBox *interruptible;
|
||||||
|
QVBoxLayout *extendSequenceLayout;
|
||||||
|
QPushButton *extend;
|
||||||
|
QPushButton *reduce;
|
||||||
|
|
||||||
|
// I would prefer having a list of only widgets of type editExtendMode
|
||||||
|
// but I am not sure how to best implement that using a QListWidget.
|
||||||
|
//
|
||||||
|
// So use edit button to bring up edit widget and
|
||||||
|
// add a label to disaplay current extended sequence state.
|
||||||
|
QLabel *extendText;
|
||||||
|
|
||||||
SceneSequenceSwitch *switchData;
|
SceneSequenceSwitch *switchData;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ struct DefaultSceneTransition : SceneSwitcherEntry {
|
|||||||
static bool pause;
|
static bool pause;
|
||||||
|
|
||||||
const char *getType() { return "def_transition"; }
|
const char *getType() { return "def_transition"; }
|
||||||
|
bool checkMatch(OBSWeakSource currentScene);
|
||||||
|
void setTransition();
|
||||||
|
|
||||||
inline DefaultSceneTransition(){};
|
inline DefaultSceneTransition(){};
|
||||||
inline DefaultSceneTransition(OBSWeakSource scene_,
|
inline DefaultSceneTransition(OBSWeakSource scene_,
|
||||||
|
|||||||
98
src/headers/switch-video.hpp
Normal file
98
src/headers/switch-video.hpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QSpinBox>
|
||||||
|
|
||||||
|
#include "switch-generic.hpp"
|
||||||
|
|
||||||
|
constexpr auto video_func = 9;
|
||||||
|
constexpr auto default_priority_9 = video_func;
|
||||||
|
|
||||||
|
enum class videoSwitchType {
|
||||||
|
MATCH,
|
||||||
|
DIFFER,
|
||||||
|
HAS_NOT_CHANGED,
|
||||||
|
};
|
||||||
|
|
||||||
|
class AdvSSScreenshotObj {
|
||||||
|
public:
|
||||||
|
AdvSSScreenshotObj(obs_source_t *source);
|
||||||
|
~AdvSSScreenshotObj();
|
||||||
|
|
||||||
|
void Screenshot();
|
||||||
|
void Download();
|
||||||
|
void Copy();
|
||||||
|
void MarkDone();
|
||||||
|
|
||||||
|
gs_texrender_t *texrender = nullptr;
|
||||||
|
gs_stagesurf_t *stagesurf = nullptr;
|
||||||
|
OBSWeakSource weakSource;
|
||||||
|
std::string path;
|
||||||
|
QImage image;
|
||||||
|
uint32_t cx = 0;
|
||||||
|
uint32_t cy = 0;
|
||||||
|
|
||||||
|
int stage = 0;
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
std::chrono::high_resolution_clock::time_point time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoSwitch : virtual SceneSwitcherEntry {
|
||||||
|
static bool pause;
|
||||||
|
|
||||||
|
videoSwitchType condition = videoSwitchType::MATCH;
|
||||||
|
OBSWeakSource videoSource = nullptr;
|
||||||
|
std::string file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
|
double duration = 0;
|
||||||
|
bool ignoreInactiveSource = false;
|
||||||
|
|
||||||
|
std::unique_ptr<AdvSSScreenshotObj> screenshotData = nullptr;
|
||||||
|
std::chrono::high_resolution_clock::time_point previousTime{};
|
||||||
|
QImage matchImage;
|
||||||
|
|
||||||
|
std::chrono::milliseconds currentMatchDuration{};
|
||||||
|
|
||||||
|
const char *getType() { return "video"; }
|
||||||
|
bool initialized();
|
||||||
|
bool valid();
|
||||||
|
void save(obs_data_t *obj);
|
||||||
|
void load(obs_data_t *obj);
|
||||||
|
void getScreenshot();
|
||||||
|
bool loadImageFromFile();
|
||||||
|
bool checkMatch();
|
||||||
|
|
||||||
|
VideoSwitch(){};
|
||||||
|
friend void swap(VideoSwitch &first, VideoSwitch &second);
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoSwitchWidget : public SwitchWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VideoSwitchWidget(QWidget *parent, VideoSwitch *s);
|
||||||
|
VideoSwitch *getSwitchData();
|
||||||
|
void setSwitchData(VideoSwitch *s);
|
||||||
|
|
||||||
|
static void swapSwitchData(VideoSwitchWidget *as1,
|
||||||
|
VideoSwitchWidget *as2);
|
||||||
|
|
||||||
|
void UpdatePreviewTooltip();
|
||||||
|
void SetFilePath(const QString &text);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void SourceChanged(const QString &text);
|
||||||
|
void ConditionChanged(int cond);
|
||||||
|
void DurationChanged(double dur);
|
||||||
|
void FilePathChanged();
|
||||||
|
void BrowseButtonClicked();
|
||||||
|
void IgnoreInactiveChanged(int state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QComboBox *videoSources;
|
||||||
|
QComboBox *condition;
|
||||||
|
QDoubleSpinBox *duration;
|
||||||
|
QLineEdit *filePath;
|
||||||
|
QPushButton *browseButton;
|
||||||
|
QCheckBox *ignoreInactiveSource;
|
||||||
|
|
||||||
|
VideoSwitch *switchData;
|
||||||
|
};
|
||||||
@@ -21,9 +21,11 @@
|
|||||||
#include "switch-transitions.hpp"
|
#include "switch-transitions.hpp"
|
||||||
#include "switch-window.hpp"
|
#include "switch-window.hpp"
|
||||||
#include "switch-sequence.hpp"
|
#include "switch-sequence.hpp"
|
||||||
|
#include "switch-video.hpp"
|
||||||
|
|
||||||
constexpr auto default_interval = 300;
|
constexpr auto default_interval = 300;
|
||||||
constexpr auto previous_scene_name = "Previous Scene";
|
constexpr auto previous_scene_name = "Previous Scene";
|
||||||
|
constexpr auto current_transition_name = "Current Transition";
|
||||||
|
|
||||||
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch;
|
||||||
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior;
|
||||||
@@ -84,6 +86,7 @@ struct SwitcherData {
|
|||||||
|
|
||||||
std::vector<std::string> ignoreWindowsSwitches;
|
std::vector<std::string> ignoreWindowsSwitches;
|
||||||
|
|
||||||
|
bool uninterruptibleSceneSequenceActive = false;
|
||||||
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
|
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
|
||||||
|
|
||||||
std::deque<RandomSwitch> randomSwitches;
|
std::deque<RandomSwitch> randomSwitches;
|
||||||
@@ -100,7 +103,6 @@ struct SwitcherData {
|
|||||||
|
|
||||||
std::deque<SceneTransition> sceneTransitions;
|
std::deque<SceneTransition> sceneTransitions;
|
||||||
std::deque<DefaultSceneTransition> defaultSceneTransitions;
|
std::deque<DefaultSceneTransition> defaultSceneTransitions;
|
||||||
bool checkedDefTransition = false;
|
|
||||||
|
|
||||||
std::deque<MediaSwitch> mediaSwitches;
|
std::deque<MediaSwitch> mediaSwitches;
|
||||||
|
|
||||||
@@ -112,12 +114,15 @@ struct SwitcherData {
|
|||||||
std::deque<AudioSwitch> audioSwitches;
|
std::deque<AudioSwitch> audioSwitches;
|
||||||
AudioSwitchFallback audioFallback;
|
AudioSwitchFallback audioFallback;
|
||||||
|
|
||||||
|
std::deque<VideoSwitch> videoSwitches;
|
||||||
|
|
||||||
std::deque<SceneGroup> sceneGroups;
|
std::deque<SceneGroup> sceneGroups;
|
||||||
|
|
||||||
std::vector<int> functionNamesByPriority = std::vector<int>{
|
std::vector<int> functionNamesByPriority = std::vector<int>{
|
||||||
default_priority_0, default_priority_1, default_priority_2,
|
default_priority_0, default_priority_1, default_priority_2,
|
||||||
default_priority_3, default_priority_4, default_priority_5,
|
default_priority_3, default_priority_4, default_priority_5,
|
||||||
default_priority_6, default_priority_7, default_priority_8};
|
default_priority_6, default_priority_7, default_priority_8,
|
||||||
|
default_priority_9};
|
||||||
|
|
||||||
struct ThreadPrio {
|
struct ThreadPrio {
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -160,13 +165,17 @@ struct SwitcherData {
|
|||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
bool sceneChangedDuringWait();
|
bool sceneChangedDuringWait();
|
||||||
|
|
||||||
bool prioFuncsValid();
|
bool prioFuncsValid();
|
||||||
|
|
||||||
|
bool tabOrderValid();
|
||||||
|
void resetTabOrder();
|
||||||
|
|
||||||
void writeSceneInfoToFile();
|
void writeSceneInfoToFile();
|
||||||
void writeToStatusFile(QString status);
|
void writeToStatusFile(QString status);
|
||||||
bool checkPause();
|
|
||||||
void checkDefaultSceneTransitions(bool &match,
|
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||||
OBSWeakSource &transition);
|
int &linger);
|
||||||
void setCurrentDefTransition(OBSWeakSource &transition);
|
|
||||||
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition, int &linger);
|
OBSWeakSource &transition, int &linger);
|
||||||
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||||
@@ -191,10 +200,14 @@ struct SwitcherData {
|
|||||||
OBSWeakSource &transition);
|
OBSWeakSource &transition);
|
||||||
void checkAudioSwitchFallback(OBSWeakSource &scene,
|
void checkAudioSwitchFallback(OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition);
|
OBSWeakSource &transition);
|
||||||
|
void checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition);
|
||||||
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
void checkNoMatchSwitch(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition, int &sleep);
|
OBSWeakSource &transition, int &sleep);
|
||||||
void checkSwitchCooldown(bool &match);
|
void checkSwitchCooldown(bool &match);
|
||||||
void checkTriggers();
|
void checkTriggers();
|
||||||
|
bool checkPause();
|
||||||
|
void checkDefaultSceneTransitions();
|
||||||
|
|
||||||
void saveSettings(obs_data_t *obj);
|
void saveSettings(obs_data_t *obj);
|
||||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||||
@@ -211,6 +224,7 @@ struct SwitcherData {
|
|||||||
void saveAudioSwitches(obs_data_t *obj);
|
void saveAudioSwitches(obs_data_t *obj);
|
||||||
void saveSceneGroups(obs_data_t *obj);
|
void saveSceneGroups(obs_data_t *obj);
|
||||||
void saveSceneTriggers(obs_data_t *obj);
|
void saveSceneTriggers(obs_data_t *obj);
|
||||||
|
void saveVideoSwitches(obs_data_t *obj);
|
||||||
void saveGeneralSettings(obs_data_t *obj);
|
void saveGeneralSettings(obs_data_t *obj);
|
||||||
void saveHotkeys(obs_data_t *obj);
|
void saveHotkeys(obs_data_t *obj);
|
||||||
void saveVersion(obs_data_t *obj, std::string currentVersion);
|
void saveVersion(obs_data_t *obj, std::string currentVersion);
|
||||||
@@ -231,6 +245,7 @@ struct SwitcherData {
|
|||||||
void loadAudioSwitches(obs_data_t *obj);
|
void loadAudioSwitches(obs_data_t *obj);
|
||||||
void loadSceneGroups(obs_data_t *obj);
|
void loadSceneGroups(obs_data_t *obj);
|
||||||
void loadSceneTriggers(obs_data_t *obj);
|
void loadSceneTriggers(obs_data_t *obj);
|
||||||
|
void loadVideoSwitches(obs_data_t *obj);
|
||||||
void loadGeneralSettings(obs_data_t *obj);
|
void loadGeneralSettings(obs_data_t *obj);
|
||||||
void loadHotkeys(obs_data_t *obj);
|
void loadHotkeys(obs_data_t *obj);
|
||||||
|
|
||||||
|
|||||||
@@ -51,27 +51,26 @@ OBSWeakSource SceneGroup::getNextSceneTime()
|
|||||||
OBSWeakSource SceneGroup::getNextSceneRandom()
|
OBSWeakSource SceneGroup::getNextSceneRandom()
|
||||||
{
|
{
|
||||||
if (scenes.size() == 1) {
|
if (scenes.size() == 1) {
|
||||||
return *scenes.begin();
|
currentIdx = 0;
|
||||||
|
return scenes[currentIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<OBSWeakSource> rs(scenes);
|
int rIdx = lastRandomScene;
|
||||||
std::random_device rng;
|
do {
|
||||||
std::mt19937 urng(rng());
|
rIdx = rand() % scenes.size();
|
||||||
std::shuffle(rs.begin(), rs.end(), urng);
|
} while (rIdx == lastRandomScene);
|
||||||
for (OBSWeakSource &s : rs) {
|
|
||||||
if (s == lastRandomScene) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastRandomScene = s;
|
lastRandomScene = rIdx;
|
||||||
|
currentIdx = rIdx;
|
||||||
|
|
||||||
return s;
|
return scenes[currentIdx];
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSWeakSource SceneGroup::getCurrentScene()
|
OBSWeakSource SceneGroup::getCurrentScene()
|
||||||
{
|
{
|
||||||
|
if (scenes.size() < currentIdx + 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return scenes[currentIdx];
|
return scenes[currentIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,15 @@ void SceneTrigger::logMatch()
|
|||||||
case sceneTriggerAction::MUTE_SOURCE:
|
case sceneTriggerAction::MUTE_SOURCE:
|
||||||
actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")";
|
actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")";
|
||||||
break;
|
break;
|
||||||
|
case sceneTriggerAction::UNMUTE_SOURCE:
|
||||||
|
actionName = "UNMUTE (" + GetWeakSourceName(audioSource) + ")";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::START_SWITCHER:
|
||||||
|
actionName = "START SCENE SWITCHER";
|
||||||
|
break;
|
||||||
|
case sceneTriggerAction::STOP_SWITCHER:
|
||||||
|
actionName = "STOP SCENE SWITCHER";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
actionName = "UNKOWN";
|
actionName = "UNKOWN";
|
||||||
break;
|
break;
|
||||||
@@ -176,24 +185,35 @@ void frontEndActionThread(sceneTriggerAction action, double delay)
|
|||||||
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
case sceneTriggerAction::STOP_REPLAY_BUFFER:
|
||||||
obs_frontend_replay_buffer_stop();
|
obs_frontend_replay_buffer_stop();
|
||||||
break;
|
break;
|
||||||
case sceneTriggerAction::MUTE_SOURCE:
|
|
||||||
obs_frontend_replay_buffer_stop();
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
blog(LOG_WARNING, "ignoring unexpected frontend action '%d'",
|
||||||
|
static_cast<int>(action));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void muteThread(OBSWeakSource source, double delay)
|
void muteThread(OBSWeakSource source, double delay, bool mute)
|
||||||
{
|
{
|
||||||
long long mil = delay * 1000;
|
long long mil = delay * 1000;
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
|
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
|
||||||
|
|
||||||
auto s = obs_weak_source_get_source(source);
|
auto s = obs_weak_source_get_source(source);
|
||||||
obs_source_set_muted(s, true);
|
obs_source_set_muted(s, mute);
|
||||||
obs_source_release(s);
|
obs_source_release(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void statusThread(double delay, bool stop)
|
||||||
|
{
|
||||||
|
long long mil = delay * 1000;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(mil));
|
||||||
|
|
||||||
|
if (stop) {
|
||||||
|
switcher->Stop();
|
||||||
|
} else {
|
||||||
|
switcher->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isFrontendAction(sceneTriggerAction triggerAction)
|
bool isFrontendAction(sceneTriggerAction triggerAction)
|
||||||
{
|
{
|
||||||
return triggerAction == sceneTriggerAction::START_RECORDING ||
|
return triggerAction == sceneTriggerAction::START_RECORDING ||
|
||||||
@@ -206,6 +226,18 @@ bool isFrontendAction(sceneTriggerAction triggerAction)
|
|||||||
triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER;
|
triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAudioAction(sceneTriggerAction t)
|
||||||
|
{
|
||||||
|
return t == sceneTriggerAction::MUTE_SOURCE ||
|
||||||
|
t == sceneTriggerAction::UNMUTE_SOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSwitcherStatusAction(sceneTriggerAction t)
|
||||||
|
{
|
||||||
|
return t == sceneTriggerAction::START_SWITCHER ||
|
||||||
|
t == sceneTriggerAction::STOP_SWITCHER;
|
||||||
|
}
|
||||||
|
|
||||||
void SceneTrigger::performAction()
|
void SceneTrigger::performAction()
|
||||||
{
|
{
|
||||||
if (triggerAction == sceneTriggerAction::NONE) {
|
if (triggerAction == sceneTriggerAction::NONE) {
|
||||||
@@ -216,20 +248,23 @@ void SceneTrigger::performAction()
|
|||||||
|
|
||||||
if (isFrontendAction(triggerAction)) {
|
if (isFrontendAction(triggerAction)) {
|
||||||
t = std::thread(frontEndActionThread, triggerAction, duration);
|
t = std::thread(frontEndActionThread, triggerAction, duration);
|
||||||
|
} else if (isAudioAction(triggerAction)) {
|
||||||
|
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
|
||||||
|
t = std::thread(muteThread, audioSource, duration, mute);
|
||||||
|
} else if (isSwitcherStatusAction(triggerAction)) {
|
||||||
|
bool stop = triggerAction == sceneTriggerAction::STOP_SWITCHER;
|
||||||
|
t = std::thread(statusThread, duration, stop);
|
||||||
} else {
|
} else {
|
||||||
t = std::thread(muteThread, audioSource, duration);
|
blog(LOG_WARNING, "ignoring unkown action '%d'",
|
||||||
|
static_cast<int>(triggerAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
t.detach();
|
t.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneTrigger::checkMatch(OBSWeakSource previousScene)
|
bool SceneTrigger::checkMatch(OBSWeakSource currentScene,
|
||||||
|
OBSWeakSource previousScene)
|
||||||
{
|
{
|
||||||
OBSSource source = obs_frontend_get_current_scene();
|
|
||||||
OBSWeakSource currentScene = obs_source_get_weak_source(source);
|
|
||||||
obs_source_release(source);
|
|
||||||
obs_weak_source_release(currentScene);
|
|
||||||
|
|
||||||
switch (triggerType) {
|
switch (triggerType) {
|
||||||
case sceneTriggerType::NONE:
|
case sceneTriggerType::NONE:
|
||||||
return false;
|
return false;
|
||||||
@@ -245,16 +280,26 @@ bool SceneTrigger::checkMatch(OBSWeakSource previousScene)
|
|||||||
|
|
||||||
void SwitcherData::checkTriggers()
|
void SwitcherData::checkTriggers()
|
||||||
{
|
{
|
||||||
if (SceneTrigger::pause || stop) {
|
if (SceneTrigger::pause) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OBSSource source = obs_frontend_get_current_scene();
|
||||||
|
OBSWeakSource currentScene = obs_source_get_weak_source(source);
|
||||||
|
|
||||||
for (auto &t : sceneTriggers) {
|
for (auto &t : sceneTriggers) {
|
||||||
if (t.checkMatch(previousScene)) {
|
if (stop && !isSwitcherStatusAction(t.triggerAction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.checkMatch(currentScene, previousScene)) {
|
||||||
t.logMatch();
|
t.logMatch();
|
||||||
t.performAction();
|
t.performAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_source_release(source);
|
||||||
|
obs_weak_source_release(currentScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::saveSceneTriggers(obs_data_t *obj)
|
void SwitcherData::saveSceneTriggers(obs_data_t *obj)
|
||||||
@@ -395,19 +440,12 @@ void AdvSceneSwitcher::setupTriggerTab()
|
|||||||
|
|
||||||
void SceneTrigger::save(obs_data_t *obj)
|
void SceneTrigger::save(obs_data_t *obj)
|
||||||
{
|
{
|
||||||
obs_source_t *sceneSource = obs_weak_source_get_source(scene);
|
obs_data_set_string(obj, "scene", GetWeakSourceName(scene).c_str());
|
||||||
const char *sceneName = obs_source_get_name(sceneSource);
|
|
||||||
obs_data_set_string(obj, "scene", sceneName);
|
|
||||||
obs_source_release(sceneSource);
|
|
||||||
|
|
||||||
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
|
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
|
||||||
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
|
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
|
||||||
obs_data_set_double(obj, "duration", duration);
|
obs_data_set_double(obj, "duration", duration);
|
||||||
|
obs_data_set_string(obj, "audioSource",
|
||||||
obs_source_t *source = obs_weak_source_get_source(audioSource);
|
GetWeakSourceName(audioSource).c_str());
|
||||||
const char *audioSourceName = obs_source_get_name(source);
|
|
||||||
obs_data_set_string(obj, "audioSource", audioSourceName);
|
|
||||||
obs_source_release(source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneTrigger::load(obs_data_t *obj)
|
void SceneTrigger::load(obs_data_t *obj)
|
||||||
@@ -425,10 +463,13 @@ void SceneTrigger::load(obs_data_t *obj)
|
|||||||
audioSource = GetWeakSourceByName(audioSourceName);
|
audioSource = GetWeakSourceByName(audioSourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void populateTriggers(QComboBox *list)
|
static inline void populateTriggers(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(obs_module_text(
|
AdvSceneSwitcher::addSelectionEntry(
|
||||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
|
list,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none"));
|
||||||
|
|
||||||
list->addItem(obs_module_text(
|
list->addItem(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive"));
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive"));
|
||||||
list->addItem(obs_module_text(
|
list->addItem(obs_module_text(
|
||||||
@@ -439,8 +480,11 @@ inline void populateTriggers(QComboBox *list)
|
|||||||
|
|
||||||
inline void populateActions(QComboBox *list)
|
inline void populateActions(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(obs_module_text(
|
AdvSceneSwitcher::addSelectionEntry(
|
||||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
|
list,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none"));
|
||||||
|
|
||||||
list->addItem(obs_module_text(
|
list->addItem(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording"));
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording"));
|
||||||
list->addItem(obs_module_text(
|
list->addItem(obs_module_text(
|
||||||
@@ -459,6 +503,12 @@ inline void populateActions(QComboBox *list)
|
|||||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer"));
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer"));
|
||||||
list->addItem(obs_module_text(
|
list->addItem(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource"));
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unmuteSource"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startSwitcher"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopSwitcher"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
||||||
@@ -495,7 +545,7 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
|||||||
audioSources->setCurrentText(
|
audioSources->setCurrentText(
|
||||||
GetWeakSourceName(s->audioSource).c_str());
|
GetWeakSourceName(s->audioSource).c_str());
|
||||||
|
|
||||||
if (s->triggerAction == sceneTriggerAction::MUTE_SOURCE) {
|
if (isAudioAction(s->triggerAction)) {
|
||||||
audioSources->show();
|
audioSources->show();
|
||||||
} else {
|
} else {
|
||||||
audioSources->hide();
|
audioSources->hide();
|
||||||
@@ -560,7 +610,7 @@ void SceneTriggerWidget::TriggerActionChanged(int index)
|
|||||||
static_cast<sceneTriggerAction>(index);
|
static_cast<sceneTriggerAction>(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switchData->triggerAction == sceneTriggerAction::MUTE_SOURCE) {
|
if (isAudioAction(switchData->triggerAction)) {
|
||||||
audioSources->show();
|
audioSources->show();
|
||||||
} else {
|
} else {
|
||||||
audioSources->hide();
|
audioSources->hide();
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ void swap(AudioSwitch &first, AudioSwitch &second)
|
|||||||
second.resetVolmeter();
|
second.resetVolmeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void populateConditionSelection(QComboBox *list)
|
static inline void populateConditionSelection(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.audioTab.condition.above"));
|
obs_module_text("AdvSceneSwitcher.audioTab.condition.above"));
|
||||||
|
|||||||
@@ -118,29 +118,37 @@ void SwitcherData::writeToStatusFile(QString msg)
|
|||||||
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkSwitchInfoFromFile(bool &match, OBSWeakSource &scene,
|
||||||
OBSWeakSource &transition)
|
OBSWeakSource &transition)
|
||||||
{
|
{
|
||||||
if (!fileIO.readEnabled || fileIO.readPath.empty()) {
|
if (!fileIO.readEnabled || fileIO.readPath.empty() ||
|
||||||
|
FileSwitch::pause) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile file(QString::fromStdString(fileIO.readPath));
|
QFile file(QString::fromStdString(fileIO.readPath));
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
QTextStream in(&file);
|
return;
|
||||||
QString sceneStr = in.readLine();
|
|
||||||
obs_source_t *sceneRead =
|
|
||||||
obs_get_source_by_name(sceneStr.toUtf8().constData());
|
|
||||||
if (sceneRead) {
|
|
||||||
obs_weak_source_t *sceneReadWs =
|
|
||||||
obs_source_get_weak_source(sceneRead);
|
|
||||||
|
|
||||||
match = true;
|
|
||||||
scene = sceneReadWs;
|
|
||||||
transition = nullptr;
|
|
||||||
|
|
||||||
obs_weak_source_release(sceneReadWs);
|
|
||||||
obs_source_release(sceneRead);
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
|
||||||
|
QString sceneStr = in.readLine();
|
||||||
|
OBSWeakSource sceneRead = GetWeakSourceByQString(sceneStr);
|
||||||
|
|
||||||
|
QString transitionStr = in.readLine();
|
||||||
|
OBSWeakSource transitionRead =
|
||||||
|
GetWeakTransitionByQString(transitionStr);
|
||||||
|
|
||||||
|
if (sceneRead) {
|
||||||
|
match = true;
|
||||||
|
scene = sceneRead;
|
||||||
|
transition = transitionRead;
|
||||||
|
|
||||||
|
vblog(LOG_INFO,
|
||||||
|
"match for 'file' - read scene '%s' and transition '%s' from file '%s'",
|
||||||
|
sceneStr.toUtf8().constData(),
|
||||||
|
transitionStr.toUtf8().constData(),
|
||||||
|
fileIO.readPath.c_str());
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ bool SceneSwitcherEntry::initialized()
|
|||||||
{
|
{
|
||||||
return (usePreviousScene || WeakSourceValid(scene) ||
|
return (usePreviousScene || WeakSourceValid(scene) ||
|
||||||
SceneGroupValid(group)) &&
|
SceneGroupValid(group)) &&
|
||||||
transition;
|
(useCurrentTransition || transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SceneSwitcherEntry::valid()
|
bool SceneSwitcherEntry::valid()
|
||||||
{
|
{
|
||||||
return !initialized() || ((usePreviousScene || WeakSourceValid(scene) ||
|
return !initialized() ||
|
||||||
SceneGroupValid(group)) &&
|
((usePreviousScene || WeakSourceValid(scene) ||
|
||||||
WeakSourceValid(transition));
|
SceneGroupValid(group)) &&
|
||||||
|
(useCurrentTransition || WeakSourceValid(transition)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSwitcherEntry::logMatchScene()
|
void SceneSwitcherEntry::logMatchScene()
|
||||||
@@ -82,8 +83,11 @@ void SceneSwitcherEntry::save(obs_data_t *obj, const char *targetTypeSaveName,
|
|||||||
|
|
||||||
obs_data_set_string(obj, targetSaveName, targetName.c_str());
|
obs_data_set_string(obj, targetSaveName, targetName.c_str());
|
||||||
|
|
||||||
obs_data_set_string(obj, transitionSaveName,
|
std::string transitionName = current_transition_name;
|
||||||
GetWeakSourceName(transition).c_str());
|
if (!useCurrentTransition) {
|
||||||
|
transitionName = GetWeakSourceName(transition);
|
||||||
|
}
|
||||||
|
obs_data_set_string(obj, transitionSaveName, transitionName.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName,
|
void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName,
|
||||||
@@ -104,11 +108,14 @@ void SceneSwitcherEntry::load(obs_data_t *obj, const char *targetTypeLoadName,
|
|||||||
group = GetSceneGroupByName(targetName);
|
group = GetSceneGroupByName(targetName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usePreviousScene = strcmp(targetName, previous_scene_name) == 0;
|
||||||
|
|
||||||
const char *transitionName =
|
const char *transitionName =
|
||||||
obs_data_get_string(obj, transitionLoadName);
|
obs_data_get_string(obj, transitionLoadName);
|
||||||
transition = GetWeakTransitionByName(transitionName);
|
transition = GetWeakTransitionByName(transitionName);
|
||||||
|
|
||||||
usePreviousScene = strcmp(targetName, previous_scene_name) == 0;
|
useCurrentTransition =
|
||||||
|
strcmp(transitionName, current_transition_name) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitchWidget::SceneGroupAdd(const QString &name)
|
void SwitchWidget::SceneGroupAdd(const QString &name)
|
||||||
@@ -166,7 +173,8 @@ void SwitchWidget::SceneGroupRename(const QString &oldName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
||||||
bool usePreviousScene, bool addSceneGroup)
|
bool usePreviousScene, bool addSceneGroup,
|
||||||
|
bool addCurrentTransition)
|
||||||
{
|
{
|
||||||
scenes = new QComboBox();
|
scenes = new QComboBox();
|
||||||
transitions = new QComboBox();
|
transitions = new QComboBox();
|
||||||
@@ -194,7 +202,8 @@ SwitchWidget::SwitchWidget(QWidget *parent, SceneSwitcherEntry *s,
|
|||||||
|
|
||||||
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene,
|
AdvSceneSwitcher::populateSceneSelection(scenes, usePreviousScene,
|
||||||
addSceneGroup);
|
addSceneGroup);
|
||||||
AdvSceneSwitcher::populateTransitionSelection(transitions);
|
AdvSceneSwitcher::populateTransitionSelection(transitions,
|
||||||
|
addCurrentTransition);
|
||||||
|
|
||||||
switchData = s;
|
switchData = s;
|
||||||
showSwitchData();
|
showSwitchData();
|
||||||
@@ -218,6 +227,10 @@ void SwitchWidget::showSwitchData()
|
|||||||
|
|
||||||
transitions->setCurrentText(
|
transitions->setCurrentText(
|
||||||
GetWeakSourceName(switchData->transition).c_str());
|
GetWeakSourceName(switchData->transition).c_str());
|
||||||
|
if (switchData->useCurrentTransition) {
|
||||||
|
transitions->setCurrentText(
|
||||||
|
obs_module_text("AdvSceneSwitcher.currentTransition"));
|
||||||
|
}
|
||||||
|
|
||||||
if (switchData->usePreviousScene) {
|
if (switchData->usePreviousScene) {
|
||||||
scenes->setCurrentText(obs_module_text(
|
scenes->setCurrentText(obs_module_text(
|
||||||
@@ -278,4 +291,5 @@ void SwitchWidget::TransitionChanged(const QString &text)
|
|||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->transition = GetWeakTransitionByQString(text);
|
switchData->transition = GetWeakTransitionByQString(text);
|
||||||
|
switchData->useCurrentTransition = switchData->transition == nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ void resetPause()
|
|||||||
IdleData::pause = false;
|
IdleData::pause = false;
|
||||||
SceneSequenceSwitch::pause = false;
|
SceneSequenceSwitch::pause = false;
|
||||||
AudioSwitch::pause = false;
|
AudioSwitch::pause = false;
|
||||||
|
VideoSwitch::pause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPauseTarget(PauseTarget &target, bool &verbose)
|
void setPauseTarget(PauseTarget &target, bool &verbose)
|
||||||
@@ -139,6 +140,10 @@ void setPauseTarget(PauseTarget &target, bool &verbose)
|
|||||||
vblog(LOG_INFO, "pause audio switching");
|
vblog(LOG_INFO, "pause audio switching");
|
||||||
AudioSwitch::pause = true;
|
AudioSwitch::pause = true;
|
||||||
break;
|
break;
|
||||||
|
case PauseTarget::Video:
|
||||||
|
vblog(LOG_INFO, "pause video switching");
|
||||||
|
VideoSwitch::pause = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +345,7 @@ void populatePauseTargets(QComboBox *list)
|
|||||||
list->addItem(
|
list->addItem(
|
||||||
obs_module_text("AdvSceneSwitcher.sceneSequenceTab.title"));
|
obs_module_text("AdvSceneSwitcher.sceneSequenceTab.title"));
|
||||||
list->addItem(obs_module_text("AdvSceneSwitcher.audioTab.title"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.audioTab.title"));
|
||||||
|
list->addItem(obs_module_text("AdvSceneSwitcher.VideoTab.title"));
|
||||||
}
|
}
|
||||||
|
|
||||||
PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
|
PauseEntryWidget::PauseEntryWidget(QWidget *parent, PauseEntry *s)
|
||||||
|
|||||||
@@ -55,15 +55,18 @@ void AdvSceneSwitcher::on_priorityDown_clicked()
|
|||||||
|
|
||||||
bool SwitcherData::prioFuncsValid()
|
bool SwitcherData::prioFuncsValid()
|
||||||
{
|
{
|
||||||
auto it = std::unique(functionNamesByPriority.begin(),
|
auto fNBPCopy = functionNamesByPriority;
|
||||||
functionNamesByPriority.end());
|
|
||||||
bool wasUnique = (it == functionNamesByPriority.end());
|
std::sort(fNBPCopy.begin(), fNBPCopy.end());
|
||||||
|
auto it = std::unique(fNBPCopy.begin(), fNBPCopy.end());
|
||||||
|
bool wasUnique = (it == fNBPCopy.end());
|
||||||
|
|
||||||
if (!wasUnique) {
|
if (!wasUnique) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int p : functionNamesByPriority) {
|
for (int p : functionNamesByPriority) {
|
||||||
if (p < 0 || p > 8) {
|
if (p < 0 || p > 9) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -353,7 +353,8 @@ ScreenRegionWidget::ScreenRegionWidget(QWidget *parent, ScreenRegionSwitch *s)
|
|||||||
AdvSceneSwitcher::populateSceneSelection(
|
AdvSceneSwitcher::populateSceneSelection(
|
||||||
excludeScenes, false, false, true,
|
excludeScenes, false, false, true,
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None"));
|
"AdvSceneSwitcher.screenRegionTab.excludeScenes.None"),
|
||||||
|
true);
|
||||||
|
|
||||||
if (s) {
|
if (s) {
|
||||||
excludeScenes->setCurrentText(
|
excludeScenes->setCurrentText(
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "headers/advanced-scene-switcher.hpp"
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
constexpr auto max_extend_text_size = 150;
|
||||||
|
|
||||||
bool SceneSequenceSwitch::pause = false;
|
bool SceneSequenceSwitch::pause = false;
|
||||||
static QMetaObject::Connection addPulse;
|
static QMetaObject::Connection addPulse;
|
||||||
|
|
||||||
@@ -28,20 +30,10 @@ void AdvSceneSwitcher::on_sceneSequenceRemove_clicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Might be in waiting state of sequence
|
|
||||||
// causing invalid access after wakeup
|
|
||||||
// thus we need to stop the main thread before delete
|
|
||||||
bool wasRunning = !switcher->stop;
|
|
||||||
switcher->Stop();
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
int idx = ui->sceneSequenceSwitches->currentRow();
|
int idx = ui->sceneSequenceSwitches->currentRow();
|
||||||
auto &switches = switcher->sceneSequenceSwitches;
|
auto &switches = switcher->sceneSequenceSwitches;
|
||||||
switches.erase(switches.begin() + idx);
|
switches.erase(switches.begin() + idx);
|
||||||
|
|
||||||
if (wasRunning) {
|
|
||||||
switcher->Start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete item;
|
delete item;
|
||||||
@@ -140,18 +132,8 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Might be in waiting state of sequence
|
|
||||||
// causing invalid access after wakeup
|
|
||||||
// thus we need to stop the main thread before delete
|
|
||||||
bool wasRunning = !switcher->stop;
|
|
||||||
switcher->Stop();
|
|
||||||
|
|
||||||
switcher->loadSceneSequenceSwitches(obj);
|
switcher->loadSceneSequenceSwitches(obj);
|
||||||
|
|
||||||
if (wasRunning) {
|
|
||||||
switcher->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
obs_data_release(obj);
|
obs_data_release(obj);
|
||||||
|
|
||||||
DisplayMessage(obs_module_text(
|
DisplayMessage(obs_module_text(
|
||||||
@@ -159,30 +141,42 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool matchInterruptible(SwitcherData *switcher, SceneSequenceSwitch &s)
|
void AdvSceneSwitcher::OpenSequenceExtendEdit(SequenceWidget *sw)
|
||||||
{
|
{
|
||||||
bool durationReached = s.matchCount * (switcher->interval / 1000.0) >=
|
QDialog edit;
|
||||||
s.delay;
|
SequenceWidget editWidget(this, sw->getSwitchData(), false, true,
|
||||||
|
false);
|
||||||
|
QHBoxLayout layout;
|
||||||
|
layout.setSizeConstraint(QLayout::SetFixedSize);
|
||||||
|
layout.addWidget(&editWidget);
|
||||||
|
edit.setLayout(&layout);
|
||||||
|
edit.setWindowTitle(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneSequenceTab.extendEdit"));
|
||||||
|
edit.exec();
|
||||||
|
|
||||||
s.matchCount++;
|
sw->UpdateWidgetStatus(true);
|
||||||
|
|
||||||
if (durationReached) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool matchUninterruptible(SwitcherData *switcher, SceneSequenceSwitch &s,
|
void AdvSceneSwitcher::on_sequenceEdit_clicked()
|
||||||
obs_source_t *currentSource, int &linger)
|
|
||||||
{
|
{
|
||||||
// scene was already active for the previous cycle so remove this time
|
int index = ui->sceneSequenceSwitches->currentRow();
|
||||||
int dur = s.delay * 1000 - switcher->interval;
|
if (index == -1) {
|
||||||
if (dur > 0) {
|
return;
|
||||||
switcher->waitScene = currentSource;
|
|
||||||
linger = dur;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
SequenceWidget *currentWidget =
|
||||||
|
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
||||||
|
ui->sceneSequenceSwitches->item(index));
|
||||||
|
|
||||||
|
OpenSequenceExtendEdit(currentWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_sceneSequenceSwitches_itemDoubleClicked(
|
||||||
|
QListWidgetItem *item)
|
||||||
|
{
|
||||||
|
SequenceWidget *currentWidget =
|
||||||
|
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(item);
|
||||||
|
OpenSequenceExtendEdit(currentWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||||
@@ -192,38 +186,53 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
obs_weak_source_t *currentScene =
|
||||||
|
obs_source_get_weak_source(currentSceneSource);
|
||||||
|
|
||||||
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
||||||
if (!s.initialized()) {
|
// Continue the active uninterruptible sequence and skip others
|
||||||
|
if (uninterruptibleSceneSequenceActive &&
|
||||||
|
s.activeSequence == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.startScene == ws) {
|
bool matched = s.checkMatch(currentScene, linger);
|
||||||
if (!match) {
|
|
||||||
if (s.interruptible) {
|
|
||||||
match = matchInterruptible(switcher, s);
|
|
||||||
} else {
|
|
||||||
match = matchUninterruptible(
|
|
||||||
switcher, s, currentSource,
|
|
||||||
linger);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
if (!match && matched) {
|
||||||
scene = s.getScene();
|
match = matched;
|
||||||
transition = s.transition;
|
|
||||||
if (switcher->verbose) {
|
if (s.activeSequence) {
|
||||||
s.logMatch();
|
scene = s.activeSequence->getScene();
|
||||||
}
|
transition = s.activeSequence->transition;
|
||||||
|
} else {
|
||||||
|
scene = s.getScene();
|
||||||
|
transition = s.transition;
|
||||||
|
if (verbose) {
|
||||||
|
s.logMatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
s.matchCount = 0;
|
s.advanceActiveSequence();
|
||||||
|
if (verbose) {
|
||||||
|
s.logAdvanceSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore other switching methods if sequence is not
|
||||||
|
// interruptible and has not reached its end
|
||||||
|
if (s.activeSequence) {
|
||||||
|
uninterruptibleSceneSequenceActive =
|
||||||
|
!s.interruptible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
if (!match) {
|
||||||
|
uninterruptibleSceneSequenceActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_release(currentSceneSource);
|
||||||
|
obs_weak_source_release(currentScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
||||||
@@ -291,18 +300,32 @@ bool SceneSequenceSwitch::valid()
|
|||||||
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
|
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSequenceSwitch::save(obs_data_t *obj)
|
void SceneSequenceSwitch::save(obs_data_t *obj, bool saveExt)
|
||||||
{
|
{
|
||||||
SceneSwitcherEntry::save(obj);
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_int(obj, "startTargetType",
|
||||||
|
static_cast<int>(startTargetType));
|
||||||
obs_data_set_string(obj, "startScene",
|
obs_data_set_string(obj, "startScene",
|
||||||
GetWeakSourceName(startScene).c_str());
|
GetWeakSourceName(startScene).c_str());
|
||||||
|
|
||||||
obs_data_set_double(obj, "delay", delay);
|
obs_data_set_double(obj, "delay", delay);
|
||||||
|
|
||||||
obs_data_set_int(obj, "delayMultiplier", delayMultiplier);
|
obs_data_set_int(obj, "delayMultiplier", delayMultiplier);
|
||||||
|
|
||||||
obs_data_set_bool(obj, "interruptible", interruptible);
|
obs_data_set_bool(obj, "interruptible", interruptible);
|
||||||
|
|
||||||
|
if (saveExt) {
|
||||||
|
auto cur = extendedSequence.get();
|
||||||
|
|
||||||
|
obs_data_array_t *extendScenes = obs_data_array_create();
|
||||||
|
while (cur) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
cur->save(array_obj, false);
|
||||||
|
obs_data_array_push_back(extendScenes, array_obj);
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "extendScenes", extendScenes);
|
||||||
|
obs_data_array_release(extendScenes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To be removed in future version
|
// To be removed in future version
|
||||||
@@ -341,14 +364,15 @@ bool loadOldScequence(obs_data_t *obj, SceneSequenceSwitch *s)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneSequenceSwitch::load(obs_data_t *obj)
|
void SceneSequenceSwitch::load(obs_data_t *obj, bool saveExt)
|
||||||
{
|
{
|
||||||
if (loadOldScequence(obj, this)) {
|
if (loadOldScequence(obj, this)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneSwitcherEntry::load(obj);
|
SceneSwitcherEntry::load(obj);
|
||||||
|
startTargetType = static_cast<SwitchTargetType>(
|
||||||
|
obs_data_get_int(obj, "startTargetType"));
|
||||||
const char *scene = obs_data_get_string(obj, "startScene");
|
const char *scene = obs_data_get_string(obj, "startScene");
|
||||||
startScene = GetWeakSourceByName(scene);
|
startScene = GetWeakSourceByName(scene);
|
||||||
|
|
||||||
@@ -360,6 +384,220 @@ void SceneSequenceSwitch::load(obs_data_t *obj)
|
|||||||
delayMultiplier = 1;
|
delayMultiplier = 1;
|
||||||
|
|
||||||
interruptible = obs_data_get_bool(obj, "interruptible");
|
interruptible = obs_data_get_bool(obj, "interruptible");
|
||||||
|
|
||||||
|
if (saveExt) {
|
||||||
|
auto cur = this;
|
||||||
|
|
||||||
|
obs_data_array_t *extendScenes =
|
||||||
|
obs_data_get_array(obj, "extendScenes");
|
||||||
|
size_t count = obs_data_array_count(extendScenes);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj =
|
||||||
|
obs_data_array_item(extendScenes, i);
|
||||||
|
|
||||||
|
cur->extendedSequence =
|
||||||
|
std::make_unique<SceneSequenceSwitch>();
|
||||||
|
|
||||||
|
cur->extendedSequence->load(array_obj, false);
|
||||||
|
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(extendScenes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneSequenceSwitch::reduce()
|
||||||
|
{
|
||||||
|
// Reset activeSequence just in case it was one of the deleted entries
|
||||||
|
activeSequence = nullptr;
|
||||||
|
|
||||||
|
if (!extendedSequence) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (extendedSequence->reduce()) {
|
||||||
|
extendedSequence.reset(nullptr);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneSequenceSwitch *SceneSequenceSwitch::extend()
|
||||||
|
{
|
||||||
|
if (extendedSequence) {
|
||||||
|
return extendedSequence->extend();
|
||||||
|
}
|
||||||
|
extendedSequence = std::make_unique<SceneSequenceSwitch>();
|
||||||
|
extendedSequence->startScene = scene;
|
||||||
|
return extendedSequence.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneSequenceSwitch::checkMatch(OBSWeakSource currentScene, int &linger,
|
||||||
|
SceneSequenceSwitch *root)
|
||||||
|
{
|
||||||
|
if (!initialized()) {
|
||||||
|
if (root) {
|
||||||
|
root->activeSequence = nullptr;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
if (activeSequence) {
|
||||||
|
return activeSequence->checkMatch(currentScene, linger, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startScene == currentScene) {
|
||||||
|
if (interruptible) {
|
||||||
|
match = checkDurationMatchInterruptible();
|
||||||
|
} else {
|
||||||
|
match = true;
|
||||||
|
prepareUninterruptibleMatch(currentScene, linger);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matchCount = 0;
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
root->activeSequence = nullptr;
|
||||||
|
logSequenceCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SceneSequenceSwitch::checkDurationMatchInterruptible()
|
||||||
|
{
|
||||||
|
bool durationReached = matchCount * (switcher->interval / 1000.0) >=
|
||||||
|
delay;
|
||||||
|
matchCount++;
|
||||||
|
if (durationReached) {
|
||||||
|
matchCount = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::prepareUninterruptibleMatch(
|
||||||
|
OBSWeakSource currentScene, int &linger)
|
||||||
|
{
|
||||||
|
int dur = delay * 1000;
|
||||||
|
if (dur > 0) {
|
||||||
|
switcher->waitScene = obs_weak_source_get_source(currentScene);
|
||||||
|
obs_source_release(switcher->waitScene);
|
||||||
|
linger = dur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::advanceActiveSequence()
|
||||||
|
{
|
||||||
|
// Set start Scene
|
||||||
|
OBSWeakSource currentSceneGroupScene = nullptr;
|
||||||
|
if (targetType == SwitchTargetType::SceneGroup && group) {
|
||||||
|
currentSceneGroupScene = group->getCurrentScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeSequence) {
|
||||||
|
activeSequence = activeSequence->extendedSequence.get();
|
||||||
|
} else {
|
||||||
|
activeSequence = extendedSequence.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeSequence) {
|
||||||
|
if (activeSequence->startTargetType ==
|
||||||
|
SwitchTargetType::SceneGroup) {
|
||||||
|
activeSequence->startScene = currentSceneGroupScene;
|
||||||
|
}
|
||||||
|
if (activeSequence->targetType == SwitchTargetType::Scene &&
|
||||||
|
!activeSequence->scene) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"cannot advance sequence - null scene set");
|
||||||
|
activeSequence = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (activeSequence->targetType ==
|
||||||
|
SwitchTargetType::SceneGroup &&
|
||||||
|
activeSequence->group &&
|
||||||
|
activeSequence->group->scenes.empty()) {
|
||||||
|
blog(LOG_WARNING,
|
||||||
|
"cannot advance sequence - no scenes specified in '%s'",
|
||||||
|
activeSequence->group->name.c_str());
|
||||||
|
activeSequence = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinit old matchCount value in case it was previously set
|
||||||
|
activeSequence->matchCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::logAdvanceSequence()
|
||||||
|
{
|
||||||
|
if (activeSequence) {
|
||||||
|
std::string targetName =
|
||||||
|
GetWeakSourceName(activeSequence->scene);
|
||||||
|
|
||||||
|
if (activeSequence->targetType ==
|
||||||
|
SwitchTargetType::SceneGroup &&
|
||||||
|
activeSequence->group) {
|
||||||
|
targetName = activeSequence->group->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
blog(LOG_INFO, "continuing sequence with '%s' -> '%s'",
|
||||||
|
GetWeakSourceName(activeSequence->startScene).c_str(),
|
||||||
|
targetName.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneSequenceSwitch::logSequenceCanceled()
|
||||||
|
{
|
||||||
|
blog(LOG_INFO, "unexpected scene change - cancel sequence");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString delayMultiplierToString(int delayMultiplier)
|
||||||
|
{
|
||||||
|
switch (delayMultiplier) {
|
||||||
|
case 1:
|
||||||
|
return obs_module_text("AdvSceneSwitcher.unit.secends");
|
||||||
|
case 60:
|
||||||
|
return obs_module_text("AdvSceneSwitcher.unit.minutes");
|
||||||
|
case 60 * 60:
|
||||||
|
return obs_module_text("AdvSceneSwitcher.unit.hours");
|
||||||
|
}
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ext = "";
|
||||||
|
|
||||||
|
ext = QString::number(s->delay / s->delayMultiplier) + " ";
|
||||||
|
ext += delayMultiplierToString(s->delayMultiplier);
|
||||||
|
|
||||||
|
QString sceneName = GetWeakSourceName(s->scene).c_str();
|
||||||
|
if (s->targetType == SwitchTargetType::SceneGroup && s->group) {
|
||||||
|
sceneName = QString::fromStdString(s->group->name);
|
||||||
|
}
|
||||||
|
if (sceneName.isEmpty()) {
|
||||||
|
sceneName = obs_module_text("AdvSceneSwitcher.selectScene");
|
||||||
|
}
|
||||||
|
ext += " -> [" + sceneName + "]";
|
||||||
|
|
||||||
|
if (ext.length() + curLen > max_extend_text_size) {
|
||||||
|
return "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->extendedSequence.get()) {
|
||||||
|
return ext +=
|
||||||
|
" | " + makeExtendText(s->extendedSequence.get(),
|
||||||
|
curLen + ext.length());
|
||||||
|
} else {
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void populateDelayUnits(QComboBox *list)
|
void populateDelayUnits(QComboBox *list)
|
||||||
@@ -369,14 +607,36 @@ void populateDelayUnits(QComboBox *list)
|
|||||||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
|
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||||
: SwitchWidget(parent, s, true, true)
|
bool extendSequence, bool editExtendMode,
|
||||||
|
bool showExtendText)
|
||||||
|
: SwitchWidget(parent, s, !extendSequence, true)
|
||||||
{
|
{
|
||||||
|
this->setParent(parent);
|
||||||
|
|
||||||
delay = new QDoubleSpinBox();
|
delay = new QDoubleSpinBox();
|
||||||
delayUnits = new QComboBox();
|
delayUnits = new QComboBox();
|
||||||
startScenes = new QComboBox();
|
startScenes = new QComboBox();
|
||||||
interruptible = new QCheckBox(obs_module_text(
|
interruptible = new QCheckBox(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneSequenceTab.interruptible"));
|
"AdvSceneSwitcher.sceneSequenceTab.interruptible"));
|
||||||
|
extendText = new QLabel();
|
||||||
|
extend = new QPushButton();
|
||||||
|
reduce = new QPushButton();
|
||||||
|
|
||||||
|
extend->setProperty("themeID",
|
||||||
|
QVariant(QStringLiteral("addIconSmall")));
|
||||||
|
reduce->setProperty("themeID",
|
||||||
|
QVariant(QStringLiteral("removeIconSmall")));
|
||||||
|
|
||||||
|
extend->setMaximumSize(22, 22);
|
||||||
|
reduce->setMaximumSize(22, 22);
|
||||||
|
|
||||||
|
// We need to extend the generic SwitchWidget::SceneChanged()
|
||||||
|
// with our own SequenceWidget::SceneChanged()
|
||||||
|
// so the old singal / slot needs to be disconnected
|
||||||
|
scenes->disconnect();
|
||||||
|
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||||
|
this, SLOT(SceneChanged(const QString &)));
|
||||||
|
|
||||||
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
|
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
|
||||||
SLOT(DelayChanged(double)));
|
SLOT(DelayChanged(double)));
|
||||||
@@ -388,48 +648,80 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
|
|||||||
QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(InterruptibleChanged(int)));
|
SLOT(InterruptibleChanged(int)));
|
||||||
|
|
||||||
|
QWidget::connect(extend, SIGNAL(clicked()), this,
|
||||||
|
SLOT(ExtendClicked()));
|
||||||
|
QWidget::connect(reduce, SIGNAL(clicked()), this,
|
||||||
|
SLOT(ReduceClicked()));
|
||||||
|
|
||||||
delay->setMaximum(99999.000000);
|
delay->setMaximum(99999.000000);
|
||||||
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
|
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
|
||||||
populateDelayUnits(delayUnits);
|
populateDelayUnits(delayUnits);
|
||||||
interruptible->setToolTip(obs_module_text(
|
interruptible->setToolTip(obs_module_text(
|
||||||
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));
|
"AdvSceneSwitcher.sceneSequenceTab.interruptibleHint"));
|
||||||
|
|
||||||
if (s) {
|
// The extended sequence widgets never exist on their own and are always
|
||||||
switch (s->delayMultiplier) {
|
// place inside a non-extend sequence widget
|
||||||
case 1:
|
if (extendSequence) {
|
||||||
delayUnits->setCurrentIndex(0);
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
break;
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
case 60:
|
{"{{scenes}}", scenes},
|
||||||
delayUnits->setCurrentIndex(1);
|
{"{{delay}}", delay},
|
||||||
break;
|
{"{{delayUnits}}", delayUnits},
|
||||||
case 3600:
|
{"{{transitions}}", transitions}};
|
||||||
delayUnits->setCurrentIndex(2);
|
placeWidgets(
|
||||||
break;
|
obs_module_text(
|
||||||
default:
|
"AdvSceneSwitcher.sceneSequenceTab.extendEntry"),
|
||||||
delayUnits->setCurrentIndex(0);
|
mainLayout, widgetPlaceholders);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
} else {
|
||||||
|
QHBoxLayout *startSequence = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{startScenes}}", startScenes},
|
||||||
|
{"{{scenes}}", scenes},
|
||||||
|
{"{{delay}}", delay},
|
||||||
|
{"{{delayUnits}}", delayUnits},
|
||||||
|
{"{{transitions}}", transitions},
|
||||||
|
{"{{interruptible}}", interruptible}};
|
||||||
|
placeWidgets(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.sceneSequenceTab.entry"),
|
||||||
|
startSequence, widgetPlaceholders);
|
||||||
|
|
||||||
|
// The exetend widgets placed here
|
||||||
|
extendSequenceLayout = new QVBoxLayout;
|
||||||
|
if (s) {
|
||||||
|
if (!editExtendMode) {
|
||||||
|
extendText->setText(makeExtendText(
|
||||||
|
s->extendedSequence.get()));
|
||||||
|
} else {
|
||||||
|
auto cur = s->extendedSequence.get();
|
||||||
|
while (cur != nullptr) {
|
||||||
|
extendSequenceLayout->addWidget(
|
||||||
|
new SequenceWidget(parent, cur,
|
||||||
|
true, true));
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
startScenes->setCurrentText(
|
|
||||||
GetWeakSourceName(s->startScene).c_str());
|
QHBoxLayout *extendSequenceControlsLayout = new QHBoxLayout;
|
||||||
interruptible->setChecked(s->interruptible);
|
if (editExtendMode) {
|
||||||
|
extendSequenceControlsLayout->addWidget(extend);
|
||||||
|
extendSequenceControlsLayout->addWidget(reduce);
|
||||||
|
}
|
||||||
|
extendSequenceControlsLayout->addStretch();
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
mainLayout->addLayout(startSequence);
|
||||||
|
mainLayout->addLayout(extendSequenceLayout);
|
||||||
|
mainLayout->addWidget(extendText);
|
||||||
|
mainLayout->addLayout(extendSequenceControlsLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
|
||||||
{"{{startScenes}}", startScenes},
|
|
||||||
{"{{scenes}}", scenes},
|
|
||||||
{"{{delay}}", delay},
|
|
||||||
{"{{delayUnits}}", delayUnits},
|
|
||||||
{"{{transitions}}", transitions},
|
|
||||||
{"{{interruptible}}", interruptible}};
|
|
||||||
placeWidgets(obs_module_text("AdvSceneSwitcher.sceneSequenceTab.entry"),
|
|
||||||
mainLayout, widgetPlaceholders);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
|
|
||||||
switchData = s;
|
switchData = s;
|
||||||
|
UpdateWidgetStatus(showExtendText);
|
||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
||||||
UpdateDelay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneSequenceSwitch *SequenceWidget::getSwitchData()
|
SceneSequenceSwitch *SequenceWidget::getSwitchData()
|
||||||
@@ -493,6 +785,32 @@ void SequenceWidget::DelayUnitsChanged(int idx)
|
|||||||
UpdateDelay();
|
UpdateDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::setExtendedSequenceStartScene()
|
||||||
|
{
|
||||||
|
switchData->extendedSequence->startScene = switchData->scene;
|
||||||
|
switchData->extendedSequence->startTargetType = SwitchTargetType::Scene;
|
||||||
|
|
||||||
|
if (switchData->targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
switchData->extendedSequence->startScene = nullptr;
|
||||||
|
switchData->extendedSequence->startTargetType =
|
||||||
|
SwitchTargetType::SceneGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::SceneChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchWidget::SceneChanged(text);
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
if (switchData->extendedSequence) {
|
||||||
|
setExtendedSequenceStartScene();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SequenceWidget::StartSceneChanged(const QString &text)
|
void SequenceWidget::StartSceneChanged(const QString &text)
|
||||||
{
|
{
|
||||||
if (loading || !switchData) {
|
if (loading || !switchData) {
|
||||||
@@ -511,4 +829,69 @@ void SequenceWidget::InterruptibleChanged(int state)
|
|||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->interruptible = state;
|
switchData->interruptible = state;
|
||||||
|
|
||||||
|
auto cur = switchData->extendedSequence.get();
|
||||||
|
while (cur != nullptr) {
|
||||||
|
cur->interruptible = state;
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::UpdateWidgetStatus(bool showExtendText)
|
||||||
|
{
|
||||||
|
if (showExtendText) {
|
||||||
|
extendText->setText(
|
||||||
|
makeExtendText(switchData->extendedSequence.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (switchData->delayMultiplier) {
|
||||||
|
case 1:
|
||||||
|
delayUnits->setCurrentIndex(0);
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
delayUnits->setCurrentIndex(1);
|
||||||
|
break;
|
||||||
|
case 3600:
|
||||||
|
delayUnits->setCurrentIndex(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
delayUnits->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
UpdateDelay();
|
||||||
|
|
||||||
|
startScenes->setCurrentText(
|
||||||
|
GetWeakSourceName(switchData->startScene).c_str());
|
||||||
|
interruptible->setChecked(switchData->interruptible);
|
||||||
|
|
||||||
|
SwitchWidget::showSwitchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::ExtendClicked()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
auto es = switchData->extend();
|
||||||
|
|
||||||
|
SequenceWidget *ew = new SequenceWidget(this->parentWidget(), es, true);
|
||||||
|
extendSequenceLayout->addWidget(ew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceWidget::ReduceClicked()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->reduce();
|
||||||
|
|
||||||
|
int count = extendSequenceLayout->count();
|
||||||
|
auto item = extendSequenceLayout->takeAt(count - 1);
|
||||||
|
if (item) {
|
||||||
|
item->widget()->setVisible(false);
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ void TimeSwitch::load(obs_data_t *obj)
|
|||||||
time = QTime::fromString(obs_data_get_string(obj, "time"));
|
time = QTime::fromString(obs_data_get_string(obj, "time"));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void populateTriggers(QComboBox *list)
|
static inline void populateTriggers(QComboBox *list)
|
||||||
{
|
{
|
||||||
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay"));
|
||||||
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays"));
|
list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays"));
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "headers/advanced-scene-switcher.hpp"
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
#include "headers/utility.hpp"
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
@@ -148,42 +150,28 @@ void AdvSceneSwitcher::on_defaultTransitionsDown_clicked()
|
|||||||
switcher->defaultSceneTransitions[index + 1]);
|
switcher->defaultSceneTransitions[index + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitcherData::checkDefaultSceneTransitions(bool &match,
|
void SwitcherData::checkDefaultSceneTransitions()
|
||||||
OBSWeakSource &transition)
|
|
||||||
{
|
{
|
||||||
if (checkedDefTransition || DefaultSceneTransition::pause) {
|
if (DefaultSceneTransition::pause || stop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
|
||||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
obs_weak_source_t *currentScene =
|
||||||
|
obs_source_get_weak_source(currentSceneSource);
|
||||||
for (DefaultSceneTransition &s : defaultSceneTransitions) {
|
|
||||||
if (s.scene == ws) {
|
|
||||||
if (!s.initialized()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match = true;
|
|
||||||
transition = s.transition;
|
|
||||||
|
|
||||||
|
for (auto &t : defaultSceneTransitions) {
|
||||||
|
if (t.checkMatch(currentScene)) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
s.logMatch();
|
t.logMatch();
|
||||||
}
|
}
|
||||||
|
t.setTransition();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
obs_source_release(currentSource);
|
|
||||||
obs_weak_source_release(ws);
|
|
||||||
|
|
||||||
checkedDefTransition = true;
|
obs_weak_source_release(currentScene);
|
||||||
}
|
obs_source_release(currentSceneSource);
|
||||||
|
|
||||||
void SwitcherData::setCurrentDefTransition(OBSWeakSource &transition)
|
|
||||||
{
|
|
||||||
obs_source_t *transitionSource = obs_weak_source_get_source(transition);
|
|
||||||
obs_frontend_set_current_transition(transitionSource);
|
|
||||||
obs_source_release(transitionSource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
void AdvSceneSwitcher::on_transitionOverridecheckBox_stateChanged(int state)
|
||||||
@@ -439,7 +427,7 @@ bool SceneTransition::valid()
|
|||||||
|
|
||||||
TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
|
TransitionSwitchWidget::TransitionSwitchWidget(QWidget *parent,
|
||||||
SceneTransition *s)
|
SceneTransition *s)
|
||||||
: SwitchWidget(parent, s, false)
|
: SwitchWidget(parent, s, false, false, false)
|
||||||
{
|
{
|
||||||
scenes2 = new QComboBox();
|
scenes2 = new QComboBox();
|
||||||
|
|
||||||
@@ -498,7 +486,7 @@ void TransitionSwitchWidget::Scene2Changed(const QString &text)
|
|||||||
|
|
||||||
DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
|
DefTransitionSwitchWidget::DefTransitionSwitchWidget(QWidget *parent,
|
||||||
DefaultSceneTransition *s)
|
DefaultSceneTransition *s)
|
||||||
: SwitchWidget(parent, s, false)
|
: SwitchWidget(parent, s, false, false, false)
|
||||||
{
|
{
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
@@ -533,3 +521,37 @@ void DefTransitionSwitchWidget::swapSwitchData(DefTransitionSwitchWidget *s1,
|
|||||||
s1->setSwitchData(s2->getSwitchData());
|
s1->setSwitchData(s2->getSwitchData());
|
||||||
s2->setSwitchData(t);
|
s2->setSwitchData(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DefaultSceneTransition::checkMatch(OBSWeakSource currentScene)
|
||||||
|
{
|
||||||
|
return scene == currentScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTransitionDelayed(OBSWeakSource transition)
|
||||||
|
{
|
||||||
|
// A hardcoded delay of 50 ms before switching transition type is
|
||||||
|
// necessary due to OBS_FRONTEND_EVENT_SCENE_CHANGED seemingly firing a
|
||||||
|
// bit too early and thus leading to canceled transitions.
|
||||||
|
//
|
||||||
|
// The same is to be the case for OBS_FRONTEND_EVENT_TRANSITION_STOPPED.
|
||||||
|
//
|
||||||
|
// 50 ms was chosen as it seems to avoid the problem mentioned above and
|
||||||
|
// becuase that is the minimum value which can be chosen for the scene
|
||||||
|
// switcher's check interval.
|
||||||
|
// Thus it can be made sure that the delayed setting of the transition
|
||||||
|
// does not interfere with any new scene changes triggered by the scene
|
||||||
|
// switcher
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
|
obs_source_t *transitionSource = obs_weak_source_get_source(transition);
|
||||||
|
obs_frontend_set_current_transition(transitionSource);
|
||||||
|
obs_source_release(transitionSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefaultSceneTransition::setTransition()
|
||||||
|
{
|
||||||
|
std::thread t;
|
||||||
|
t = std::thread(setTransitionDelayed, transition);
|
||||||
|
t.detach();
|
||||||
|
}
|
||||||
|
|||||||
698
src/switch-video.cpp
Normal file
698
src/switch-video.cpp
Normal file
@@ -0,0 +1,698 @@
|
|||||||
|
#include <QFileDialog>
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QToolTip>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
bool VideoSwitch::pause = false;
|
||||||
|
static QMetaObject::Connection addPulse;
|
||||||
|
|
||||||
|
static void ScreenshotTick(void *param, float);
|
||||||
|
|
||||||
|
AdvSSScreenshotObj::AdvSSScreenshotObj(obs_source_t *source)
|
||||||
|
: weakSource(OBSGetWeakRef(source))
|
||||||
|
{
|
||||||
|
obs_add_tick_callback(ScreenshotTick, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AdvSSScreenshotObj::~AdvSSScreenshotObj()
|
||||||
|
{
|
||||||
|
obs_enter_graphics();
|
||||||
|
gs_stagesurface_destroy(stagesurf);
|
||||||
|
gs_texrender_destroy(texrender);
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::Screenshot()
|
||||||
|
{
|
||||||
|
OBSSource source = OBSGetStrongRef(weakSource);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
cx = obs_source_get_base_width(source);
|
||||||
|
cy = obs_source_get_base_height(source);
|
||||||
|
} else {
|
||||||
|
obs_video_info ovi;
|
||||||
|
obs_get_video_info(&ovi);
|
||||||
|
cx = ovi.base_width;
|
||||||
|
cy = ovi.base_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cx || !cy) {
|
||||||
|
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, this);
|
||||||
|
done = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
||||||
|
stagesurf = gs_stagesurface_create(cx, cy, GS_RGBA);
|
||||||
|
|
||||||
|
gs_texrender_reset(texrender);
|
||||||
|
if (gs_texrender_begin(texrender, cx, cy)) {
|
||||||
|
vec4 zero;
|
||||||
|
vec4_zero(&zero);
|
||||||
|
|
||||||
|
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||||
|
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||||
|
|
||||||
|
gs_blend_state_push();
|
||||||
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
obs_source_inc_showing(source);
|
||||||
|
obs_source_video_render(source);
|
||||||
|
obs_source_dec_showing(source);
|
||||||
|
} else {
|
||||||
|
obs_render_main_texture();
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_blend_state_pop();
|
||||||
|
gs_texrender_end(texrender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::Download()
|
||||||
|
{
|
||||||
|
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::Copy()
|
||||||
|
{
|
||||||
|
uint8_t *videoData = nullptr;
|
||||||
|
uint32_t videoLinesize = 0;
|
||||||
|
|
||||||
|
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||||
|
|
||||||
|
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||||
|
int linesize = image.bytesPerLine();
|
||||||
|
for (int y = 0; y < (int)cy; y++)
|
||||||
|
memcpy(image.scanLine(y),
|
||||||
|
videoData + (y * videoLinesize), linesize);
|
||||||
|
|
||||||
|
gs_stagesurface_unmap(stagesurf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSSScreenshotObj::MarkDone()
|
||||||
|
{
|
||||||
|
time = std::chrono::high_resolution_clock::now();
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STAGE_SCREENSHOT 0
|
||||||
|
#define STAGE_DOWNLOAD 1
|
||||||
|
#define STAGE_COPY_AND_SAVE 2
|
||||||
|
#define STAGE_FINISH 3
|
||||||
|
|
||||||
|
static void ScreenshotTick(void *param, float)
|
||||||
|
{
|
||||||
|
AdvSSScreenshotObj *data =
|
||||||
|
reinterpret_cast<AdvSSScreenshotObj *>(param);
|
||||||
|
|
||||||
|
if (data->stage == STAGE_FINISH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_enter_graphics();
|
||||||
|
|
||||||
|
switch (data->stage) {
|
||||||
|
case STAGE_SCREENSHOT:
|
||||||
|
data->Screenshot();
|
||||||
|
break;
|
||||||
|
case STAGE_DOWNLOAD:
|
||||||
|
data->Download();
|
||||||
|
break;
|
||||||
|
case STAGE_COPY_AND_SAVE:
|
||||||
|
data->Copy();
|
||||||
|
data->MarkDone();
|
||||||
|
|
||||||
|
obs_remove_tick_callback(ScreenshotTick, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_leave_graphics();
|
||||||
|
|
||||||
|
data->stage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoAdd_clicked()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switcher->videoSwitches.emplace_back();
|
||||||
|
|
||||||
|
VideoSwitchWidget *sw =
|
||||||
|
new VideoSwitchWidget(this, &switcher->videoSwitches.back());
|
||||||
|
|
||||||
|
listAddClicked(ui->videoSwitches, sw, ui->videoAdd, &addPulse);
|
||||||
|
|
||||||
|
ui->videoHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoRemove_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->videoSwitches->currentItem();
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
int idx = ui->videoSwitches->currentRow();
|
||||||
|
auto &switches = switcher->videoSwitches;
|
||||||
|
switches.erase(switches.begin() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoUp_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->videoSwitches->currentRow();
|
||||||
|
if (!listMoveUp(ui->videoSwitches)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget *s1 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index));
|
||||||
|
VideoSwitchWidget *s2 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index - 1));
|
||||||
|
VideoSwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
std::swap(switcher->videoSwitches[index],
|
||||||
|
switcher->videoSwitches[index - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_videoDown_clicked()
|
||||||
|
{
|
||||||
|
int index = ui->videoSwitches->currentRow();
|
||||||
|
|
||||||
|
if (!listMoveDown(ui->videoSwitches)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget *s1 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index));
|
||||||
|
VideoSwitchWidget *s2 =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(
|
||||||
|
ui->videoSwitches->item(index + 1));
|
||||||
|
VideoSwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
|
||||||
|
std::swap(switcher->videoSwitches[index],
|
||||||
|
switcher->videoSwitches[index + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::on_getScreenshot_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem *item = ui->videoSwitches->currentItem();
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget *sw =
|
||||||
|
(VideoSwitchWidget *)ui->videoSwitches->itemWidget(item);
|
||||||
|
auto s = sw->getSwitchData();
|
||||||
|
if (!s || !s->videoSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto source = obs_weak_source_get_source(s->videoSource);
|
||||||
|
auto screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
|
||||||
|
QString filePath = QFileDialog::getSaveFileName(this);
|
||||||
|
if (filePath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(filePath);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// During selection of the save path enough time should usually have
|
||||||
|
// passed already
|
||||||
|
// Add this just in case ...
|
||||||
|
if (!screenshotData->done) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!screenshotData->done) {
|
||||||
|
DisplayMessage("Failed to get screenshot of source!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
screenshotData->image.save(file.fileName());
|
||||||
|
sw->SetFilePath(file.fileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::checkVideoSwitch(bool &match, OBSWeakSource &scene,
|
||||||
|
OBSWeakSource &transition)
|
||||||
|
{
|
||||||
|
if (VideoSwitch::pause) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &s : videoSwitches) {
|
||||||
|
bool matched = s.checkMatch();
|
||||||
|
if (!match && matched) {
|
||||||
|
match = true;
|
||||||
|
scene = s.getScene();
|
||||||
|
transition = s.transition;
|
||||||
|
if (verbose) {
|
||||||
|
s.logMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::saveVideoSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
obs_data_array_t *videoArray = obs_data_array_create();
|
||||||
|
for (VideoSwitch &s : videoSwitches) {
|
||||||
|
obs_data_t *array_obj = obs_data_create();
|
||||||
|
|
||||||
|
s.save(array_obj);
|
||||||
|
obs_data_array_push_back(videoArray, array_obj);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_set_array(obj, "videoSwitches", videoArray);
|
||||||
|
obs_data_array_release(videoArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwitcherData::loadVideoSwitches(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
switcher->videoSwitches.clear();
|
||||||
|
|
||||||
|
obs_data_array_t *videoArray = obs_data_get_array(obj, "videoSwitches");
|
||||||
|
size_t count = obs_data_array_count(videoArray);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
obs_data_t *array_obj = obs_data_array_item(videoArray, i);
|
||||||
|
|
||||||
|
switcher->videoSwitches.emplace_back();
|
||||||
|
videoSwitches.back().load(array_obj);
|
||||||
|
|
||||||
|
obs_data_release(array_obj);
|
||||||
|
}
|
||||||
|
obs_data_array_release(videoArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::setupVideoTab()
|
||||||
|
{
|
||||||
|
for (auto &s : switcher->videoSwitches) {
|
||||||
|
QListWidgetItem *item;
|
||||||
|
item = new QListWidgetItem(ui->videoSwitches);
|
||||||
|
ui->videoSwitches->addItem(item);
|
||||||
|
VideoSwitchWidget *sw = new VideoSwitchWidget(this, &s);
|
||||||
|
item->setSizeHint(sw->minimumSizeHint());
|
||||||
|
ui->videoSwitches->setItemWidget(item, sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switcher->videoSwitches.size() == 0) {
|
||||||
|
addPulse = PulseWidget(ui->videoAdd, QColor(Qt::green));
|
||||||
|
ui->videoHelp->setVisible(true);
|
||||||
|
} else {
|
||||||
|
ui->videoHelp->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->getScreenshot->setToolTip(
|
||||||
|
obs_module_text("AdvSceneSwitcher.videoTab.getScreenshotHelp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::initialized()
|
||||||
|
{
|
||||||
|
return SceneSwitcherEntry::initialized() && videoSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::valid()
|
||||||
|
{
|
||||||
|
return !initialized() ||
|
||||||
|
(SceneSwitcherEntry::valid() && WeakSourceValid(videoSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitch::save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::save(obj);
|
||||||
|
|
||||||
|
obs_data_set_string(obj, "videoSource",
|
||||||
|
GetWeakSourceName(videoSource).c_str());
|
||||||
|
obs_data_set_int(obj, "condition", static_cast<int>(condition));
|
||||||
|
obs_data_set_double(obj, "duration", duration);
|
||||||
|
obs_data_set_string(obj, "filePath", file.c_str());
|
||||||
|
obs_data_set_bool(obj, "ignoreInactiveSource", ignoreInactiveSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitch::load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
SceneSwitcherEntry::load(obj);
|
||||||
|
|
||||||
|
const char *videoSourceName = obs_data_get_string(obj, "videoSource");
|
||||||
|
videoSource = GetWeakSourceByName(videoSourceName);
|
||||||
|
condition = static_cast<videoSwitchType>(
|
||||||
|
obs_data_get_int(obj, "condition"));
|
||||||
|
duration = obs_data_get_double(obj, "duration");
|
||||||
|
file = obs_data_get_string(obj, "filePath");
|
||||||
|
ignoreInactiveSource = obs_data_get_bool(obj, "ignoreInactiveSource");
|
||||||
|
|
||||||
|
if (condition != videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
(void)loadImageFromFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitch::getScreenshot()
|
||||||
|
{
|
||||||
|
auto source = obs_weak_source_get_source(videoSource);
|
||||||
|
screenshotData = std::make_unique<AdvSSScreenshotObj>(source);
|
||||||
|
obs_source_release(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::loadImageFromFile()
|
||||||
|
{
|
||||||
|
if (!matchImage.load(QString::fromStdString(file))) {
|
||||||
|
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||||
|
file.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
matchImage =
|
||||||
|
matchImage.convertToFormat(QImage::Format::Format_RGBX8888);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoSwitch::checkMatch()
|
||||||
|
{
|
||||||
|
if (ignoreInactiveSource) {
|
||||||
|
obs_source_t *vs = obs_weak_source_get_source(videoSource);
|
||||||
|
bool videoActive = obs_source_active(vs);
|
||||||
|
obs_source_release(vs);
|
||||||
|
|
||||||
|
if (!videoActive) {
|
||||||
|
screenshotData.reset(nullptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
if (screenshotData) {
|
||||||
|
if (screenshotData->done) {
|
||||||
|
bool conditionMatch = false;
|
||||||
|
|
||||||
|
switch (condition) {
|
||||||
|
case videoSwitchType::MATCH:
|
||||||
|
conditionMatch = screenshotData->image ==
|
||||||
|
matchImage;
|
||||||
|
break;
|
||||||
|
case videoSwitchType::DIFFER:
|
||||||
|
conditionMatch = screenshotData->image !=
|
||||||
|
matchImage;
|
||||||
|
break;
|
||||||
|
case videoSwitchType::HAS_NOT_CHANGED:
|
||||||
|
conditionMatch = screenshotData->image ==
|
||||||
|
matchImage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conditionMatch) {
|
||||||
|
currentMatchDuration +=
|
||||||
|
std::chrono::duration_cast<
|
||||||
|
std::chrono::milliseconds>(
|
||||||
|
screenshotData->time -
|
||||||
|
previousTime);
|
||||||
|
} else {
|
||||||
|
currentMatchDuration = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool durationMatch = currentMatchDuration.count() >=
|
||||||
|
duration * 1000;
|
||||||
|
|
||||||
|
if (conditionMatch && durationMatch) {
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
matchImage = std::move(screenshotData->image);
|
||||||
|
}
|
||||||
|
previousTime = std::move(screenshotData->time);
|
||||||
|
|
||||||
|
screenshotData.reset(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getScreenshot();
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(VideoSwitch &first, VideoSwitch &second)
|
||||||
|
{
|
||||||
|
std::swap(first.targetType, second.targetType);
|
||||||
|
std::swap(first.group, second.group);
|
||||||
|
std::swap(first.scene, second.scene);
|
||||||
|
std::swap(first.transition, second.transition);
|
||||||
|
std::swap(first.usePreviousScene, second.usePreviousScene);
|
||||||
|
std::swap(first.videoSource, second.videoSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void populateConditionSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.videoTab.condition.match"));
|
||||||
|
list->setItemData(
|
||||||
|
0,
|
||||||
|
obs_module_text(
|
||||||
|
"AdvSceneSwitcher.videoTab.condition.match.tooltip"),
|
||||||
|
Qt::ToolTipRole);
|
||||||
|
list->addItem(
|
||||||
|
obs_module_text("AdvSceneSwitcher.videoTab.condition.differ"));
|
||||||
|
list->addItem(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.videoTab.condition.hasNotChanged"));
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitchWidget::VideoSwitchWidget(QWidget *parent, VideoSwitch *s)
|
||||||
|
: SwitchWidget(parent, s, true, true)
|
||||||
|
{
|
||||||
|
videoSources = new QComboBox();
|
||||||
|
condition = new QComboBox();
|
||||||
|
duration = new QDoubleSpinBox();
|
||||||
|
filePath = new QLineEdit();
|
||||||
|
browseButton =
|
||||||
|
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
|
||||||
|
ignoreInactiveSource = new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.videoTab.ignoreInactiveSource"));
|
||||||
|
|
||||||
|
filePath->setFixedWidth(100);
|
||||||
|
|
||||||
|
browseButton->setStyleSheet("border:1px solid gray;");
|
||||||
|
|
||||||
|
duration->setMinimum(0.0);
|
||||||
|
duration->setMaximum(99.000000);
|
||||||
|
duration->setSuffix("s");
|
||||||
|
|
||||||
|
QWidget::connect(videoSources,
|
||||||
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
|
SLOT(SourceChanged(const QString &)));
|
||||||
|
QWidget::connect(condition, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ConditionChanged(int)));
|
||||||
|
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
||||||
|
SLOT(DurationChanged(double)));
|
||||||
|
QWidget::connect(filePath, SIGNAL(editingFinished()), this,
|
||||||
|
SLOT(FilePathChanged()));
|
||||||
|
QWidget::connect(browseButton, SIGNAL(clicked()), this,
|
||||||
|
SLOT(BrowseButtonClicked()));
|
||||||
|
QWidget::connect(ignoreInactiveSource, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(IgnoreInactiveChanged(int)));
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Figure out why scene do not work for "match exactly".
|
||||||
|
// Until then do not allow selecting scenes
|
||||||
|
AdvSceneSwitcher::populateVideoSelection(videoSources, false);
|
||||||
|
populateConditionSelection(condition);
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
videoSources->setCurrentText(
|
||||||
|
GetWeakSourceName(s->videoSource).c_str());
|
||||||
|
condition->setCurrentIndex(static_cast<int>(s->condition));
|
||||||
|
duration->setValue(s->duration);
|
||||||
|
filePath->setText(QString::fromStdString(s->file));
|
||||||
|
ignoreInactiveSource->setChecked(s->ignoreInactiveSource);
|
||||||
|
|
||||||
|
if (s->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
filePath->hide();
|
||||||
|
browseButton->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *switchLayout = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{videoSources}}", videoSources},
|
||||||
|
{"{{condition}}", condition},
|
||||||
|
{"{{duration}}", duration},
|
||||||
|
{"{{filePath}}", filePath},
|
||||||
|
{"{{browseButton}}", browseButton},
|
||||||
|
{"{{ignoreInactiveSource}}", ignoreInactiveSource},
|
||||||
|
{"{{scenes}}", scenes},
|
||||||
|
{"{{transitions}}", transitions}};
|
||||||
|
placeWidgets(obs_module_text("AdvSceneSwitcher.videoTab.entry"),
|
||||||
|
switchLayout, widgetPlaceholders);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
mainLayout->addLayout(switchLayout);
|
||||||
|
setLayout(mainLayout);
|
||||||
|
|
||||||
|
switchData = s;
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSwitch *VideoSwitchWidget::getSwitchData()
|
||||||
|
{
|
||||||
|
return switchData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::setSwitchData(VideoSwitch *s)
|
||||||
|
{
|
||||||
|
switchData = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::swapSwitchData(VideoSwitchWidget *s1,
|
||||||
|
VideoSwitchWidget *s2)
|
||||||
|
{
|
||||||
|
SwitchWidget::swapSwitchData(s1, s2);
|
||||||
|
|
||||||
|
VideoSwitch *t = s1->getSwitchData();
|
||||||
|
s1->setSwitchData(s2->getSwitchData());
|
||||||
|
s2->setSwitchData(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::UpdatePreviewTooltip()
|
||||||
|
{
|
||||||
|
if (!switchData ||
|
||||||
|
switchData->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage preview =
|
||||||
|
switchData->matchImage.scaled({300, 300}, Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
QBuffer buffer(&data);
|
||||||
|
if (!preview.save(&buffer, "PNG")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString html =
|
||||||
|
QString("<html><img src='data:image/png;base64, %0'/></html>")
|
||||||
|
.arg(QString(data.toBase64()));
|
||||||
|
this->setToolTip(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::SetFilePath(const QString &text)
|
||||||
|
{
|
||||||
|
filePath->setText(text);
|
||||||
|
FilePathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::SourceChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->videoSource = GetWeakSourceByQString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::ConditionChanged(int cond)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->condition = static_cast<videoSwitchType>(cond);
|
||||||
|
|
||||||
|
if (switchData->condition == videoSwitchType::HAS_NOT_CHANGED) {
|
||||||
|
filePath->hide();
|
||||||
|
browseButton->hide();
|
||||||
|
} else {
|
||||||
|
filePath->show();
|
||||||
|
browseButton->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload image data to avoid incorrect matches.
|
||||||
|
//
|
||||||
|
// Condition type HAS_NOT_CHANGED will use matchImage to store previous
|
||||||
|
// frame of video source, which will differ from the image stored at
|
||||||
|
// specified file location.
|
||||||
|
if (switchData->loadImageFromFile()) {
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::DurationChanged(double dur)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->duration = dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::FilePathChanged()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->file = filePath->text().toUtf8().constData();
|
||||||
|
if (switchData->loadImageFromFile()) {
|
||||||
|
UpdatePreviewTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::BrowseButtonClicked()
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path = QFileDialog::getOpenFileName(
|
||||||
|
this,
|
||||||
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.selectRead")),
|
||||||
|
QDir::currentPath(),
|
||||||
|
tr(obs_module_text("AdvSceneSwitcher.fileTab.anyFileType")));
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath->setText(path);
|
||||||
|
FilePathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoSwitchWidget::IgnoreInactiveChanged(int state)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->ignoreInactiveSource = state;
|
||||||
|
}
|
||||||
@@ -43,6 +43,16 @@ void SwitcherData::Prune()
|
|||||||
sceneSequenceSwitches.erase(
|
sceneSequenceSwitches.erase(
|
||||||
sceneSequenceSwitches.begin() + i--);
|
sceneSequenceSwitches.begin() + i--);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto cur = &s;
|
||||||
|
while (cur != nullptr) {
|
||||||
|
if (cur->extendedSequence &&
|
||||||
|
!cur->extendedSequence->valid()) {
|
||||||
|
cur->extendedSequence.reset(nullptr);
|
||||||
|
s.activeSequence = nullptr;
|
||||||
|
}
|
||||||
|
cur = cur->extendedSequence.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sceneTransitions.size(); i++) {
|
for (size_t i = 0; i < sceneTransitions.size(); i++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user