From 1ebb6f9257c087e2774988f13fe0efefb756a395 Mon Sep 17 00:00:00 2001
From: Palakis <contact@slepin.fr>
Date: Tue, 28 Feb 2017 15:40:40 +0100
Subject: [PATCH] Transition events + scene change compatibility with OBS
 18.0.0

---
 PROTOCOL.md       | 12 ++++++
 WSEvents.cpp      | 99 ++++++++++++++++++++++++++++++++---------------
 WSEvents.h        | 10 ++++-
 WSServer.cpp      | 12 +++++-
 obs-websocket.cpp |  2 -
 5 files changed, 98 insertions(+), 37 deletions(-)

diff --git a/PROTOCOL.md b/PROTOCOL.md
index 1b604979..59b63c01 100644
--- a/PROTOCOL.md
+++ b/PROTOCOL.md
@@ -16,6 +16,8 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami
     - ["SwitchTransition"](#switchtransition)
     - ["TransitionDurationChanged"](#transitiondurationchanged)
     - ["TransitionListChanged"](#transitionlistchanged)
+    - ["TransitionBegin"](#transitionbegin)
+    - ["TransitionEnd"](#transitionend)
     - ["ProfileChanged"](#profilechanged)
     - ["ProfileListChanged"](#profilelistchanged)
     - ["StreamStarting"](#streamstarting)
@@ -106,6 +108,16 @@ The list of available transitions has been modified (Transitions have been added
 
 ---
 
+#### "TransitionBegin"
+A non-fixed transition has begun.
+
+---
+
+#### "TransitionEnd"
+A non-fixed transition has ended.
+
+---
+
 #### "ProfileChanged"
 Triggered when switching to another profile or when renaming the current profile.
 
diff --git a/WSEvents.cpp b/WSEvents.cpp
index 9ae977ca..c68d0fef 100644
--- a/WSEvents.cpp
+++ b/WSEvents.cpp
@@ -34,6 +34,15 @@ WSEvents::WSEvents(WSServer *srv)
 	connect(statusTimer, SIGNAL(timeout()), this, SLOT(StreamStatus()));
 	statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS
 
+	transition_handler = nullptr;
+
+	WSEvents* instance = this;
+	QTimer::singleShot(1000, [instance]() {
+		obs_source_t* transition = obs_frontend_get_current_transition();
+		instance->connectTransitionSignals(transition);
+		obs_source_release(transition);
+	});
+
 	_stream_starttime = 0;
 	_rec_starttime = 0;
 }
@@ -50,7 +59,7 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void *private
 	if (!owner->_srv)
 		return;
 
-	// TODO : implement SourceChanged, SourceOrderChanged and RepopulateSources
+	// TODO : implement SourceOrderChanged and RepopulateSources
 
 	if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED)
 	{
@@ -136,39 +145,32 @@ void WSEvents::broadcastUpdate(const char *updateType, obs_data_t *additionalFie
 	obs_data_release(update);
 }
 
+void WSEvents::connectTransitionSignals(obs_source_t* current_transition)
+{
+	if (!obs_transition_fixed(current_transition))
+	{
+		transition_handler = obs_source_get_signal_handler(current_transition);
+		signal_handler_connect(transition_handler, "transition_start", OnTransitionBegin, this);
+		signal_handler_connect(transition_handler, "transition_stop", OnTransitionEnd, this);
+	}
+	else
+	{
+		transition_handler = nullptr;
+	}
+}
+
 void WSEvents::OnSceneChange()
 {
 	// Implements an existing update type from bilhamil's OBS Remote
-
-	// Default behavior : get the new scene from the running transition
-	obs_source_t *transition = obs_frontend_get_current_transition();
-	obs_source_t *new_scene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_B);
-	if (!new_scene)
-	{
-		obs_source_release(transition);
-		return;
-	}
-	
-	const char *scene_name = obs_source_get_name(new_scene);
-	if (!scene_name)
-	{
-		// Fallback behaviour : get the new scene straight from the API
-		obs_source_release(new_scene);
-		new_scene = obs_frontend_get_current_scene();
-
-		if (new_scene) {
-			scene_name = obs_source_get_name(new_scene);
-		}
-	}
-
 	obs_data_t *data = obs_data_create();
-	obs_data_set_string(data, "scene-name", scene_name);
+
+	obs_source_t* current_scene = obs_frontend_get_current_scene();
+	obs_data_set_string(data, "scene-name", obs_source_get_name(current_scene));
 	
 	broadcastUpdate("SwitchScenes", data);
 
 	obs_data_release(data);
-	obs_source_release(new_scene);
-	obs_source_release(transition);
+	obs_source_release(current_scene);
 }
 
 void WSEvents::OnSceneListChange()
@@ -188,16 +190,22 @@ void WSEvents::OnSceneCollectionListChange()
 
 void WSEvents::OnTransitionChange()
 {
-	obs_source_t *transition = obs_frontend_get_current_transition();
-	const char *transition_name = obs_source_get_name(transition);
+	if (transition_handler)
+	{
+		signal_handler_disconnect(transition_handler, "transition_start", OnTransitionBegin, this);
+		signal_handler_disconnect(transition_handler, "transition_stop", OnTransitionEnd, this);
+	}
+
+	obs_source_t* current_transition = obs_frontend_get_current_transition();
+	connectTransitionSignals(current_transition);
 
 	obs_data_t *data = obs_data_create();
-	obs_data_set_string(data, "transition-name", transition_name);
-
+	obs_data_set_string(data, "transition-name", obs_source_get_name(current_transition));
+		
 	broadcastUpdate("SwitchTransition", data);
-
+	
 	obs_data_release(data);
-	obs_source_release(transition);
+	obs_source_release(current_transition);
 }
 
 void WSEvents::OnTransitionListChange()
@@ -352,4 +360,31 @@ void WSEvents::TransitionDurationChanged(int ms)
 	broadcastUpdate("TransitionDurationChanged", fields);
 
 	obs_data_release(fields);
+}
+
+void WSEvents::OnTransitionBegin(void* param, calldata_t* data)
+{
+	UNUSED_PARAMETER(data);
+
+	WSEvents* instance = static_cast<WSEvents*>(param);
+	instance->broadcastUpdate("TransitionBegin");
+
+	blog(LOG_INFO, "transition begin");
+}
+
+void WSEvents::OnTransitionEnd(void* param, calldata_t* data)
+{
+	UNUSED_PARAMETER(data);
+
+	WSEvents* instance = static_cast<WSEvents*>(param);
+	instance->broadcastUpdate("TransitionEnd");
+
+	blog(LOG_INFO, "transition end");
+}
+
+void WSEvents::OnTransitionStopped(void* param, calldata_t* data)
+{
+
+
+	blog(LOG_INFO, "transition stopped");
 }
\ No newline at end of file
diff --git a/WSEvents.h b/WSEvents.h
index 3b0b65b0..b94713ab 100644
--- a/WSEvents.h
+++ b/WSEvents.h
@@ -31,6 +31,7 @@ class WSEvents : public QObject
 		explicit WSEvents(WSServer *srv);
 		~WSEvents();
 		static void FrontendEventHandler(enum obs_frontend_event event, void *private_data);
+		void connectTransitionSignals(obs_source_t* current_transition);
 
 	private Q_SLOTS:
 		void StreamStatus();
@@ -38,12 +39,15 @@ class WSEvents : public QObject
 
 	private:
 		WSServer *_srv;
+		signal_handler_t *transition_handler;
+		
 		uint64_t _stream_starttime;
 		uint64_t _rec_starttime;
 		uint64_t _lastBytesSent;
 		uint64_t _lastBytesSentTime;
-		void broadcastUpdate(const char *updateType, obs_data_t *additionalFields);
 
+		void broadcastUpdate(const char *updateType, obs_data_t *additionalFields);
+		
 		void OnSceneChange();
 		void OnSceneListChange();
 		void OnSceneCollectionChange();
@@ -66,6 +70,10 @@ class WSEvents : public QObject
 		void OnRecordingStopped();
 
 		void OnExit();
+
+		static void OnTransitionBegin(void *param, calldata_t *data);
+		static void OnTransitionEnd(void *param, calldata_t *data);
+		static void OnTransitionStopped(void *param, calldata_t *data);
 };
 
 #endif // WSEVENTS_H
\ No newline at end of file
diff --git a/WSServer.cpp b/WSServer.cpp
index ffdeea97..555609c6 100644
--- a/WSServer.cpp
+++ b/WSServer.cpp
@@ -27,7 +27,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
 
 QT_USE_NAMESPACE
 
-WSServer* WSServer::Instance = nullptr;
+WSServer* WSServer::Instance = new WSServer();
 
 WSServer::WSServer(QObject *parent) :
 	QObject(parent),
@@ -97,6 +97,10 @@ void WSServer::broadcast(QString message)
 	}
 
 	_clMutex.unlock();
+	
+	// Dirty hack because several quick successive calls to sendTextMessage() 
+	// can deadlock the socket
+	QThread::msleep(50);
 }
 
 void WSServer::onNewConnection()
@@ -126,6 +130,10 @@ void WSServer::textMessageReceived(QString message)
 	{
 		WSRequestHandler handler(pSocket);
 		handler.processIncomingMessage(message);
+
+		// Dirty hack because several quick successive calls to sendTextMessage() 
+		// can deadlock the socket
+		QThread::msleep(50);
 	}
 }
 
@@ -140,7 +148,7 @@ void WSServer::socketDisconnected()
 		_clMutex.lock();
 		_clients.removeAll(pSocket);
 		_clMutex.unlock();
-
+		
 		pSocket->deleteLater();
 
 		QByteArray client_ip = pSocket->peerAddress().toString().toUtf8();
diff --git a/obs-websocket.cpp b/obs-websocket.cpp
index 558d0adb..fc3e3cea 100644
--- a/obs-websocket.cpp
+++ b/obs-websocket.cpp
@@ -42,8 +42,6 @@ bool obs_module_load(void)
 	Config* config = Config::Current();
 	config->Load();
 
-	WSServer::Instance = new WSServer();
-
 	if (config->ServerEnabled)
 		WSServer::Instance->Start(config->ServerPort);