From b71f633fac82d0a0b83ab7df6c3895911588771a Mon Sep 17 00:00:00 2001
From: WarmUpTill
Date: Sat, 6 May 2023 23:19:24 +0200
Subject: [PATCH] Add support for websocket messages not following the OBS WS
protocol
---
data/locale/en-US.ini | 1 +
src/utils/connection-manager.cpp | 47 ++++++++++++++++++++-
src/utils/connection-manager.hpp | 7 +++-
src/utils/websocket-helpers.cpp | 70 ++++++++++++++++++++++++++------
src/utils/websocket-helpers.hpp | 10 +++--
5 files changed, 117 insertions(+), 18 deletions(-)
diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini
index d6fd3a5a..c3d4421b 100644
--- a/data/locale/en-US.ini
+++ b/data/locale/en-US.ini
@@ -952,6 +952,7 @@ AdvSceneSwitcher.connection.password="Password:"
AdvSceneSwitcher.connection.reconnect="Reconnect automatically:"
AdvSceneSwitcher.connection.reconnectDelay="Automatically reconnect after:"
AdvSceneSwitcher.connection.connectOnStart="Connect on startup:"
+AdvSceneSwitcher.connection.useOBSWebsocketProtocol="
Use the obs-websocket protocol
"
AdvSceneSwitcher.connection.test="Test connection"
AdvSceneSwitcher.connection.status.disconnected="Disconnected"
AdvSceneSwitcher.connection.status.connecting="Connecting"
diff --git a/src/utils/connection-manager.cpp b/src/utils/connection-manager.cpp
index 998df3cb..0522b465 100644
--- a/src/utils/connection-manager.cpp
+++ b/src/utils/connection-manager.cpp
@@ -46,14 +46,16 @@ void SwitcherData::LoadConnections(obs_data_t *obj)
Connection::Connection(std::string name, std::string address, uint64_t port,
std::string pass, bool connectOnStart, bool reconnect,
- int reconnectDelay)
+ int reconnectDelay, bool useOBSWebsocketProtocol)
: Item(name),
_address(address),
_port(port),
_password(pass),
_connectOnStart(connectOnStart),
_reconnect(reconnect),
- _reconnectDelay(reconnectDelay)
+ _reconnectDelay(reconnectDelay),
+ _useOBSWSProtocol(useOBSWebsocketProtocol),
+ _client(useOBSWebsocketProtocol)
{
}
@@ -66,6 +68,8 @@ Connection::Connection(const Connection &other) : Item(other)
_connectOnStart = other._connectOnStart;
_reconnect = other._reconnect;
_reconnectDelay = other._reconnectDelay;
+ _useOBSWSProtocol = other._useOBSWSProtocol;
+ _client.UseOBSWebsocketProtocol(_useOBSWSProtocol);
}
Connection &Connection::operator=(const Connection &other)
@@ -78,6 +82,8 @@ Connection &Connection::operator=(const Connection &other)
_connectOnStart = other._connectOnStart;
_reconnect = other._reconnect;
_reconnectDelay = other._reconnectDelay;
+ _client.UseOBSWebsocketProtocol(_useOBSWSProtocol);
+ _useOBSWSProtocol = other._useOBSWSProtocol;
_client.Disconnect();
}
return *this;
@@ -234,6 +240,7 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
"AdvSceneSwitcher.connection.add", parent),
_address(new QLineEdit()),
_port(new QSpinBox()),
+ _useOBSWSProtocol(new QCheckBox()),
_password(new QLineEdit()),
_showPassword(new QPushButton()),
_connectOnStart(new QCheckBox()),
@@ -257,7 +264,10 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
_connectOnStart->setChecked(settings._connectOnStart);
_reconnect->setChecked(settings._reconnect);
_reconnectDelay->setValue(settings._reconnectDelay);
+ _useOBSWSProtocol->setChecked(settings._useOBSWSProtocol);
+ QWidget::connect(_useOBSWSProtocol, SIGNAL(stateChanged(int)), this,
+ SLOT(ProtocolChanged(int)));
QWidget::connect(_reconnect, SIGNAL(stateChanged(int)), this,
SLOT(ReconnectChanged(int)));
QWidget::connect(_showPassword, SIGNAL(pressed()), this,
@@ -312,6 +322,12 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
row, 0);
layout->addWidget(_reconnectDelay, row, 1);
++row;
+ layout->addWidget(
+ new QLabel(obs_module_text(
+ "AdvSceneSwitcher.connection.useOBSWebsocketProtocol")),
+ row, 0);
+ layout->addWidget(_useOBSWSProtocol, row, 1);
+ ++row;
layout->addWidget(_test, row, 0);
layout->addWidget(_status, row, 1);
++row;
@@ -319,9 +335,16 @@ ConnectionSettingsDialog::ConnectionSettingsDialog(QWidget *parent,
setLayout(layout);
ReconnectChanged(_reconnect->isChecked());
+ ProtocolChanged(_useOBSWSProtocol->isChecked());
HidePassword();
}
+void ConnectionSettingsDialog::ProtocolChanged(int state)
+{
+ _password->setEnabled(state);
+ _showPassword->setEnabled(state);
+}
+
void ConnectionSettingsDialog::ReconnectChanged(int state)
{
_reconnectDelay->setEnabled(state);
@@ -365,6 +388,7 @@ void ConnectionSettingsDialog::HidePassword()
void ConnectionSettingsDialog::TestConnection()
{
+ _testConnection.UseOBSWebsocketProtocol(_useOBSWSProtocol->isChecked());
_testConnection.Disconnect();
_testConnection.Connect(GetUri(_address->text().toStdString(),
_port->value()),
@@ -391,6 +415,7 @@ bool ConnectionSettingsDialog::AskForSettings(QWidget *parent,
settings._connectOnStart = dialog._connectOnStart->isChecked();
settings._reconnect = dialog._reconnect->isChecked();
settings._reconnectDelay = dialog._reconnectDelay->value();
+ settings.UseOBSWebsocketProtocol(dialog._useOBSWSProtocol->isChecked());
settings.Reconnect();
return true;
}
@@ -398,6 +423,16 @@ bool ConnectionSettingsDialog::AskForSettings(QWidget *parent,
void Connection::Load(obs_data_t *obj)
{
Item::Load(obj);
+
+ if (obs_data_has_user_value(obj, "version")) {
+ UseOBSWebsocketProtocol(
+ obs_data_get_bool(obj, "useOBSWSProtocol"));
+ } else {
+ // TODO: Remove this fallback in future version
+ _useOBSWSProtocol = true;
+ }
+ _client.UseOBSWebsocketProtocol(_useOBSWSProtocol);
+
_address = obs_data_get_string(obj, "address");
_port = obs_data_get_int(obj, "port");
_password = obs_data_get_string(obj, "password");
@@ -414,12 +449,20 @@ void Connection::Load(obs_data_t *obj)
void Connection::Save(obs_data_t *obj) const
{
Item::Save(obj);
+ obs_data_set_bool(obj, "useOBSWSProtocol", _useOBSWSProtocol);
obs_data_set_string(obj, "address", _address.c_str());
obs_data_set_int(obj, "port", _port);
obs_data_set_string(obj, "password", _password.c_str());
obs_data_set_bool(obj, "connectOnStart", _connectOnStart);
obs_data_set_bool(obj, "reconnect", _reconnect);
obs_data_set_int(obj, "reconnectDelay", _reconnectDelay);
+ obs_data_set_int(obj, "version", 1);
+}
+
+void Connection::UseOBSWebsocketProtocol(bool useOBSWSProtocol)
+{
+ _useOBSWSProtocol = useOBSWSProtocol;
+ _client.UseOBSWebsocketProtocol(useOBSWSProtocol);
}
} // namespace advss
diff --git a/src/utils/connection-manager.hpp b/src/utils/connection-manager.hpp
index 4285decc..0177d9a7 100644
--- a/src/utils/connection-manager.hpp
+++ b/src/utils/connection-manager.hpp
@@ -24,7 +24,7 @@ class Connection : public Item {
public:
Connection(std::string name, std::string address, uint64_t port,
std::string pass, bool connectOnStart, bool reconnect,
- int reconnectDelay);
+ int reconnectDelay, bool useOBSWebsocketProtocol);
Connection() = default;
Connection(const Connection &);
Connection &operator=(const Connection &);
@@ -42,6 +42,9 @@ public:
std::vector &Events() { return _client.Events(); }
private:
+ void UseOBSWebsocketProtocol(bool);
+
+ bool _useOBSWSProtocol = true;
std::string _address = "localhost";
uint64_t _port = 4455;
std::string _password = "password";
@@ -70,6 +73,7 @@ public:
static bool AskForSettings(QWidget *parent, Connection &settings);
private slots:
+ void ProtocolChanged(int);
void ReconnectChanged(int);
void ShowPassword();
void HidePassword();
@@ -79,6 +83,7 @@ private slots:
private:
QLineEdit *_address;
QSpinBox *_port;
+ QCheckBox *_useOBSWSProtocol;
QLineEdit *_password;
QPushButton *_showPassword;
QCheckBox *_connectOnStart;
diff --git a/src/utils/websocket-helpers.cpp b/src/utils/websocket-helpers.cpp
index d5b02326..9fe5aed0 100644
--- a/src/utils/websocket-helpers.cpp
+++ b/src/utils/websocket-helpers.cpp
@@ -75,7 +75,8 @@ extern "C" void RegisterWebsocketVendor()
}
}
-WSConnection::WSConnection() : QObject(nullptr)
+WSConnection::WSConnection(bool useOBSProtocol)
+ : QObject(nullptr), _useOBSProtocol(useOBSProtocol)
{
_client.get_alog().clear_channels(
websocketpp::log::alevel::frame_header |
@@ -86,9 +87,7 @@ WSConnection::WSConnection() : QObject(nullptr)
_client.set_reuse_addr(true);
#endif
- _client.set_open_handler(bind(&WSConnection::OnOpen, this, _1));
- _client.set_message_handler(
- bind(&WSConnection::OnMessage, this, _1, _2));
+ UseOBSWebsocketProtocol(useOBSProtocol);
_client.set_close_handler(bind(&WSConnection::OnClose, this, _1));
}
@@ -180,31 +179,43 @@ void WSConnection::Disconnect()
_status = Status::DISCONNECTED;
}
-void WSConnection::SendRequest(const std::string &msg)
+static std::string constructVendorRequestMessage(const std::string &message,
+ const std::string &uri)
{
auto request = obs_data_create();
obs_data_set_int(request, "op", 6);
auto *data = obs_data_create();
obs_data_set_string(data, "requestType", "CallVendorRequest");
- obs_data_set_string(data, "requestId", (msg + " - " + _uri).c_str());
+ obs_data_set_string(data, "requestId", (message + " - " + uri).c_str());
auto vendorData = obs_data_create();
obs_data_set_string(vendorData, "vendorName", VendorName);
obs_data_set_string(vendorData, "requestType", VendorRequest);
auto msgObj = obs_data_create();
- obs_data_set_string(msgObj, "message", msg.c_str());
+ obs_data_set_string(msgObj, "message", message.c_str());
obs_data_set_obj(vendorData, "requestData", msgObj);
obs_data_set_obj(data, "requestData", vendorData);
obs_data_set_obj(request, "d", data);
- const std::string response(obs_data_get_json(request));
+
+ const std::string result(obs_data_get_json(request));
+
obs_data_release(msgObj);
obs_data_release(vendorData);
obs_data_release(data);
obs_data_release(request);
- Send(response);
+ return result;
+}
+
+void WSConnection::SendRequest(const std::string &msg)
+{
+ if (_useOBSProtocol) {
+ Send(constructVendorRequestMessage(msg, _uri));
+ } else {
+ Send(msg);
+ }
}
WSConnection::Status WSConnection::GetStatus() const
@@ -212,7 +223,26 @@ WSConnection::Status WSConnection::GetStatus() const
return _status;
}
-void WSConnection::OnOpen(connection_hdl)
+void WSConnection::UseOBSWebsocketProtocol(bool useOBSProtocol)
+{
+ _useOBSProtocol = useOBSProtocol;
+ _client.set_open_handler(bind(useOBSProtocol
+ ? &WSConnection::OnOBSOpen
+ : &WSConnection::OnGenericOpen,
+ this, _1));
+ _client.set_message_handler(
+ bind(useOBSProtocol ? &WSConnection::OnOBSMessage
+ : &WSConnection::OnGenericMessage,
+ this, _1, _2));
+}
+
+void WSConnection::OnGenericOpen(connection_hdl hdl)
+{
+ blog(LOG_INFO, "connection to %s opened", _uri.c_str());
+ _status = Status::AUTHENTICATED;
+}
+
+void WSConnection::OnOBSOpen(connection_hdl)
{
blog(LOG_INFO, "connection to %s opened", _uri.c_str());
_status = Status::CONNECTING;
@@ -294,7 +324,23 @@ void WSConnection::HandleResponse(obs_data_t *response)
obs_data_release(data);
}
-void WSConnection::OnMessage(connection_hdl, client::message_ptr message)
+void WSConnection::OnGenericMessage(connection_hdl hdl,
+ client::message_ptr message)
+{
+ if (!message) {
+ return;
+ }
+ if (message->get_opcode() != websocketpp::frame::opcode::text) {
+ return;
+ }
+
+ std::lock_guard lock(switcher->m);
+ const auto payload = message->get_payload();
+ _messages.emplace_back(payload);
+ vblog(LOG_INFO, "received event msg \"%s\"", payload.c_str());
+}
+
+void WSConnection::OnOBSMessage(connection_hdl, client::message_ptr message)
{
if (!message) {
return;
@@ -305,7 +351,7 @@ void WSConnection::OnMessage(connection_hdl, client::message_ptr message)
std::string payload = message->get_payload();
const char *msg = payload.c_str();
- obs_data_t *json = obs_data_create_from_json(msg);
+ auto json = obs_data_create_from_json(msg);
if (!json) {
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
obs_data_release(json);
diff --git a/src/utils/websocket-helpers.hpp b/src/utils/websocket-helpers.hpp
index 68782776..145d9098 100644
--- a/src/utils/websocket-helpers.hpp
+++ b/src/utils/websocket-helpers.hpp
@@ -33,7 +33,7 @@ void SendWebsocketEvent(const std::string &);
class WSConnection : public QObject {
public:
- explicit WSConnection();
+ explicit WSConnection(bool useOBSProtocol = true);
virtual ~WSConnection();
void Connect(const std::string &uri, const std::string &pass,
@@ -50,10 +50,13 @@ public:
AUTHENTICATED,
};
Status GetStatus() const;
+ void UseOBSWebsocketProtocol(bool);
private:
- void OnOpen(connection_hdl hdl);
- void OnMessage(connection_hdl hdl, client::message_ptr message);
+ void OnGenericOpen(connection_hdl hdl);
+ void OnOBSOpen(connection_hdl hdl);
+ void OnGenericMessage(connection_hdl hdl, client::message_ptr message);
+ void OnOBSMessage(connection_hdl hdl, client::message_ptr message);
void OnClose(connection_hdl hdl);
void Send(const std::string &);
void ConnectThread();
@@ -76,6 +79,7 @@ private:
std::atomic_bool _disconnect{false};
std::vector _messages;
+ bool _useOBSProtocol = true;
};
} // namespace advss