From 07c868edcd7336b5c728e7cba9f930108caa343c Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 11:21:10 +0200 Subject: [PATCH 01/11] Preliminary work on the Preview request types --- Utils.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Utils.h | 13 +++++++++ 2 files changed, 93 insertions(+) diff --git a/Utils.cpp b/Utils.cpp index 0ac75611..3693eaf1 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -20,6 +20,9 @@ with this program. If not, see #include #include #include +#include +#include +#include #include "obs-websocket.h" obs_data_array_t* string_list_to_array(char** strings, char* key) @@ -238,6 +241,83 @@ void Utils::SetTransitionDuration(int ms) } } +QPushButton* Utils::GetPreviewModeButtonControl() +{ + QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); + return main->findChild("modeSwitch"); +} + +QLayout* Utils::GetPreviewLayout() +{ + QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); + return main->findChild("previewLayout"); +} + +bool Utils::IsPreviewModeActive() +{ + QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); + + // Clue 1 : "Studio Mode" button is toggled on + bool buttonToggledOn = GetPreviewModeButtonControl()->isChecked(); + + // Clue 2 : Preview layout has more than one item + int previewChildCount = GetPreviewLayout()->children().count(); + + return buttonToggledOn || (previewChildCount >= 2); +} + +void Utils::EnablePreviewMode() +{ + GetPreviewModeButtonControl()->setChecked(true); +} + +void Utils::DisablePreviewMode() +{ + GetPreviewModeButtonControl()->setChecked(false); +} + +void Utils::TogglePreviewMode() +{ + GetPreviewModeButtonControl()->toggle(); +} + +const char* Utils::GetPreviewSceneName() +{ + if (IsPreviewModeActive()) + { + QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); + QListWidget* sceneList = main->findChild("scenes"); + + QString name = sceneList->selectedItems().first()->text(); + return name.toUtf8().constData(); + } + + return nullptr; +} + +void Utils::TransitionToProgram() +{ + if (!IsPreviewModeActive()) + return; + + // WARNING : if the layout created in OBS' CreateProgramOptions() changes + // then this won't work as expected + + // The program options widget is the second item in the left-to-right layout + QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget(); + + // The "Transition" button lies in the mainButtonLayout + // which is the first itemin the program options' layout + QLayout* mainButtonLayout = programOptions->layout()->itemAt(0)->layout(); + QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget(); + + // Try to cast that widget into a button + QPushButton* transitionBtn = qobject_cast(transitionBtnWidget); + + // Perform a click on that button + transitionBtn->click(); +} + const char* Utils::OBSVersionString() { uint32_t version = obs_get_version(); diff --git a/Utils.h b/Utils.h index 91b22be7..d75aca48 100644 --- a/Utils.h +++ b/Utils.h @@ -20,6 +20,8 @@ with this program. If not, see #define UTILS_H #include +#include +#include #include #include @@ -42,6 +44,17 @@ class Utils static int GetTransitionDuration(); static void SetTransitionDuration(int ms); + static QPushButton* GetPreviewModeButtonControl(); + static QLayout* GetPreviewLayout(); + + static bool IsPreviewModeActive(); + static void EnablePreviewMode(); + static void DisablePreviewMode(); + static void TogglePreviewMode(); + + static const char* GetPreviewSceneName(); + static void TransitionToProgram(); + static const char* OBSVersionString(); }; From 8c4bd91c78deb6761ca9d4a97ad53110b175d4c5 Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 11:34:37 +0200 Subject: [PATCH 02/11] New request types to get and set the status of Studio Mode --- Utils.cpp | 1 + WSRequestHandler.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ WSRequestHandler.h | 5 +++++ 3 files changed, 46 insertions(+) diff --git a/Utils.cpp b/Utils.cpp index 3693eaf1..c3c10c99 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -262,6 +262,7 @@ bool Utils::IsPreviewModeActive() // Clue 2 : Preview layout has more than one item int previewChildCount = GetPreviewLayout()->children().count(); + blog(LOG_INFO, "preview layout children count : %d", previewChildCount); return buttonToggledOn || (previewChildCount >= 2); } diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index 026a96f5..2d3e75f8 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -71,6 +71,11 @@ WSRequestHandler::WSRequestHandler(QWebSocket *client) : messageMap["GetCurrentProfile"] = WSRequestHandler::HandleGetCurrentProfile; messageMap["ListProfiles"] = WSRequestHandler::HandleListProfiles; + messageMap["GetStudioModeStatus"] = WSRequestHandler::HandleGetStudioModeStatus; + messageMap["EnableStudioMode"] = WSRequestHandler::HandleEnableStudioMode; + messageMap["DisableStudioMode"] = WSRequestHandler::HandleDisableStudioMode; + messageMap["ToggleStudioMode"] = WSRequestHandler::HandleToggleStudioMode; + authNotRequired.insert("GetVersion"); authNotRequired.insert("GetAuthRequired"); authNotRequired.insert("Authenticate"); @@ -746,6 +751,41 @@ void WSRequestHandler::HandleListProfiles(WSRequestHandler *owner) obs_data_array_release(profiles); } +void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler *owner) +{ + bool previewActive = Utils::IsPreviewModeActive(); + + obs_data_t* response = obs_data_create(); + obs_data_set_bool(response, "studio-mode", previewActive); + + if (previewActive) { + //const char* currentPreviewScene = Utils::GetPreviewSceneName(); + //obs_data_set_string(response, "preview-scene", currentPreviewScene); + } + + owner->SendOKResponse(response); + + obs_data_release(response); +} + +void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler *owner) +{ + Utils::EnablePreviewMode(); + owner->SendOKResponse(); +} + +void WSRequestHandler::HandleDisableStudioMode(WSRequestHandler *owner) +{ + Utils::DisablePreviewMode(); + owner->SendOKResponse(); +} + +void WSRequestHandler::HandleToggleStudioMode(WSRequestHandler *owner) +{ + Utils::TogglePreviewMode(); + owner->SendOKResponse(); +} + void WSRequestHandler::ErrNotImplemented(WSRequestHandler *owner) { owner->SendErrorResponse("not implemented"); diff --git a/WSRequestHandler.h b/WSRequestHandler.h index afe696db..06f434b5 100644 --- a/WSRequestHandler.h +++ b/WSRequestHandler.h @@ -87,6 +87,11 @@ class WSRequestHandler : public QObject static void HandleSetTransitionDuration(WSRequestHandler *owner); static void HandleGetTransitionDuration(WSRequestHandler *owner); + + static void HandleGetStudioModeStatus(WSRequestHandler *owner); + static void HandleEnableStudioMode(WSRequestHandler *owner); + static void HandleDisableStudioMode(WSRequestHandler *owner); + static void HandleToggleStudioMode(WSRequestHandler *owner); }; #endif // WSPROTOCOL_H From ff8eda36827cf3e6f01bb15872e7a5988a53fce1 Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 11:50:49 +0200 Subject: [PATCH 03/11] Fixed detection and control behaviour for some functions used by Preview API --- Utils.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Utils.cpp b/Utils.cpp index c3c10c99..aeb8e4a2 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -261,7 +261,7 @@ bool Utils::IsPreviewModeActive() bool buttonToggledOn = GetPreviewModeButtonControl()->isChecked(); // Clue 2 : Preview layout has more than one item - int previewChildCount = GetPreviewLayout()->children().count(); + int previewChildCount = GetPreviewLayout()->count(); blog(LOG_INFO, "preview layout children count : %d", previewChildCount); return buttonToggledOn || (previewChildCount >= 2); @@ -269,17 +269,19 @@ bool Utils::IsPreviewModeActive() void Utils::EnablePreviewMode() { - GetPreviewModeButtonControl()->setChecked(true); + if (!IsPreviewModeActive()) + GetPreviewModeButtonControl()->click(); } void Utils::DisablePreviewMode() { - GetPreviewModeButtonControl()->setChecked(false); + if (IsPreviewModeActive()) + GetPreviewModeButtonControl()->click(); } void Utils::TogglePreviewMode() { - GetPreviewModeButtonControl()->toggle(); + GetPreviewModeButtonControl()->click(); } const char* Utils::GetPreviewSceneName() From b7df1e859699b191b625c04b45e68701639c0edd Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 13:43:58 +0200 Subject: [PATCH 04/11] Add SetPreviewScene request type and PreviewSceneChanged event --- Utils.cpp | 46 ++++++++++++++++++++++++++++++++++++-------- Utils.h | 4 ++++ WSEvents.cpp | 21 ++++++++++++++++++++ WSEvents.h | 2 ++ WSRequestHandler.cpp | 19 ++++++++++++++++-- WSRequestHandler.h | 1 + 6 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Utils.cpp b/Utils.cpp index aeb8e4a2..6294d2ce 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -18,13 +18,12 @@ with this program. If not, see #include "Utils.h" #include +#include #include -#include -#include -#include -#include #include "obs-websocket.h" +Q_DECLARE_METATYPE(OBSScene); + obs_data_array_t* string_list_to_array(char** strings, char* key) { if (!strings) @@ -247,6 +246,21 @@ QPushButton* Utils::GetPreviewModeButtonControl() return main->findChild("modeSwitch"); } +QListWidget* Utils::GetSceneListControl() +{ + QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); + return main->findChild("scenes"); +} + +obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) +{ + if (!item) + return nullptr; + + QVariant item_data = item->data(static_cast(Qt::UserRole)); + return item_data.value(); +} + QLayout* Utils::GetPreviewLayout() { QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); @@ -288,16 +302,32 @@ const char* Utils::GetPreviewSceneName() { if (IsPreviewModeActive()) { - QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); - QListWidget* sceneList = main->findChild("scenes"); + QListWidget* sceneList = GetSceneListControl(); - QString name = sceneList->selectedItems().first()->text(); - return name.toUtf8().constData(); + QList selected = sceneList->selectedItems(); + blog(LOG_INFO, "GetPreviewSceneName: %d selected item(s)", selected.count()); + + // Qt::UserRole == QtUserRole::OBSRef + obs_source_t* source = obs_scene_get_source(Utils::SceneListItemToScene(selected.first())); + + return obs_source_get_name(source); } return nullptr; } +void Utils::SetPreviewScene(const char* name) +{ + if (IsPreviewModeActive()) + { + QListWidget* sceneList = GetSceneListControl(); + QList matchingItems = sceneList->findItems(name, Qt::MatchExactly); + + if (matchingItems.count() > 0) + sceneList->setCurrentItem(matchingItems.first()); + } +} + void Utils::TransitionToProgram() { if (!IsPreviewModeActive()) diff --git a/Utils.h b/Utils.h index d75aca48..ecb96359 100644 --- a/Utils.h +++ b/Utils.h @@ -22,6 +22,7 @@ with this program. If not, see #include #include #include +#include #include #include @@ -46,6 +47,8 @@ class Utils static QPushButton* GetPreviewModeButtonControl(); static QLayout* GetPreviewLayout(); + static QListWidget* GetSceneListControl(); + static obs_scene_t* SceneListItemToScene(QListWidgetItem* item); static bool IsPreviewModeActive(); static void EnablePreviewMode(); @@ -53,6 +56,7 @@ class Utils static void TogglePreviewMode(); static const char* GetPreviewSceneName(); + static void SetPreviewScene(const char* name); static void TransitionToProgram(); static const char* OBSVersionString(); diff --git a/WSEvents.cpp b/WSEvents.cpp index ec20852a..4ae179a1 100644 --- a/WSEvents.cpp +++ b/WSEvents.cpp @@ -68,6 +68,9 @@ WSEvents::WSEvents(WSServer *srv) connect(statusTimer, SIGNAL(timeout()), this, SLOT(StreamStatus())); statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS + QListWidget* sceneList = Utils::GetSceneListControl(); + connect(sceneList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(SelectedSceneChanged(QListWidgetItem*, QListWidgetItem*))); + transition_handler = nullptr; scene_handler = nullptr; @@ -568,4 +571,22 @@ void WSEvents::OnSceneItemVisibilityChanged(void *param, calldata_t *data) instance->broadcastUpdate("SceneItemVisibilityChanged", fields); obs_data_release(fields); +} + +void WSEvents::SelectedSceneChanged(QListWidgetItem *current, QListWidgetItem *prev) +{ + if (Utils::IsPreviewModeActive()) + { + obs_scene_t* scene = Utils::SceneListItemToScene(current); + if (!scene) return; + + obs_source_t* scene_source = obs_scene_get_source(scene); + + obs_data_t* data = obs_data_create(); + obs_data_set_string(data, "scene-name", obs_source_get_name(scene_source)); + + broadcastUpdate("PreviewSceneChanged", data); + + obs_data_release(data); + } } \ No newline at end of file diff --git a/WSEvents.h b/WSEvents.h index 2c678364..31c3bf23 100644 --- a/WSEvents.h +++ b/WSEvents.h @@ -21,6 +21,7 @@ with this program. If not, see #define WSEVENTS_H #include +#include #include "WSServer.h" class WSEvents : public QObject @@ -43,6 +44,7 @@ class WSEvents : public QObject private Q_SLOTS: void StreamStatus(); void TransitionDurationChanged(int ms); + void SelectedSceneChanged(QListWidgetItem *current, QListWidgetItem *prev); void deferredInitOperations(); private: diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index 2d3e75f8..994ab318 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -72,6 +72,7 @@ WSRequestHandler::WSRequestHandler(QWebSocket *client) : messageMap["ListProfiles"] = WSRequestHandler::HandleListProfiles; messageMap["GetStudioModeStatus"] = WSRequestHandler::HandleGetStudioModeStatus; + messageMap["SetPreviewScene"] = WSRequestHandler::HandleSetPreviewScene; messageMap["EnableStudioMode"] = WSRequestHandler::HandleEnableStudioMode; messageMap["DisableStudioMode"] = WSRequestHandler::HandleDisableStudioMode; messageMap["ToggleStudioMode"] = WSRequestHandler::HandleToggleStudioMode; @@ -759,8 +760,8 @@ void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler *owner) obs_data_set_bool(response, "studio-mode", previewActive); if (previewActive) { - //const char* currentPreviewScene = Utils::GetPreviewSceneName(); - //obs_data_set_string(response, "preview-scene", currentPreviewScene); + const char* currentPreviewScene = Utils::GetPreviewSceneName(); + obs_data_set_string(response, "preview-scene", currentPreviewScene); } owner->SendOKResponse(response); @@ -768,6 +769,20 @@ void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler *owner) obs_data_release(response); } +void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) +{ + const char* scene_name = obs_data_get_string(owner->_requestData, "scene-name"); + if (!scene_name) + { + owner->SendErrorResponse("invalid request parameters"); + return; + } + + Utils::SetPreviewScene(scene_name); + + owner->SendOKResponse(); +} + void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler *owner) { Utils::EnablePreviewMode(); diff --git a/WSRequestHandler.h b/WSRequestHandler.h index 06f434b5..6b595266 100644 --- a/WSRequestHandler.h +++ b/WSRequestHandler.h @@ -89,6 +89,7 @@ class WSRequestHandler : public QObject static void HandleGetTransitionDuration(WSRequestHandler *owner); static void HandleGetStudioModeStatus(WSRequestHandler *owner); + static void HandleSetPreviewScene(WSRequestHandler *owner); static void HandleEnableStudioMode(WSRequestHandler *owner); static void HandleDisableStudioMode(WSRequestHandler *owner); static void HandleToggleStudioMode(WSRequestHandler *owner); From f0bb941c47419fc75d608187e8b7f73de291497c Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 14:36:53 +0200 Subject: [PATCH 05/11] New request type : TransitionToProgram --- Utils.cpp | 5 ++++- WSRequestHandler.cpp | 7 +++++++ WSRequestHandler.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Utils.cpp b/Utils.cpp index 6294d2ce..75993608 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -20,6 +20,7 @@ with this program. If not, see #include #include #include +#include #include "obs-websocket.h" Q_DECLARE_METATYPE(OBSScene); @@ -336,12 +337,14 @@ void Utils::TransitionToProgram() // WARNING : if the layout created in OBS' CreateProgramOptions() changes // then this won't work as expected + QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); + // The program options widget is the second item in the left-to-right layout QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget(); // The "Transition" button lies in the mainButtonLayout // which is the first itemin the program options' layout - QLayout* mainButtonLayout = programOptions->layout()->itemAt(0)->layout(); + QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout(); QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget(); // Try to cast that widget into a button diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index 994ab318..c12ec262 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -73,6 +73,7 @@ WSRequestHandler::WSRequestHandler(QWebSocket *client) : messageMap["GetStudioModeStatus"] = WSRequestHandler::HandleGetStudioModeStatus; messageMap["SetPreviewScene"] = WSRequestHandler::HandleSetPreviewScene; + messageMap["TransitionToProgram"] = WSRequestHandler::HandleTransitionToProgram; messageMap["EnableStudioMode"] = WSRequestHandler::HandleEnableStudioMode; messageMap["DisableStudioMode"] = WSRequestHandler::HandleDisableStudioMode; messageMap["ToggleStudioMode"] = WSRequestHandler::HandleToggleStudioMode; @@ -783,6 +784,12 @@ void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) owner->SendOKResponse(); } +void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler *owner) +{ + Utils::TransitionToProgram(); + owner->SendOKResponse(); +} + void WSRequestHandler::HandleEnableStudioMode(WSRequestHandler *owner) { Utils::EnablePreviewMode(); diff --git a/WSRequestHandler.h b/WSRequestHandler.h index 6b595266..f54f43cd 100644 --- a/WSRequestHandler.h +++ b/WSRequestHandler.h @@ -90,6 +90,7 @@ class WSRequestHandler : public QObject static void HandleGetStudioModeStatus(WSRequestHandler *owner); static void HandleSetPreviewScene(WSRequestHandler *owner); + static void HandleTransitionToProgram(WSRequestHandler *owner); static void HandleEnableStudioMode(WSRequestHandler *owner); static void HandleDisableStudioMode(WSRequestHandler *owner); static void HandleToggleStudioMode(WSRequestHandler *owner); From 85fa6b60e2846eb7781c16234c9abcdaff2e43ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20L?= Date: Wed, 19 Apr 2017 15:00:47 +0200 Subject: [PATCH 06/11] Update PROTOCOL.md --- PROTOCOL.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 007c3547..470b2aeb 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -21,6 +21,7 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami - ["TransitionDurationChanged"](#transitiondurationchanged) - ["TransitionListChanged"](#transitionlistchanged) - ["TransitionBegin"](#transitionbegin) + - ["PreviewSceneChanged"](#previewscenechanged) - ["ProfileChanged"](#profilechanged) - ["ProfileListChanged"](#profilelistchanged) - ["StreamStarting"](#streamstarting) @@ -43,6 +44,12 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami - ["SetCurrentScene"](#setcurrentscene) - ["GetSceneList"](#getscenelist) - ["SetSourceRender"](#setsourcerender) + - ["GetStudioModeStatus"](#getstudiomodestatus) + - ["SetPreviewScene"](#setpreviewscene) + - ["TransitionToProgram"](#transitiontoprogram) + - ["EnableStudioMode"](#enablestudiomode) + - ["DisableStudioMode"](#disablestudiomode) + - ["ToggleStudioMode"](#togglestudiomode) - ["StartStopStreaming"](#startstopstreaming) - ["StartStopRecording"](#startstoprecording) - ["StartStreaming"](#startstreaming) @@ -154,6 +161,11 @@ A transition other than "Cut" has begun. --- +#### "PreviewSceneChanged" +The selected Preview scene changed (only in Studio Mode). + +--- + #### "ProfileChanged" Triggered when switching to another profile or when renaming the current profile. @@ -335,8 +347,60 @@ __Response__ : OK if source exists in the current scene, error otherwise. --- +#### "GetStudioModeStatus" +List OBS' scenes. + +__Request fields__ : none +__Response__ : always OK, with these additional fields : +- **"studio-mode"** (bool) : true if OBS is in Studio Mode, false otherwise. +- **"preview-scene"** (string, optional) : name of the current Previewed scene (present only if Studio Mode is enabled). + +--- + +#### "SetPreviewScene" +Studio Mode only. Sets the specified scene as the Previewed scene in Studio Mode. + +__Request fields__ : +- **"scene-name"** (string) : name of the scene to selected as the preview of Studio Mode + +__Response__ : OK if specified scene exists, error otherwise. + +--- + +#### "TransitionToProgram" +Studio Mode only. Transitions the currently previewed scene to Program (main output). + +__Request fields__ : none +__Response__ : always OK. No additional fields. + +--- + +#### "EnableStudioMode" +Enables Studio Mode. + +__Request fields__ : none +__Response__ : always OK. No additional fields. + +--- + +#### "DisableStudioMode" +Disables Studio Mode. + +__Request fields__ : none +__Response__ : always OK. No additional fields. + +--- + +#### "ToggleStudioMode" +Toggles Studio Mode on or off. + +__Request fields__ : none +__Response__ : always OK. No additional fields. + +--- + #### "StartStopStreaming" -Toggle streaming on or off. +Toggles streaming on or off. __Request fields__ : none __Response__ : always OK. No additional fields. @@ -344,7 +408,7 @@ __Response__ : always OK. No additional fields. --- #### "StartStopRecording" -Toggle recording on or off. +Toggles recording on or off. __Request fields__ : none __Response__ : always OK. No additional fields. From 5748e163f8cdce4b970e497dfa6c85c632dec84f Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 15:21:21 +0200 Subject: [PATCH 07/11] Added GetPreviewScene and modified GetStudioModeStatus --- PROTOCOL.md | 11 +++++++++-- Utils.cpp | 7 ++++--- Utils.h | 2 +- WSRequestHandler.cpp | 32 +++++++++++++++++++++++++++----- WSRequestHandler.h | 1 + 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 470b2aeb..da88181a 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -348,12 +348,19 @@ __Response__ : OK if source exists in the current scene, error otherwise. --- #### "GetStudioModeStatus" -List OBS' scenes. +Tells if Studio Mode is currently enabled or disabled. __Request fields__ : none __Response__ : always OK, with these additional fields : - **"studio-mode"** (bool) : true if OBS is in Studio Mode, false otherwise. -- **"preview-scene"** (string, optional) : name of the current Previewed scene (present only if Studio Mode is enabled). + +--- + +#### "GetPreviewScene" +Studio Mode only. Gets the name of the currently Previewed scene, along with a list of its sources. + +__Request fields__ : none +__Response__ : OK if Studio Mode is enabled, with the same fields as [`GetCurrentScene`](#getcurrentscene), error otherwise. --- diff --git a/Utils.cpp b/Utils.cpp index 75993608..9ef3fc09 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -299,7 +299,7 @@ void Utils::TogglePreviewMode() GetPreviewModeButtonControl()->click(); } -const char* Utils::GetPreviewSceneName() +obs_scene_t* Utils::GetPreviewScene() { if (IsPreviewModeActive()) { @@ -309,9 +309,10 @@ const char* Utils::GetPreviewSceneName() blog(LOG_INFO, "GetPreviewSceneName: %d selected item(s)", selected.count()); // Qt::UserRole == QtUserRole::OBSRef - obs_source_t* source = obs_scene_get_source(Utils::SceneListItemToScene(selected.first())); + obs_scene_t* scene = Utils::SceneListItemToScene(selected.first()); - return obs_source_get_name(source); + obs_scene_addref(scene); + return scene; } return nullptr; diff --git a/Utils.h b/Utils.h index ecb96359..4f5b10a0 100644 --- a/Utils.h +++ b/Utils.h @@ -55,7 +55,7 @@ class Utils static void DisablePreviewMode(); static void TogglePreviewMode(); - static const char* GetPreviewSceneName(); + static obs_scene_t* GetPreviewScene(); static void SetPreviewScene(const char* name); static void TransitionToProgram(); diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index c12ec262..45365977 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -72,6 +72,7 @@ WSRequestHandler::WSRequestHandler(QWebSocket *client) : messageMap["ListProfiles"] = WSRequestHandler::HandleListProfiles; messageMap["GetStudioModeStatus"] = WSRequestHandler::HandleGetStudioModeStatus; + messageMap["GetPreviewScene"] = WSRequestHandler::HandleGetPreviewScene; messageMap["SetPreviewScene"] = WSRequestHandler::HandleSetPreviewScene; messageMap["TransitionToProgram"] = WSRequestHandler::HandleTransitionToProgram; messageMap["EnableStudioMode"] = WSRequestHandler::HandleEnableStudioMode; @@ -760,16 +761,37 @@ void WSRequestHandler::HandleGetStudioModeStatus(WSRequestHandler *owner) obs_data_t* response = obs_data_create(); obs_data_set_bool(response, "studio-mode", previewActive); - if (previewActive) { - const char* currentPreviewScene = Utils::GetPreviewSceneName(); - obs_data_set_string(response, "preview-scene", currentPreviewScene); - } - owner->SendOKResponse(response); obs_data_release(response); } +void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler *owner) +{ + if (!Utils::IsPreviewModeActive()) + { + owner->SendErrorResponse("studio mode not enabled"); + return; + } + + obs_scene_t* preview_scene = Utils::GetPreviewScene(); + obs_source_t* source = obs_scene_get_source(preview_scene); + const char *name = obs_source_get_name(source); + + obs_data_array_t *scene_items = Utils::GetSceneItems(source); + + obs_data_t *data = obs_data_create(); + obs_data_set_string(data, "name", name); + obs_data_set_array(data, "sources", scene_items); + + owner->SendOKResponse(data); + + obs_data_release(data); + obs_data_array_release(scene_items); + + obs_scene_release(preview_scene); +} + void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) { const char* scene_name = obs_data_get_string(owner->_requestData, "scene-name"); diff --git a/WSRequestHandler.h b/WSRequestHandler.h index f54f43cd..c1c67680 100644 --- a/WSRequestHandler.h +++ b/WSRequestHandler.h @@ -89,6 +89,7 @@ class WSRequestHandler : public QObject static void HandleGetTransitionDuration(WSRequestHandler *owner); static void HandleGetStudioModeStatus(WSRequestHandler *owner); + static void HandleGetPreviewScene(WSRequestHandler *owner); static void HandleSetPreviewScene(WSRequestHandler *owner); static void HandleTransitionToProgram(WSRequestHandler *owner); static void HandleEnableStudioMode(WSRequestHandler *owner); From a6677edbf5312564d1a07230e6bbfb42b3bcb885 Mon Sep 17 00:00:00 2001 From: Palakis Date: Wed, 19 Apr 2017 18:53:09 +0200 Subject: [PATCH 08/11] Add ability to specify a transition when calling TransitionToProgram --- PROTOCOL.md | 10 ++++++++-- Utils.cpp | 17 +++++++++++++++++ Utils.h | 2 ++ WSRequestHandler.cpp | 36 +++++++++++++++++++++++++++--------- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index da88181a..ec5dae51 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -377,8 +377,14 @@ __Response__ : OK if specified scene exists, error otherwise. #### "TransitionToProgram" Studio Mode only. Transitions the currently previewed scene to Program (main output). -__Request fields__ : none -__Response__ : always OK. No additional fields. +__Request fields__ : +- **"with-transition" (object, optional) : if specified, use this transition when switching from preview to program. This will change the current transition in the frontend to this one. + +__Response__ : always OK. No additional fields + +An object passed as `"with-transition"` in a request must have the following fields : +- **"name"** (string) : transition name +- **"duration"** (integer, optional) : transition duration in milliseconds --- diff --git a/Utils.cpp b/Utils.cpp index 9ef3fc09..d92a20c5 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -241,6 +241,23 @@ void Utils::SetTransitionDuration(int ms) } } +bool Utils::SetTransitionByName(const char* transition_name) +{ + obs_source_t *transition = GetTransitionFromName(transition_name); + + if (transition) + { + obs_frontend_set_current_transition(transition); + obs_source_release(transition); + + return true; + } + else + { + return false; + } +} + QPushButton* Utils::GetPreviewModeButtonControl() { QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window(); diff --git a/Utils.h b/Utils.h index 4f5b10a0..d7e0b0a2 100644 --- a/Utils.h +++ b/Utils.h @@ -45,6 +45,8 @@ class Utils static int GetTransitionDuration(); static void SetTransitionDuration(int ms); + static bool SetTransitionByName(const char* transition_name); + static QPushButton* GetPreviewModeButtonControl(); static QLayout* GetPreviewLayout(); static QListWidget* GetSceneListControl(); diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index 45365977..e3ef5466 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -437,19 +437,13 @@ void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner) { const char *name = obs_data_get_string(owner->_requestData, "transition-name"); - obs_source_t *transition = Utils::GetTransitionFromName(name); - if (transition) - { - obs_frontend_set_current_transition(transition); + bool success = Utils::SetTransitionByName(name); + + if (success) owner->SendOKResponse(); - - obs_source_release(transition); - } else - { owner->SendErrorResponse("requested transition does not exist"); - } } void WSRequestHandler::HandleSetTransitionDuration(WSRequestHandler *owner) @@ -808,6 +802,30 @@ void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler *owner) { + obs_data_t* transitionInfo = obs_data_get_obj(owner->_requestData, "with-transition"); + + if (transitionInfo) + { + const char* transitionName = obs_data_get_string(transitionInfo, "name"); + int transitionDuration = obs_data_get_int(transitionInfo, "duration"); + + if (!transitionName) + { + owner->SendErrorResponse("specified transition doesn't exist"); + return; + } + + bool success = Utils::SetTransitionByName(transitionName); + if (!success) + { + owner->SendErrorResponse("unknown error while trying to change current transition"); + return; + } + + if (transitionDuration > 0) + Utils::SetTransitionDuration(transitionDuration); + } + Utils::TransitionToProgram(); owner->SendOKResponse(); } From e241518f8d510d1bafb17d9afef67a90cf05ec2d Mon Sep 17 00:00:00 2001 From: Palakis Date: Thu, 20 Apr 2017 09:53:31 +0200 Subject: [PATCH 09/11] Add StudioModeSwitched event --- PROTOCOL.md | 7 +++++++ Utils.cpp | 1 - WSEvents.cpp | 14 ++++++++++++++ WSEvents.h | 3 ++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index ec5dae51..fa0ded54 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -22,6 +22,7 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami - ["TransitionListChanged"](#transitionlistchanged) - ["TransitionBegin"](#transitionbegin) - ["PreviewSceneChanged"](#previewscenechanged) + - ["StudioModeSwitched"](#studiomodeswitched) - ["ProfileChanged"](#profilechanged) - ["ProfileListChanged"](#profilelistchanged) - ["StreamStarting"](#streamstarting) @@ -166,6 +167,12 @@ The selected Preview scene changed (only in Studio Mode). --- +#### "StudioModeSwitched" +Studio Mode has been switched on or off. +- **"new-state"** (bool) : new state of Studio Mode: true if enabled, false if disabled. + +--- + #### "ProfileChanged" Triggered when switching to another profile or when renaming the current profile. diff --git a/Utils.cpp b/Utils.cpp index d92a20c5..70d0fafe 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -323,7 +323,6 @@ obs_scene_t* Utils::GetPreviewScene() QListWidget* sceneList = GetSceneListControl(); QList selected = sceneList->selectedItems(); - blog(LOG_INFO, "GetPreviewSceneName: %d selected item(s)", selected.count()); // Qt::UserRole == QtUserRole::OBSRef obs_scene_t* scene = Utils::SceneListItemToScene(selected.first()); diff --git a/WSEvents.cpp b/WSEvents.cpp index 4ae179a1..343e0f02 100644 --- a/WSEvents.cpp +++ b/WSEvents.cpp @@ -19,6 +19,7 @@ with this program. If not, see #include #include +#include #include "Utils.h" #include "WSEvents.h" #include "obs-websocket.h" @@ -71,6 +72,9 @@ WSEvents::WSEvents(WSServer *srv) QListWidget* sceneList = Utils::GetSceneListControl(); connect(sceneList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(SelectedSceneChanged(QListWidgetItem*, QListWidgetItem*))); + QPushButton* modeSwitch = Utils::GetPreviewModeButtonControl(); + connect(modeSwitch, SIGNAL(clicked(bool)), this, SLOT(ModeSwitchClicked(bool))); + transition_handler = nullptr; scene_handler = nullptr; @@ -589,4 +593,14 @@ void WSEvents::SelectedSceneChanged(QListWidgetItem *current, QListWidgetItem *p obs_data_release(data); } +} + +void WSEvents::ModeSwitchClicked(bool checked) +{ + obs_data_t* data = obs_data_create(); + obs_data_set_bool(data, "new-state", checked); + + broadcastUpdate("StudioModeSwitched", data); + + obs_data_release(data); } \ No newline at end of file diff --git a/WSEvents.h b/WSEvents.h index 31c3bf23..5e9d02bf 100644 --- a/WSEvents.h +++ b/WSEvents.h @@ -42,10 +42,11 @@ class WSEvents : public QObject const char* GetRecordingTimecode(); private Q_SLOTS: + void deferredInitOperations(); void StreamStatus(); void TransitionDurationChanged(int ms); void SelectedSceneChanged(QListWidgetItem *current, QListWidgetItem *prev); - void deferredInitOperations(); + void ModeSwitchClicked(bool checked); private: WSServer *_srv; From ed4526751ef7b5cfe75914f55b5dcd7654820ed0 Mon Sep 17 00:00:00 2001 From: Palakis Date: Thu, 20 Apr 2017 10:06:43 +0200 Subject: [PATCH 10/11] Better param checks for Studio Mode request types --- PROTOCOL.md | 6 +++--- WSRequestHandler.cpp | 48 ++++++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index fa0ded54..79f5d6d4 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -377,7 +377,7 @@ Studio Mode only. Sets the specified scene as the Previewed scene in Studio Mode __Request fields__ : - **"scene-name"** (string) : name of the scene to selected as the preview of Studio Mode -__Response__ : OK if specified scene exists, error otherwise. +__Response__ : OK if Studio Mode is enabled and specified scene exists, error otherwise. --- @@ -387,10 +387,10 @@ Studio Mode only. Transitions the currently previewed scene to Program (main out __Request fields__ : - **"with-transition" (object, optional) : if specified, use this transition when switching from preview to program. This will change the current transition in the frontend to this one. -__Response__ : always OK. No additional fields +__Response__ : OK if studio mode is enabled and optional transition exists, error otherwise. An object passed as `"with-transition"` in a request must have the following fields : -- **"name"** (string) : transition name +- **"name"** (string, optional) : transition name - **"duration"** (integer, optional) : transition duration in milliseconds --- diff --git a/WSRequestHandler.cpp b/WSRequestHandler.cpp index e3ef5466..c2af012f 100644 --- a/WSRequestHandler.cpp +++ b/WSRequestHandler.cpp @@ -788,13 +788,19 @@ void WSRequestHandler::HandleGetPreviewScene(WSRequestHandler *owner) void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) { - const char* scene_name = obs_data_get_string(owner->_requestData, "scene-name"); - if (!scene_name) + if (!Utils::IsPreviewModeActive()) + { + owner->SendErrorResponse("studio mode not enabled"); + return; + } + + if (!obs_data_has_user_value(owner->_requestData, "scene-name")) { owner->SendErrorResponse("invalid request parameters"); return; } + const char* scene_name = obs_data_get_string(owner->_requestData, "scene-name"); Utils::SetPreviewScene(scene_name); owner->SendOKResponse(); @@ -802,28 +808,36 @@ void WSRequestHandler::HandleSetPreviewScene(WSRequestHandler *owner) void WSRequestHandler::HandleTransitionToProgram(WSRequestHandler *owner) { - obs_data_t* transitionInfo = obs_data_get_obj(owner->_requestData, "with-transition"); - - if (transitionInfo) + if (!Utils::IsPreviewModeActive()) { - const char* transitionName = obs_data_get_string(transitionInfo, "name"); - int transitionDuration = obs_data_get_int(transitionInfo, "duration"); + owner->SendErrorResponse("studio mode not enabled"); + return; + } - if (!transitionName) + if (obs_data_has_user_value(owner->_requestData, "with-transition")) + { + obs_data_t* transitionInfo = obs_data_get_obj(owner->_requestData, "with-transition"); + + if (obs_data_has_user_value(transitionInfo, "name")) { - owner->SendErrorResponse("specified transition doesn't exist"); - return; + const char* transitionName = obs_data_get_string(transitionInfo, "name"); + bool success = Utils::SetTransitionByName(transitionName); + + if (!success) + { + owner->SendErrorResponse("specified transition doesn't exist"); + obs_data_release(transitionInfo); + return; + } } - bool success = Utils::SetTransitionByName(transitionName); - if (!success) + if (obs_data_has_user_value(transitionInfo, "duration")) { - owner->SendErrorResponse("unknown error while trying to change current transition"); - return; - } - - if (transitionDuration > 0) + int transitionDuration = obs_data_get_int(transitionInfo, "duration"); Utils::SetTransitionDuration(transitionDuration); + } + + obs_data_release(transitionInfo); } Utils::TransitionToProgram(); From c7305889c367b4021f25447a645140d2dc4e5679 Mon Sep 17 00:00:00 2001 From: Palakis Date: Thu, 20 Apr 2017 19:41:58 +0200 Subject: [PATCH 11/11] Events: add scene description to SwitchScenes and PreviewSceneChanged --- PROTOCOL.md | 3 +++ WSEvents.cpp | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 79f5d6d4..ad6cd806 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -94,6 +94,7 @@ Additional fields will be present in the event message depending on the event ty #### "SwitchScenes" OBS is switching to another scene (called at the end of the transition). - **scene-name** (string) : The name of the scene being switched to. +- **sources** (array of objects) : List of sources composing the scene. Same specification as [`GetCurrentScene`](#getcurrentscene). --- @@ -164,6 +165,8 @@ A transition other than "Cut" has begun. #### "PreviewSceneChanged" The selected Preview scene changed (only in Studio Mode). +- **scene-name** (string) : Name of the scene being previewed. +- **sources** (array of objects) : List of sources composing the scene. Same specification as [`GetCurrentScene`](#getcurrentscene). --- diff --git a/WSEvents.cpp b/WSEvents.cpp index 343e0f02..90fc617a 100644 --- a/WSEvents.cpp +++ b/WSEvents.cpp @@ -283,14 +283,17 @@ void WSEvents::OnSceneChange() obs_data_t *data = obs_data_create(); obs_source_t* current_scene = obs_frontend_get_current_scene(); + obs_data_array_t* scene_items = Utils::GetSceneItems(current_scene); connectSceneSignals(current_scene); obs_data_set_string(data, "scene-name", obs_source_get_name(current_scene)); - + obs_data_set_array(data, "sources", scene_items); + broadcastUpdate("SwitchScenes", data); - obs_data_release(data); + obs_data_array_release(scene_items); obs_source_release(current_scene); + obs_data_release(data); } void WSEvents::OnSceneListChange() @@ -585,12 +588,15 @@ void WSEvents::SelectedSceneChanged(QListWidgetItem *current, QListWidgetItem *p if (!scene) return; obs_source_t* scene_source = obs_scene_get_source(scene); + obs_data_array_t* scene_items = Utils::GetSceneItems(scene_source); obs_data_t* data = obs_data_create(); obs_data_set_string(data, "scene-name", obs_source_get_name(scene_source)); + obs_data_set_array(data, "sources", scene_items); broadcastUpdate("PreviewSceneChanged", data); + obs_data_array_release(scene_items); obs_data_release(data); } }