Add support for websocket messages not following the OBS WS protocol

This commit is contained in:
WarmUpTill 2023-05-06 23:19:24 +02:00 committed by WarmUpTill
parent dbb674aa8e
commit b71f633fac
5 changed files with 117 additions and 18 deletions

View File

@ -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="<html><head/><body><p>Use the obs-websocket <a href=\"https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md\"><span style=\" text-decoration: underline; color:#268bd2;\">protocol</span></a></p></body></html>"
AdvSceneSwitcher.connection.test="Test connection"
AdvSceneSwitcher.connection.status.disconnected="Disconnected"
AdvSceneSwitcher.connection.status.connecting="Connecting"

View File

@ -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

View File

@ -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<std::string> &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;

View File

@ -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<std::mutex> 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);

View File

@ -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<std::string> _messages;
bool _useOBSProtocol = true;
};
} // namespace advss