commit 61be350befe5599352fbcbe9b64f909708502fb6 Author: Stéphane Lepin Date: Mon Oct 10 00:33:56 2016 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0233ee15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build/ +/build32/ +/build64/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..0bbb1f6e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.5) +project(obs-websocket) + +set(CMAKE_PREFIX_PATH "${QTDIR}") +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) + +include(external/FindLibObs.cmake) +find_package(LibObs REQUIRED) +find_package(Qt5Core REQUIRED) +find_package(Qt5WebSockets REQUIRED) + +set(obs-websocket_SOURCES + obs-websocket.cpp + WSServer.cpp) + +set(obs-websocket_HEADERS + WSServer.h) + +add_library(obs-websocket MODULE + ${obs-websocket_SOURCES} + ${obs-websocket_HEADERS}) +qt5_use_modules(obs-websocket + Core WebSockets) +include_directories( + "${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api" + ${Qt5Core_INCLUDES} + ${Qt5WebSockets_INCLUDES}) +target_link_libraries(obs-websocket + libobs + ${OBS_FRONTEND_LIB} + Qt5::Core + Qt5::WebSockets) \ No newline at end of file diff --git a/WSServer.cpp b/WSServer.cpp new file mode 100644 index 00000000..9893405e --- /dev/null +++ b/WSServer.cpp @@ -0,0 +1,92 @@ +#include "WSServer.h" +#include +#include +#include +#include + +QT_USE_NAMESPACE + +WSServer::WSServer(quint16 port, QObject *parent) : + QObject(parent), + _wsServer(Q_NULLPTR), + _clients() +{ + _wsServer = new QWebSocketServer( + QStringLiteral("OBS Websocket API"), + QWebSocketServer::NonSecureMode, + this); + + bool serverStarted = _wsServer->listen(QHostAddress::Any, port); + if (serverStarted) { + connect(_wsServer, &QWebSocketServer::newConnection, this, &WSServer::onNewConnection); + } +} + +WSServer::~WSServer() +{ + _wsServer->close(); + qDeleteAll(_clients.begin(), _clients.end()); +} + +void WSServer::broadcast(QString message) +{ + Q_FOREACH(QWebSocket *pClient, _clients) { + pClient->sendTextMessage(message); + } +} + +void WSServer::onNewConnection() +{ + QWebSocket *pSocket = _wsServer->nextPendingConnection(); + + blog(LOG_INFO, "[obs-websockets] new client connected from %s:%d", pSocket->peerAddress().toString().toStdString(), pSocket->peerPort()); + + connect(pSocket, &QWebSocket::textMessageReceived, this, &WSServer::processTextMessage); + connect(pSocket, &QWebSocket::disconnected, this, &WSServer::socketDisconnected); + + _clients << pSocket; +} + +void WSServer::processTextMessage(QString textMessage) { + QWebSocket *pSender = qobject_cast(sender()); + if (pSender) { + const char *msg = textMessage.toLocal8Bit(); + blog(LOG_INFO, "[obs-websockets] new message : %s", msg); + + obs_data_t *request = obs_data_create_from_json(msg); + if (!request) { + blog(LOG_ERROR, "[obs-websockets] invalid JSON payload for '%s'", msg); + } + + const char *requestType = obs_data_get_string(request, "request"); + if (strcmp(requestType, "scene_change") == 0) { + const char *sceneName = obs_data_get_string(request, "switch_to"); + + blog(LOG_INFO, "[obs-websockets] processing scene change request to %s", sceneName); + + obs_source_t *source = obs_get_source_by_name(sceneName); + + if (source) { + obs_frontend_set_current_scene(source); + } + else { + blog(LOG_ERROR, "[obs-websockets] requested scene '%s' doesn't exist !", sceneName); + } + + } + + obs_data_release(request); + } +} + +void WSServer::socketDisconnected() +{ + QWebSocket *pClient = qobject_cast(sender()); + + blog(LOG_INFO, "[obs-websockets] client %s:%d disconnected", pClient->peerAddress().toString().toStdString(), pClient->peerPort()); + + if (pClient) { + _clients.removeAll(pClient); + pClient->deleteLater(); + } +} \ No newline at end of file diff --git a/WSServer.h b/WSServer.h new file mode 100644 index 00000000..211ac25b --- /dev/null +++ b/WSServer.h @@ -0,0 +1,30 @@ +#ifndef WSSERVER_H +#define WSSERVER_H + +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QWebSocketServer) +QT_FORWARD_DECLARE_CLASS(QWebSocket) + +class WSServer : public QObject +{ + Q_OBJECT + + public: + explicit WSServer(quint16 port, QObject *parent = Q_NULLPTR); + virtual ~WSServer(); + void broadcast(QString message); + + private Q_SLOTS: + void onNewConnection(); + void processTextMessage(QString textMessage); + void socketDisconnected(); + + private: + QWebSocketServer *_wsServer; + QList _clients; +}; + +#endif // WSSERVER_H \ No newline at end of file diff --git a/external/FindLibObs.cmake b/external/FindLibObs.cmake new file mode 100644 index 00000000..ab0a3dea --- /dev/null +++ b/external/FindLibObs.cmake @@ -0,0 +1,107 @@ +# This module can be copied and used by external plugins for OBS +# +# Once done these will be defined: +# +# LIBOBS_FOUND +# LIBOBS_INCLUDE_DIRS +# LIBOBS_LIBRARIES + +find_package(PkgConfig QUIET) +if (PKG_CONFIG_FOUND) + pkg_check_modules(_OBS QUIET obs libobs) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_lib_suffix 64) +else() + set(_lib_suffix 32) +endif() + +if(DEFINED CMAKE_BUILD_TYPE) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(_build_type_base "debug") + else() + set(_build_type_base "release") + endif() +endif() + +find_path(LIBOBS_INCLUDE_DIR + NAMES obs.h + HINTS + ENV obsPath${_lib_suffix} + ENV obsPath + ${obsPath} + PATHS + /usr/include /usr/local/include /opt/local/include /sw/include + PATH_SUFFIXES + libobs + ) + +function(find_obs_lib base_name repo_build_path lib_name) + string(TOUPPER "${base_name}" base_name_u) + + if(DEFINED _build_type_base) + set(_build_type_${repo_build_path} "${_build_type_base}/${repo_build_path}") + set(_build_type_${repo_build_path}${_lib_suffix} "${_build_type_base}${_lib_suffix}/${repo_build_path}") + endif() + + find_library(${base_name_u}_LIB + NAMES ${_${base_name_u}_LIBRARIES} ${lib_name} lib${lib_name} + HINTS + ENV obsPath${_lib_suffix} + ENV obsPath + ${obsPath} + ${_${base_name_u}_LIBRARY_DIRS} + PATHS + /usr/lib /usr/local/lib /opt/local/lib /sw/lib + PATH_SUFFIXES + lib${_lib_suffix} lib + libs${_lib_suffix} libs + bin${_lib_suffix} bin + ../lib${_lib_suffix} ../lib + ../libs${_lib_suffix} ../libs + ../bin${_lib_suffix} ../bin + # base repo non-msvc-specific search paths + ${_build_type_${repo_build_path}} + ${_build_type_${repo_build_path}${_lib_suffix}} + build/${repo_build_path} + build${_lib_suffix}/${repo_build_path} + # base repo msvc-specific search paths on windows + build${_lib_suffix}/${repo_build_path}/Debug + build${_lib_suffix}/${repo_build_path}/RelWithDebInfo + build/${repo_build_path}/Debug + build/${repo_build_path}/RelWithDebInfo + ) +endfunction() + +find_obs_lib(LIBOBS libobs obs) + +if(MSVC) + find_obs_lib(W32_PTHREADS deps/w32-pthreads w32-pthreads) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libobs DEFAULT_MSG LIBOBS_LIB LIBOBS_INCLUDE_DIR) +mark_as_advanced(LIBOBS_INCLUDE_DIR LIBOBS_LIB) + +if(LIBOBS_FOUND) + if(MSVC) + if (NOT DEFINED W32_PTHREADS_LIB) + message(FATAL_ERROR "Could not find the w32-pthreads library" ) + endif() + + set(W32_PTHREADS_INCLUDE_DIR ${LIBOBS_INCLUDE_DIR}/../deps/w32-pthreads) + endif() + + set(LIBOBS_INCLUDE_DIRS ${LIBOBS_INCLUDE_DIR} ${W32_PTHREADS_INCLUDE_DIR}) + set(LIBOBS_LIBRARIES ${LIBOBS_LIB} ${W32_PTHREADS_LIB}) + include(${LIBOBS_INCLUDE_DIR}/../cmake/external/ObsPluginHelpers.cmake) + + # allows external plugins to easily use/share common dependencies that are often included with libobs (such as FFmpeg) + if(NOT DEFINED INCLUDED_LIBOBS_CMAKE_MODULES) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${LIBOBS_INCLUDE_DIR}/../cmake/Modules/") + set(INCLUDED_LIBOBS_CMAKE_MODULES true) + endif() +else() + message(FATAL_ERROR "Could not find the libobs library" ) +endif() diff --git a/obs-websocket.cpp b/obs-websocket.cpp new file mode 100644 index 00000000..95d27730 --- /dev/null +++ b/obs-websocket.cpp @@ -0,0 +1,66 @@ +#include +#include +#include "WSServer.h" + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") + +WSServer *server; + +void obs_frontend_callback(enum obs_frontend_event event, void *) +{ + bool sendMessage = false; + obs_data_t *announce = obs_data_create(); + + if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) { + obs_source_t *source = obs_frontend_get_current_scene(); + const char *name = obs_source_get_name(source); + + obs_data_set_string(announce, "type", "scene_changed"); + obs_data_set_string(announce, "name", name); + sendMessage = true; + } + else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTED) { + obs_data_set_string(announce, "type", "streaming_started"); + sendMessage = true; + } + else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPED) { + obs_data_set_string(announce, "type", "streaming_stopped"); + sendMessage = true; + } + else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) { + obs_data_set_string(announce, "type", "recording_started"); + sendMessage = true; + } + else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED) { + obs_data_set_string(announce, "type", "recording_stopped"); + sendMessage = true; + } + else if (event == OBS_FRONTEND_EVENT_EXIT) { + obs_data_set_string(announce, "type", "exiting"); + sendMessage = true; + } + + if (sendMessage && server) { + const char *message = obs_data_get_json(announce); + server->broadcast(message); + } + + obs_data_release(announce); +} + +bool obs_module_load(void) +{ + blog(LOG_INFO, "[obs-websockets] you can haz websockets"); + + server = new WSServer(8080); + obs_frontend_add_event_callback(obs_frontend_callback, nullptr); + + return true; +} + +void obs_module_unload() +{ + +} +