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