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 mkdir -p build && cd build
cmake .. \ cmake .. \
-DQTDIR=/usr/local/opt/qt \ -DQTDIR=/usr/local/opt/qt \
-DBOOST_ROOT=/usr/local/opt/boost \
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \ -DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
-DLIBOBS_LIB=../../obs-studio/libobs \ -DLIBOBS_LIB=../../obs-studio/libobs \
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \ -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 \ checkinstall \
cmake \ cmake \
obs-studio \ obs-studio \
libqt5websockets5-dev qtbase5-dev \
libboost-all-dev
# Dirty hack # 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 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> <dict>
<key>CHILDREN</key> <key>CHILDREN</key>
<array> <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> <dict>
<key>CHILDREN</key> <key>CHILDREN</key>
<array> <array>

View File

@ -12,9 +12,6 @@ fi
echo "[obs-websocket] Preparing package build" 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 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 GIT_HASH=$(git rev-parse --short HEAD)
export VERSION="$GIT_HASH-$TRAVIS_BRANCH" export VERSION="$GIT_HASH-$TRAVIS_BRANCH"
@ -27,46 +24,17 @@ fi
export FILENAME="obs-websocket-$VERSION.pkg" export FILENAME="obs-websocket-$VERSION.pkg"
export LATEST_FILENAME="obs-websocket-latest-$LATEST_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" echo "[obs-websocket] Modifying obs-websocket.so"
install_name_tool \ 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/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/QtGui.framework/Versions/5/QtGui @rpath/QtGui \
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \ -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
./build/obs-websocket.so ./build/obs-websocket.so
# Check if replacement worked # 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" echo "[obs-websocket] Dependencies for obs-websocket"
otool -L ./build/obs-websocket.so otool -L ./build/obs-websocket.so
chmod -w ./build/QtWebSockets ./build/QtNetwork
echo "[obs-websocket] Actual package build" echo "[obs-websocket] Actual package build"
packagesbuild ./CI/macos/obs-websocket.pkgproj 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 \ --backup=no --deldoc=yes --install=no \
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \ --pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
--pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \ --pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \
--requires="libqt5websockets5" --pkggroup="video" \ --pkggroup="video" \
--pkgsource="https://github.com/Palakis/obs-websocket" \ --pkgsource="https://github.com/Palakis/obs-websocket" \
--pakdir="/package" --pakdir="/package"

View File

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

View File

@ -8,6 +8,7 @@ install:
- 7z x dependencies2015.zip -odependencies2015 - 7z x dependencies2015.zip -odependencies2015
- set DepsPath32=%CD%\dependencies2015\win32 - set DepsPath32=%CD%\dependencies2015\win32
- set DepsPath64=%CD%\dependencies2015\win64 - set DepsPath64=%CD%\dependencies2015\win64
- set BoostRoot=C:\Libraries\boost_1_67_0
- call C:\projects\obs-websocket\CI\install-setup-qt.cmd - call C:\projects\obs-websocket\CI\install-setup-qt.cmd
- set build_config=RelWithDebInfo - set build_config=RelWithDebInfo
- call C:\projects\obs-websocket\CI\install-build-obs.cmd - call C:\projects\obs-websocket\CI\install-build-obs.cmd
@ -15,9 +16,9 @@ install:
- mkdir build32 - mkdir build32
- mkdir build64 - mkdir build64
- cd ./build32 - 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 - 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: 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" - 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() #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() : Config::Config() :
ServerEnabled(true), ServerEnabled(true),
@ -179,8 +184,3 @@ bool Config::CheckAuth(QString response)
return authSuccess; 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 #define CONFIG_H
#include <QString> #include <QString>
#include <QSharedPointer>
class Config;
typedef QSharedPointer<Config> ConfigPtr;
class Config { class Config {
public: public:
static ConfigPtr Current();
Config(); Config();
~Config(); ~Config();
void Load(); void Load();
@ -46,10 +52,8 @@ class Config {
QString SessionChallenge; QString SessionChallenge;
bool SettingsLoaded; bool SettingsLoaded;
static Config* Current();
private: private:
static Config* _instance; static ConfigPtr _instance;
}; };
#endif // CONFIG_H #endif // CONFIG_H

View File

@ -376,15 +376,6 @@ void Utils::SysTrayNotify(QString &text,
trayIcon->showMessage(title, text, icon); 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() { const char* Utils::GetRecordingFolder() {
config_t* profile = obs_frontend_get_profile_config(); config_t* profile = obs_frontend_get_profile_config();
QString outputMode = config_get_string(profile, "Output", "Mode"); 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 <QLayout>
#include <QListWidget> #include <QListWidget>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QHostAddress>
#include <obs.hpp> #include <obs.hpp>
#include <obs-module.h> #include <obs-module.h>
@ -70,8 +69,6 @@ class Utils {
QSystemTrayIcon::MessageIcon n, QSystemTrayIcon::MessageIcon n,
QString title = QString("obs-websocket")); QString title = QString("obs-websocket"));
static QString FormatIPAddress(QHostAddress &addr);
static const char* GetRecordingFolder(); static const char* GetRecordingFolder();
static bool SetRecordingFolder(const char* path); 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; 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; _srv = srv;
obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this); obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this);
@ -214,7 +222,7 @@ void WSEvents::broadcastUpdate(const char* updateType,
obs_data_apply(update, additionalFields); obs_data_apply(update, additionalFields);
QString json = obs_data_get_json(update); QString json = obs_data_get_json(update);
_srv->broadcast(json); _srv->broadcast(json.toStdString());
if (Config::Current()->DebugEnabled) if (Config::Current()->DebugEnabled)
blog(LOG_DEBUG, "Update << '%s'", json.toUtf8().constData()); 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.hpp>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QSharedPointer>
#include "WSServer.h" #include "WSServer.h"
class WSEvents : public QObject { class WSEvents;
Q_OBJECT typedef QSharedPointer<WSEvents> WSEventsPtr;
public:
explicit WSEvents(WSServer* srv); class WSEvents : public QObject
{
Q_OBJECT
public:
static WSEventsPtr Current();
static void ResetCurrent(WSServerPtr srv);
explicit WSEvents(WSServerPtr srv);
~WSEvents(); ~WSEvents();
static void FrontendEventHandler( static void FrontendEventHandler(
enum obs_frontend_event event, void* privateData); enum obs_frontend_event event, void* privateData);
static WSEvents* Instance;
void connectSceneSignals(obs_source_t* scene); void connectSceneSignals(obs_source_t* scene);
void hookTransitionBeginEvent(); void hookTransitionBeginEvent();
@ -44,14 +54,16 @@ class WSEvents : public QObject {
bool HeartbeatIsActive; bool HeartbeatIsActive;
private slots: private slots:
void deferredInitOperations(); void deferredInitOperations();
void StreamStatus(); void StreamStatus();
void Heartbeat(); void Heartbeat();
void TransitionDurationChanged(int ms); void TransitionDurationChanged(int ms);
private: private:
WSServer* _srv; static WSEventsPtr _instance;
WSServerPtr _srv;
OBSSource currentScene; OBSSource currentScene;
bool pulse; bool pulse;

View File

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

View File

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

View File

@ -21,6 +21,8 @@
* @since 0.3 * @since 0.3
*/ */
void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) { void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req) {
auto events = WSEvents::Current();
OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "streaming", obs_frontend_streaming_active()); obs_data_set_bool(data, "streaming", obs_frontend_streaming_active());
obs_data_set_bool(data, "recording", obs_frontend_recording_active()); obs_data_set_bool(data, "recording", obs_frontend_recording_active());
@ -28,13 +30,13 @@
const char* tc = nullptr; const char* tc = nullptr;
if (obs_frontend_streaming_active()) { if (obs_frontend_streaming_active()) {
tc = WSEvents::Instance->GetStreamingTimecode(); tc = events->GetStreamingTimecode();
obs_data_set_string(data, "stream-timecode", tc); obs_data_set_string(data, "stream-timecode", tc);
bfree((void*)tc); bfree((void*)tc);
} }
if (obs_frontend_recording_active()) { if (obs_frontend_recording_active()) {
tc = WSEvents::Instance->GetRecordingTimecode(); tc = events->GetRecordingTimecode();
obs_data_set_string(data, "rec-timecode", tc); obs_data_set_string(data, "rec-timecode", tc);
bfree((void*)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/> with this program. If not, see <https://www.gnu.org/licenses/>
*/ */
#include <QtWebSockets/QWebSocket>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QMainWindow> #include <QMainWindow>
#include <QMessageBox> #include <QMessageBox>
#include <QtConcurrent/QtConcurrent>
#include <obs-frontend-api.h> #include <obs-frontend-api.h>
#include "WSServer.h" #include "WSServer.h"
@ -30,139 +30,162 @@ with this program. If not, see <https://www.gnu.org/licenses/>
QT_USE_NAMESPACE 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) WSServerPtr WSServer::_instance = WSServerPtr(nullptr);
: QObject(parent),
_wsServer(Q_NULLPTR), WSServerPtr WSServer::Current()
_clients(), {
if (!_instance) {
ResetCurrent();
}
return _instance;
}
void WSServer::ResetCurrent()
{
_instance = WSServerPtr(new WSServer());
}
WSServer::WSServer()
: QObject(nullptr),
_connections(),
_clMutex(QMutex::Recursive) _clMutex(QMutex::Recursive)
{ {
_wsServer = new QWebSocketServer( _server.init_asio();
QStringLiteral("obs-websocket"),
QWebSocketServer::NonSecureMode); _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() { WSServer::~WSServer()
Stop(); {
stop();
} }
void WSServer::Start(quint16 port) { void WSServer::start(quint16 port)
if (port == _wsServer->serverPort()) {
if (_server.is_listening() && port == _serverPort) {
blog(LOG_INFO, "WebSocketsServer::start: server already on this port. no restart needed");
return; 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(); if (_server.is_listening()) {
stop();
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);
} }
_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() { void WSServer::stop()
QMutexLocker locker(&_clMutex); {
for(QWebSocket* pClient : _clients) { if (!_server.is_listening()) {
pClient->close(); return;
} }
locker.unlock();
_wsServer->close();
_server.stop_listening();
_server.stop();
blog(LOG_INFO, "server stopped successfully"); blog(LOG_INFO, "server stopped successfully");
} }
void WSServer::broadcast(QString message) { void WSServer::broadcast(std::string message)
{
QMutexLocker locker(&_clMutex); QMutexLocker locker(&_clMutex);
for(QWebSocket* pClient : _clients) { for (connection_hdl hdl : _connections) {
if (Config::Current()->AuthRequired if (Config::Current()->AuthRequired) {
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) { bool authenticated = _connectionProperties[hdl].value(PROP_AUTHENTICATED).toBool();
// Skip this client if unauthenticated if (!authenticated) {
continue; continue;
}
} }
pClient->sendTextMessage(message); _server.send(hdl, message, websocketpp::frame::opcode::text);
} }
} }
void WSServer::onNewConnection() { void WSServer::onOpen(connection_hdl hdl)
QWebSocket* pSocket = _wsServer->nextPendingConnection(); {
if (pSocket) { QMutexLocker locker(&_clMutex);
connect(pSocket, SIGNAL(textMessageReceived(const QString&)), _connections.insert(hdl);
this, SLOT(onTextMessageReceived(QString))); locker.unlock();
connect(pSocket, SIGNAL(disconnected()),
this, SLOT(onSocketDisconnected()));
pSocket->setProperty(PROP_AUTHENTICATED, false); QString clientIp = getRemoteEndpoint(hdl);
notifyConnection(clientIp);
QMutexLocker locker(&_clMutex); blog(LOG_INFO, "new client connection from %s", clientIp.toUtf8().constData());
_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);
}
} }
void WSServer::onTextMessageReceived(QString message) { void WSServer::onMessage(connection_hdl hdl, server::message_ptr message)
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender()); {
if (pSocket) { auto opcode = message->get_opcode();
WSRequestHandler handler(pSocket); if (opcode != websocketpp::frame::opcode::text) {
handler.processIncomingMessage(message); 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() { void WSServer::onClose(connection_hdl hdl)
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender()); {
if (pSocket) { QMutexLocker locker(&_clMutex);
pSocket->setProperty(PROP_AUTHENTICATED, false); _connections.erase(hdl);
_connectionProperties.erase(hdl);
locker.unlock();
QMutexLocker locker(&_clMutex); QString clientIp = getRemoteEndpoint(hdl);
_clients.removeAll(pSocket); notifyDisconnection(clientIp);
locker.unlock(); blog(LOG_INFO, "client %s disconnected", clientIp.toUtf8().constData());
}
pSocket->deleteLater();
QString WSServer::getRemoteEndpoint(connection_hdl hdl)
QHostAddress clientAddr = pSocket->peerAddress(); {
QString clientIp = Utils::FormatIPAddress(clientAddr); auto conn = _server.get_con_from_hdl(hdl);
return QString::fromStdString(conn->get_remote_endpoint());
blog(LOG_INFO, "client %s:%d disconnected", }
clientIp.toUtf8().constData(), pSocket->peerPort());
void WSServer::notifyConnection(QString clientIp)
obs_frontend_push_ui_translation(obs_module_get_string); {
QString title = tr("OBSWebsocket.NotifyDisconnect.Title"); obs_frontend_push_ui_translation(obs_module_get_string);
QString msg = tr("OBSWebsocket.NotifyDisconnect.Message") QString title = tr("OBSWebsocket.NotifyConnect.Title");
.arg(Utils::FormatIPAddress(clientAddr)); QString msg = tr("OBSWebsocket.NotifyConnect.Message").arg(clientIp);
obs_frontend_pop_ui_translation(); obs_frontend_pop_ui_translation();
Utils::SysTrayNotify(msg, QSystemTrayIcon::Information, title); 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 #define WSSERVER_H
#include <QObject> #include <QObject>
#include <QList>
#include <QMutex> #include <QMutex>
#include <QSharedPointer>
#include <QVariantHash>
#include <map>
#include <set>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include "WSRequestHandler.h" #include "WSRequestHandler.h"
QT_FORWARD_DECLARE_CLASS(QWebSocketServer) QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket) QT_FORWARD_DECLARE_CLASS(QWebSocket)
class WSServer : public QObject { using websocketpp::connection_hdl;
Q_OBJECT
public:
explicit WSServer(QObject* parent = Q_NULLPTR);
virtual ~WSServer();
void Start(quint16 port);
void Stop();
void broadcast(QString message);
static WSServer* Instance;
private slots: typedef websocketpp::server<websocketpp::config::asio> server;
void onNewConnection();
void onTextMessageReceived(QString message);
void onSocketDisconnected();
private: class WSServer;
QWebSocketServer* _wsServer; typedef QSharedPointer<WSServer> WSServerPtr;
QList<QWebSocket*> _clients;
QMutex _clMutex; 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 #endif // WSSERVER_H

View File

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

View File

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