Merge pull request #265 from Palakis/websocketpp-4.x

Replace QWebSocketServer with websocket++ in 4.x
This commit is contained in:
Stéphane Lepin 2019-01-01 16:35:08 +01:00 committed by GitHub
commit be6d9791f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 300 additions and 366 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "deps/websocketpp"]
path = deps/websocketpp
url = https://github.com/zaphoyd/websocketpp.git
[submodule "deps/asio"]
path = deps/asio
url = https://github.com/chriskohlhoff/asio.git

View File

@ -20,6 +20,7 @@ echo "[obs-websocket] Building 'obs-websocket' for macOS."
mkdir -p build && cd build
cmake .. \
-DQTDIR=/usr/local/opt/qt \
-DBOOST_ROOT=/usr/local/opt/boost \
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
-DLIBOBS_LIB=../../obs-studio/libobs \
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \

View File

@ -11,7 +11,8 @@ apt-get install -y \
checkinstall \
cmake \
obs-studio \
libqt5websockets5-dev
qtbase5-dev \
libboost-all-dev
# Dirty hack
wget -O /usr/include/obs/obs-frontend-api.h https://raw.githubusercontent.com/obsproject/obs-studio/master/UI/obs-frontend-api/obs-frontend-api.h

View File

@ -12,123 +12,6 @@
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../build/QtNetwork</string>
<key>PATH_TYPE</key>
<integer>1</integer>
<key>PERMISSIONS</key>
<integer>292</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>../../build/QtWebSockets</string>
<key>PATH_TYPE</key>
<integer>1</integer>
<key>PERMISSIONS</key>
<integer>292</integer>
<key>TYPE</key>
<integer>3</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>bin</string>
<key>PATH_TYPE</key>
<integer>0</integer>
<key>PERMISSIONS</key>
<integer>509</integer>
<key>TYPE</key>
<integer>2</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>Resources</string>
<key>PATH_TYPE</key>
<integer>0</integer>
<key>PERMISSIONS</key>
<integer>509</integer>
<key>TYPE</key>
<integer>2</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>Contents</string>
<key>PATH_TYPE</key>
<integer>0</integer>
<key>PERMISSIONS</key>
<integer>509</integer>
<key>TYPE</key>
<integer>2</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>OBS.app</string>
<key>PATH_TYPE</key>
<integer>0</integer>
<key>PERMISSIONS</key>
<integer>509</integer>
<key>TYPE</key>
<integer>2</integer>
<key>UID</key>
<integer>0</integer>
</dict>
</array>
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
<string>Applications</string>
<key>PATH_TYPE</key>
<integer>0</integer>
<key>PERMISSIONS</key>
<integer>509</integer>
<key>TYPE</key>
<integer>1</integer>
<key>UID</key>
<integer>0</integer>
</dict>
<dict>
<key>CHILDREN</key>
<array>

View File

@ -12,9 +12,6 @@ fi
echo "[obs-websocket] Preparing package build"
export QT_CELLAR_PREFIX="$(/usr/bin/find /usr/local/Cellar/qt -d 1 | sort -t '.' -k 1,1n -k 2,2n -k 3,3n | tail -n 1)"
export WS_LIB="/usr/local/opt/qt/lib/QtWebSockets.framework/QtWebSockets"
export NET_LIB="/usr/local/opt/qt/lib/QtNetwork.framework/QtNetwork"
export GIT_HASH=$(git rev-parse --short HEAD)
export VERSION="$GIT_HASH-$TRAVIS_BRANCH"
@ -27,46 +24,17 @@ fi
export FILENAME="obs-websocket-$VERSION.pkg"
export LATEST_FILENAME="obs-websocket-latest-$LATEST_VERSION.pkg"
echo "[obs-websocket] Copying Qt dependencies"
if [ ! -f ./build/$(basename $WS_LIB) ]; then cp $WS_LIB ./build; fi
if [ ! -f ./build/$(basename $NET_LIB) ]; then cp $NET_LIB ./build; fi
chmod +rw ./build/QtWebSockets ./build/QtNetwork
echo "[obs-websocket] Modifying QtNetwork"
install_name_tool \
-id @rpath/QtNetwork \
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
-change $QT_CELLAR_PREFIX/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
./build/QtNetwork
echo "[obs-websocket] Modifying QtWebSockets"
install_name_tool \
-id @rpath/QtWebSockets \
-change /usr/local/opt/qt/lib/QtWebSockets.framework/Versions/5/QtWebSockets @rpath/QtWebSockets \
-change $QT_CELLAR_PREFIX/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
-change $QT_CELLAR_PREFIX/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
./build/QtWebSockets
echo "[obs-websocket] Modifying obs-websocket.so"
install_name_tool \
-change /usr/local/opt/qt/lib/QtWebSockets.framework/Versions/5/QtWebSockets @rpath/QtWebSockets \
-change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @rpath/QtWidgets \
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
-change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @rpath/QtGui \
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
./build/obs-websocket.so
# Check if replacement worked
echo "[obs-websocket] Dependencies for QtNetwork"
otool -L ./build/QtNetwork
echo "[obs-websocket] Dependencies for QtWebSockets"
otool -L ./build/QtWebSockets
echo "[obs-websocket] Dependencies for obs-websocket"
otool -L ./build/obs-websocket.so
chmod -w ./build/QtWebSockets ./build/QtNetwork
echo "[obs-websocket] Actual package build"
packagesbuild ./CI/macos/obs-websocket.pkgproj

View File

@ -17,7 +17,7 @@ PAGER=cat checkinstall -y --type=debian --fstrans=no --nodoc \
--backup=no --deldoc=yes --install=no \
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
--pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \
--requires="libqt5websockets5" --pkggroup="video" \
--pkggroup="video" \
--pkgsource="https://github.com/Palakis/obs-websocket" \
--pakdir="/package"

View File

@ -6,14 +6,26 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (WIN32 OR APPLE)
include(external/FindLibObs.cmake)
endif()
add_definitions(-DASIO_STANDALONE)
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if (WIN32 OR APPLE)
include(external/FindLibObs.cmake)
endif()
find_package(LibObs REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5WebSockets REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Boost REQUIRED)
set(obs-websocket_SOURCES
src/obs-websocket.cpp
@ -52,13 +64,14 @@ add_library(obs-websocket MODULE
include_directories(
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
${Qt5Core_INCLUDES}
${Qt5WebSockets_INCLUDES}
${Qt5Widgets_INCLUDES})
${Qt5Widgets_INCLUDES}
${Boost_INCLUDE_DIRS}
"${CMAKE_SOURCE_DIR}/deps/asio/asio/include"
"${CMAKE_SOURCE_DIR}/deps/websocketpp")
target_link_libraries(obs-websocket
libobs
Qt5::Core
Qt5::WebSockets
Qt5::Widgets)
# --- End of section ---
@ -70,6 +83,8 @@ if(WIN32)
message(FATAL_ERROR "Could not find OBS Frontend API's library !")
endif()
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(ARCH_NAME "64bit")
set(OBS_BUILDDIR_ARCH "build64")
@ -102,8 +117,6 @@ if(WIN32)
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:obs-websocket>"
"${QTDIR}/bin/Qt5WebSockets.dll"
"${QTDIR}/bin/Qt5Network.dll"
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
# If config is RelWithDebInfo, package release files
@ -118,8 +131,6 @@ if(WIN32)
COMMAND if $<CONFIG:RelWithDebInfo>==1 ("${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:obs-websocket>"
"${QTDIR}/bin/Qt5WebSockets.dll"
"${QTDIR}/bin/Qt5Network.dll"
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
COMMAND if $<CONFIG:RelWithDebInfo>==1 ("${CMAKE_COMMAND}" -E copy
@ -130,8 +141,6 @@ if(WIN32)
COMMAND if $<CONFIG:Debug>==1 (
"${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:obs-websocket>"
"${QTDIR}/bin/Qt5WebSocketsd.dll"
"${QTDIR}/bin/Qt5Networkd.dll"
"${LIBOBS_INCLUDE_DIR}/../${OBS_BUILDDIR_ARCH}/rundir/$<CONFIG>/obs-plugins/${ARCH_NAME}")
COMMAND if $<CONFIG:Debug>==1 (
@ -155,11 +164,8 @@ endif()
# --- Linux-specific build settings and tasks ---
if(UNIX AND NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set_target_properties(obs-websocket PROPERTIES PREFIX "")
target_link_libraries(obs-websocket
obs-frontend-api)
target_link_libraries(obs-websocket obs-frontend-api)
file(GLOB locale_files data/locale/*.ini)

View File

@ -8,6 +8,7 @@ install:
- 7z x dependencies2015.zip -odependencies2015
- set DepsPath32=%CD%\dependencies2015\win32
- set DepsPath64=%CD%\dependencies2015\win64
- set BoostRoot=C:\Libraries\boost_1_67_0
- call C:\projects\obs-websocket\CI\install-setup-qt.cmd
- set build_config=RelWithDebInfo
- call C:\projects\obs-websocket\CI\install-build-obs.cmd
@ -15,9 +16,9 @@ install:
- mkdir build32
- mkdir build64
- cd ./build32
- cmake -G "Visual Studio 14 2015" -DQTDIR="%QTDIR32%" -DLibObs_DIR="C:\projects\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
- cmake -G "Visual Studio 14 2015" -DQTDIR="%QTDIR32%" -DBOOST_ROOT="%BoostRoot%" -DLibObs_DIR="C:\projects\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
- cd ../build64
- cmake -G "Visual Studio 14 2015 Win64" -DQTDIR="%QTDIR64%" -DLibObs_DIR="C:\projects\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
- cmake -G "Visual Studio 14 2015 Win64" -DQTDIR="%QTDIR64%" -DBOOST_ROOT="%BoostRoot%" -DLibObs_DIR="C:\projects\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
build_script:
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-websocket\build32\obs-websocket.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"

1
deps/asio vendored Submodule

@ -0,0 +1 @@
Subproject commit b73dc1d2c0ecb9452a87c26544d7f71e24342df6

1
deps/websocketpp vendored Submodule

@ -0,0 +1 @@
Subproject commit c6d7e295bf5a0ab9b5f896720cc1a0e0fdc397a7

View File

@ -36,7 +36,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define QT_TO_UTF8(str) str.toUtf8().constData()
Config* Config::_instance = new Config();
ConfigPtr Config::_instance = ConfigPtr(new Config());
ConfigPtr Config::Current()
{
return _instance;
}
Config::Config() :
ServerEnabled(true),
@ -179,8 +184,3 @@ bool Config::CheckAuth(QString response)
return authSuccess;
}
Config* Config::Current()
{
return _instance;
}

View File

@ -20,9 +20,15 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define CONFIG_H
#include <QString>
#include <QSharedPointer>
class Config;
typedef QSharedPointer<Config> ConfigPtr;
class Config {
public:
static ConfigPtr Current();
Config();
~Config();
void Load();
@ -46,10 +52,8 @@ class Config {
QString SessionChallenge;
bool SettingsLoaded;
static Config* Current();
private:
static Config* _instance;
static ConfigPtr _instance;
};
#endif // CONFIG_H

View File

@ -376,15 +376,6 @@ void Utils::SysTrayNotify(QString &text,
trayIcon->showMessage(title, text, icon);
}
QString Utils::FormatIPAddress(QHostAddress &addr) {
QRegExp v4regex("(::ffff:)(((\\d).){3})", Qt::CaseInsensitive);
QString addrString = addr.toString();
if (addrString.contains(v4regex)) {
addrString = QHostAddress(addr.toIPv4Address()).toString();
}
return addrString;
}
const char* Utils::GetRecordingFolder() {
config_t* profile = obs_frontend_get_profile_config();
QString outputMode = config_get_string(profile, "Output", "Mode");

View File

@ -26,7 +26,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QLayout>
#include <QListWidget>
#include <QSystemTrayIcon>
#include <QHostAddress>
#include <obs.hpp>
#include <obs-module.h>
@ -70,8 +69,6 @@ class Utils {
QSystemTrayIcon::MessageIcon n,
QString title = QString("obs-websocket"));
static QString FormatIPAddress(QHostAddress &addr);
static const char* GetRecordingFolder();
static bool SetRecordingFolder(const char* path);

View File

@ -62,9 +62,17 @@ void* calldata_get_ptr(const calldata_t* data, const char* name) {
return ptr;
}
WSEvents* WSEvents::Instance = nullptr;
WSEventsPtr WSEvents::_instance = WSEventsPtr(nullptr);
WSEvents::WSEvents(WSServer* srv) {
WSEventsPtr WSEvents::Current() {
return _instance;
}
void WSEvents::ResetCurrent(WSServerPtr srv) {
_instance = WSEventsPtr(new WSEvents(srv));
}
WSEvents::WSEvents(WSServerPtr srv) {
_srv = srv;
obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this);
@ -214,7 +222,7 @@ void WSEvents::broadcastUpdate(const char* updateType,
obs_data_apply(update, additionalFields);
QString json = obs_data_get_json(update);
_srv->broadcast(json);
_srv->broadcast(json.toStdString());
if (Config::Current()->DebugEnabled)
blog(LOG_DEBUG, "Update << '%s'", json.toUtf8().constData());

View File

@ -22,17 +22,27 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <obs.hpp>
#include <obs-frontend-api.h>
#include <QListWidgetItem>
#include <QSharedPointer>
#include "WSServer.h"
class WSEvents : public QObject {
Q_OBJECT
public:
explicit WSEvents(WSServer* srv);
class WSEvents;
typedef QSharedPointer<WSEvents> WSEventsPtr;
class WSEvents : public QObject
{
Q_OBJECT
public:
static WSEventsPtr Current();
static void ResetCurrent(WSServerPtr srv);
explicit WSEvents(WSServerPtr srv);
~WSEvents();
static void FrontendEventHandler(
enum obs_frontend_event event, void* privateData);
static WSEvents* Instance;
void connectSceneSignals(obs_source_t* scene);
void hookTransitionBeginEvent();
@ -44,14 +54,16 @@ class WSEvents : public QObject {
bool HeartbeatIsActive;
private slots:
private slots:
void deferredInitOperations();
void StreamStatus();
void Heartbeat();
void TransitionDurationChanged(int ms);
private:
WSServer* _srv;
private:
static WSEventsPtr _instance;
WSServerPtr _srv;
OBSSource currentScene;
bool pulse;

View File

@ -128,26 +128,23 @@ QSet<QString> WSRequestHandler::authNotRequired {
"Authenticate"
};
WSRequestHandler::WSRequestHandler(QWebSocket* client) :
WSRequestHandler::WSRequestHandler(QVariantHash& connProperties) :
_messageId(0),
_requestType(""),
data(nullptr),
_client(client)
_connProperties(connProperties)
{
}
void WSRequestHandler::processIncomingMessage(QString textMessage) {
QByteArray msgData = textMessage.toUtf8();
const char* msg = msgData.constData();
std::string WSRequestHandler::processIncomingMessage(std::string& textMessage) {
std::string msgContainer(textMessage);
const char* msg = msgContainer.c_str();
data = obs_data_create_from_json(msg);
if (!data) {
if (!msg)
msg = "<null pointer>";
blog(LOG_ERROR, "invalid JSON payload received for '%s'", msg);
SendErrorResponse("invalid JSON payload");
return;
return _response;
}
if (Config::Current()->DebugEnabled) {
@ -158,18 +155,18 @@ void WSRequestHandler::processIncomingMessage(QString textMessage) {
|| !hasField("message-id"))
{
SendErrorResponse("missing request parameters");
return;
return _response;
}
_requestType = obs_data_get_string(data, "request-type");
_messageId = obs_data_get_string(data, "message-id");
if (Config::Current()->AuthRequired
&& (_client->property(PROP_AUTHENTICATED).toBool() == false)
&& (authNotRequired.find(_requestType) == authNotRequired.end()))
&& (!authNotRequired.contains(_requestType))
&& (_connProperties.value(PROP_AUTHENTICATED).toBool() == false))
{
SendErrorResponse("Not Authenticated");
return;
return _response;
}
void (*handlerFunc)(WSRequestHandler*) = (messageMap[_requestType]);
@ -178,6 +175,8 @@ void WSRequestHandler::processIncomingMessage(QString textMessage) {
handlerFunc(this);
else
SendErrorResponse("invalid request type");
return _response;
}
WSRequestHandler::~WSRequestHandler() {
@ -215,11 +214,10 @@ void WSRequestHandler::SendErrorResponse(obs_data_t* additionalFields) {
}
void WSRequestHandler::SendResponse(obs_data_t* response) {
QString json = obs_data_get_json(response);
_client->sendTextMessage(json);
_response = obs_data_get_json(response);
if (Config::Current()->DebugEnabled)
blog(LOG_DEBUG, "Response << '%s'", json.toUtf8().constData());
blog(LOG_DEBUG, "Response << '%s'", _response.c_str());
}
bool WSRequestHandler::hasField(QString name) {

View File

@ -22,8 +22,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QHash>
#include <QSet>
#include <QWebSocket>
#include <QWebSocketServer>
#include <QVariantHash>
#include <obs.hpp>
#include <obs-frontend-api.h>
@ -34,15 +33,16 @@ class WSRequestHandler : public QObject {
Q_OBJECT
public:
explicit WSRequestHandler(QWebSocket* client);
explicit WSRequestHandler(QVariantHash& connProperties);
~WSRequestHandler();
void processIncomingMessage(QString textMessage);
std::string processIncomingMessage(std::string& textMessage);
bool hasField(QString name);
private:
QWebSocket* _client;
const char* _messageId;
const char* _requestType;
std::string _response;
QVariantHash& _connProperties;
OBSDataAutoRelease data;
void SendOKResponse(obs_data_t* additionalFields = NULL);

View File

@ -85,20 +85,24 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
return;
}
if (req->_connProperties.value(PROP_AUTHENTICATED).toBool() == true) {
req->SendErrorResponse("already authenticated");
return;
}
QString auth = obs_data_get_string(req->data, "auth");
if (auth.isEmpty()) {
req->SendErrorResponse("auth not specified!");
return;
}
if ((req->_client->property(PROP_AUTHENTICATED).toBool() == false)
&& Config::Current()->CheckAuth(auth))
{
req->_client->setProperty(PROP_AUTHENTICATED, true);
req->SendOKResponse();
} else {
if (Config::Current()->CheckAuth(auth) == false) {
req->SendErrorResponse("Authentication Failed.");
return;
}
req->_connProperties.insert(PROP_AUTHENTICATED, true);
req->SendOKResponse();
}
/**
@ -117,12 +121,12 @@ void WSRequestHandler::HandleAuthenticate(WSRequestHandler* req) {
return;
}
WSEvents::Instance->HeartbeatIsActive =
obs_data_get_bool(req->data, "enable");
auto events = WSEvents::Current();
events->HeartbeatIsActive = obs_data_get_bool(req->data, "enable");
OBSDataAutoRelease response = obs_data_create();
obs_data_set_bool(response, "enable",
WSEvents::Instance->HeartbeatIsActive);
obs_data_set_bool(response, "enable", events->HeartbeatIsActive);
req->SendOKResponse(response);
}

View File

@ -21,6 +21,8 @@
* @since 0.3
*/
void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) {
auto events = WSEvents::Current();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
obs_data_set_bool(data, "recording", obs_frontend_recording_active());
@ -28,13 +30,13 @@
const char* tc = nullptr;
if (obs_frontend_streaming_active()) {
tc = WSEvents::Instance->GetStreamingTimecode();
tc = events->GetStreamingTimecode();
obs_data_set_string(data, "stream-timecode", tc);
bfree((void*)tc);
}
if (obs_frontend_recording_active()) {
tc = WSEvents::Instance->GetRecordingTimecode();
tc = events->GetRecordingTimecode();
obs_data_set_string(data, "rec-timecode", tc);
bfree((void*)tc);
}

View File

@ -16,11 +16,11 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
*/
#include <QtWebSockets/QWebSocket>
#include <QtCore/QThread>
#include <QtCore/QByteArray>
#include <QMainWindow>
#include <QMessageBox>
#include <QtConcurrent/QtConcurrent>
#include <obs-frontend-api.h>
#include "WSServer.h"
@ -30,139 +30,162 @@ with this program. If not, see <https://www.gnu.org/licenses/>
QT_USE_NAMESPACE
WSServer* WSServer::Instance = nullptr;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
WSServer::WSServer(QObject* parent)
: QObject(parent),
_wsServer(Q_NULLPTR),
_clients(),
WSServerPtr WSServer::_instance = WSServerPtr(nullptr);
WSServerPtr WSServer::Current()
{
if (!_instance) {
ResetCurrent();
}
return _instance;
}
void WSServer::ResetCurrent()
{
_instance = WSServerPtr(new WSServer());
}
WSServer::WSServer()
: QObject(nullptr),
_connections(),
_clMutex(QMutex::Recursive)
{
_wsServer = new QWebSocketServer(
QStringLiteral("obs-websocket"),
QWebSocketServer::NonSecureMode);
_server.init_asio();
_server.set_open_handler(bind(&WSServer::onOpen, this, ::_1));
_server.set_close_handler(bind(&WSServer::onClose, this, ::_1));
_server.set_message_handler(bind(&WSServer::onMessage, this, ::_1, ::_2));
}
WSServer::~WSServer() {
Stop();
WSServer::~WSServer()
{
stop();
}
void WSServer::Start(quint16 port) {
if (port == _wsServer->serverPort())
void WSServer::start(quint16 port)
{
if (_server.is_listening() && port == _serverPort) {
blog(LOG_INFO, "WebSocketsServer::start: server already on this port. no restart needed");
return;
if(_wsServer->isListening())
Stop();
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
if (serverStarted) {
blog(LOG_INFO, "server started successfully on TCP port %d", port);
connect(_wsServer, SIGNAL(newConnection()),
this, SLOT(onNewConnection()));
}
else {
QString errorString = _wsServer->errorString();
blog(LOG_ERROR,
"error: failed to start server on TCP port %d: %s",
port, errorString.toUtf8().constData());
QMainWindow* mainWindow = (QMainWindow*)obs_frontend_get_main_window();
obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.Server.StartFailed.Title");
QString msg = tr("OBSWebsocket.Server.StartFailed.Message").arg(port);
obs_frontend_pop_ui_translation();
QMessageBox::warning(mainWindow, title, msg);
if (_server.is_listening()) {
stop();
}
_serverPort = port;
_server.listen(_serverPort);
_server.start_accept();
QtConcurrent::run([=]() {
_server.run();
});
blog(LOG_INFO, "server started successfully on port %d", _serverPort);
}
void WSServer::Stop() {
QMutexLocker locker(&_clMutex);
for(QWebSocket* pClient : _clients) {
pClient->close();
void WSServer::stop()
{
if (!_server.is_listening()) {
return;
}
locker.unlock();
_wsServer->close();
_server.stop_listening();
_server.stop();
blog(LOG_INFO, "server stopped successfully");
}
void WSServer::broadcast(QString message) {
void WSServer::broadcast(std::string message)
{
QMutexLocker locker(&_clMutex);
for(QWebSocket* pClient : _clients) {
if (Config::Current()->AuthRequired
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) {
// Skip this client if unauthenticated
continue;
for (connection_hdl hdl : _connections) {
if (Config::Current()->AuthRequired) {
bool authenticated = _connectionProperties[hdl].value(PROP_AUTHENTICATED).toBool();
if (!authenticated) {
continue;
}
}
pClient->sendTextMessage(message);
_server.send(hdl, message, websocketpp::frame::opcode::text);
}
}
void WSServer::onNewConnection() {
QWebSocket* pSocket = _wsServer->nextPendingConnection();
if (pSocket) {
connect(pSocket, SIGNAL(textMessageReceived(const QString&)),
this, SLOT(onTextMessageReceived(QString)));
connect(pSocket, SIGNAL(disconnected()),
this, SLOT(onSocketDisconnected()));
void WSServer::onOpen(connection_hdl hdl)
{
QMutexLocker locker(&_clMutex);
_connections.insert(hdl);
locker.unlock();
pSocket->setProperty(PROP_AUTHENTICATED, false);
QMutexLocker locker(&_clMutex);
_clients << pSocket;
locker.unlock();
QHostAddress clientAddr = pSocket->peerAddress();
QString clientIp = Utils::FormatIPAddress(clientAddr);
blog(LOG_INFO, "new client connection from %s:%d",
clientIp.toUtf8().constData(), pSocket->peerPort());
obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.NotifyConnect.Title");
QString msg = tr("OBSWebsocket.NotifyConnect.Message")
.arg(Utils::FormatIPAddress(clientAddr));
obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
}
QString clientIp = getRemoteEndpoint(hdl);
notifyConnection(clientIp);
blog(LOG_INFO, "new client connection from %s", clientIp.toUtf8().constData());
}
void WSServer::onTextMessageReceived(QString message) {
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
if (pSocket) {
WSRequestHandler handler(pSocket);
handler.processIncomingMessage(message);
void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
{
auto opcode = message->get_opcode();
if (opcode != websocketpp::frame::opcode::text) {
return;
}
std::string payload = message->get_payload();
QMutexLocker locker(&_clMutex);
QVariantHash connProperties = _connectionProperties[hdl];
locker.unlock();
WSRequestHandler handler(connProperties);
std::string response = handler.processIncomingMessage(payload);
_server.send(hdl, response, websocketpp::frame::opcode::text);
locker.relock();
// In multithreaded processing this would be problematic to put back
// a copy of the connection properties, because there might conflicts
// between several simultaneous handlers.
// In our case, it's fine because all messages are processed in one thread.
_connectionProperties[hdl] = connProperties;
locker.unlock();
}
void WSServer::onSocketDisconnected() {
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
if (pSocket) {
pSocket->setProperty(PROP_AUTHENTICATED, false);
void WSServer::onClose(connection_hdl hdl)
{
QMutexLocker locker(&_clMutex);
_connections.erase(hdl);
_connectionProperties.erase(hdl);
locker.unlock();
QMutexLocker locker(&_clMutex);
_clients.removeAll(pSocket);
locker.unlock();
pSocket->deleteLater();
QHostAddress clientAddr = pSocket->peerAddress();
QString clientIp = Utils::FormatIPAddress(clientAddr);
blog(LOG_INFO, "client %s:%d disconnected",
clientIp.toUtf8().constData(), pSocket->peerPort());
obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message")
.arg(Utils::FormatIPAddress(clientAddr));
obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
}
QString clientIp = getRemoteEndpoint(hdl);
notifyDisconnection(clientIp);
blog(LOG_INFO, "client %s disconnected", clientIp.toUtf8().constData());
}
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
{
auto conn = _server.get_con_from_hdl(hdl);
return QString::fromStdString(conn->get_remote_endpoint());
}
void WSServer::notifyConnection(QString clientIp)
{
obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.NotifyConnect.Title");
QString msg = tr("OBSWebsocket.NotifyConnect.Message").arg(clientIp);
obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
}
void WSServer::notifyDisconnection(QString clientIp)
{
obs_frontend_push_ui_translation(obs_module_get_string);
QString title = tr("OBSWebsocket.NotifyDisconnect.Title");
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message").arg(clientIp);
obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title);
}

View File

@ -20,33 +20,58 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define WSSERVER_H
#include <QObject>
#include <QList>
#include <QMutex>
#include <QSharedPointer>
#include <QVariantHash>
#include <map>
#include <set>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include "WSRequestHandler.h"
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
class WSServer : public QObject {
Q_OBJECT
public:
explicit WSServer(QObject* parent = Q_NULLPTR);
virtual ~WSServer();
void Start(quint16 port);
void Stop();
void broadcast(QString message);
static WSServer* Instance;
using websocketpp::connection_hdl;
private slots:
void onNewConnection();
void onTextMessageReceived(QString message);
void onSocketDisconnected();
typedef websocketpp::server<websocketpp::config::asio> server;
private:
QWebSocketServer* _wsServer;
QList<QWebSocket*> _clients;
QMutex _clMutex;
class WSServer;
typedef QSharedPointer<WSServer> WSServerPtr;
class WSServer : public QObject
{
Q_OBJECT
public:
static WSServerPtr Current();
static void ResetCurrent();
explicit WSServer();
virtual ~WSServer();
void start(quint16 port);
void stop();
void broadcast(std::string message);
private:
static WSServerPtr _instance;
void onOpen(connection_hdl hdl);
void onMessage(connection_hdl hdl, server::message_ptr message);
void onClose(connection_hdl hdl);
QString getRemoteEndpoint(connection_hdl hdl);
void notifyConnection(QString clientIp);
void notifyDisconnection(QString clientIp);
server _server;
quint16 _serverPort;
std::set<connection_hdl, std::owner_less<connection_hdl>> _connections;
std::map<connection_hdl, QVariantHash, std::owner_less<connection_hdl>> _connectionProperties;
QMutex _clMutex;
};
#endif // WSSERVER_H

View File

@ -41,7 +41,7 @@ SettingsDialog::SettingsDialog(QWidget* parent) :
}
void SettingsDialog::showEvent(QShowEvent* event) {
Config* conf = Config::Current();
auto conf = Config::Current();
ui->serverEnabled->setChecked(conf->ServerEnabled);
ui->serverPort->setValue(conf->ServerPort);
@ -68,7 +68,7 @@ void SettingsDialog::AuthCheckboxChanged() {
}
void SettingsDialog::FormAccepted() {
Config* conf = Config::Current();
auto conf = Config::Current();
conf->ServerEnabled = ui->serverEnabled->isChecked();
conf->ServerPort = ui->serverPort->value();
@ -94,9 +94,9 @@ void SettingsDialog::FormAccepted() {
conf->Save();
if (conf->ServerEnabled)
WSServer::Instance->Start(conf->ServerPort);
WSServer::Current()->start(conf->ServerPort);
else
WSServer::Instance->Stop();
WSServer::Current()->stop();
}
SettingsDialog::~SettingsDialog() {

View File

@ -45,18 +45,19 @@ bool obs_module_load(void) {
QT_VERSION_STR, qVersion());
// Core setup
Config* config = Config::Current();
auto config = Config::Current();
config->Load();
WSServer::Instance = new WSServer();
WSEvents::Instance = new WSEvents(WSServer::Instance);
WSEvents::ResetCurrent(WSServer::Current());
if (config->ServerEnabled)
WSServer::Instance->Start(config->ServerPort);
if (config->ServerEnabled) {
WSServer::Current()->start(config->ServerPort);
}
// UI setup
QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
obs_module_text("OBSWebsocket.Menu.SettingsItem"));
obs_module_text("OBSWebsocket.Menu.SettingsItem")
);
obs_frontend_push_ui_translation(obs_module_get_string);
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
@ -75,6 +76,7 @@ bool obs_module_load(void) {
}
void obs_module_unload() {
WSServer::Current()->stop();
blog(LOG_INFO, "goodbye!");
}