mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
cfb490b7e3 | |||
1947a3cfd5 | |||
fca6774d4a | |||
5fd1e978ec | |||
4985c549dd | |||
e6c378aef0 | |||
0657835ba3 | |||
36934de04a | |||
e735ab80b1 | |||
9904ca89d4 | |||
554ab54690 | |||
5abcd18ba0 | |||
03bf41b250 | |||
0df6bde5cb | |||
e3a28b640f | |||
94576fae44 | |||
d091d9c3e4 | |||
01c9e48f4f | |||
5639c18c2f | |||
25b835f4d8 | |||
78fb5b3093 | |||
73b85ef9df | |||
93c604a639 | |||
053676a320 | |||
d1a142cefd | |||
36cf2a4126 | |||
a3bc9ae8d0 | |||
6d77ae24b3 | |||
d9bbed5f69 | |||
d6ce405e71 |
@ -15,11 +15,6 @@ find_package(Qt5Widgets REQUIRED)
|
|||||||
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
|
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
|
||||||
set(ENABLED_PROGRAMS false)
|
set(ENABLED_PROGRAMS false)
|
||||||
|
|
||||||
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
|
||||||
if(OBS_FRONTEND_LIB EQUAL "OBS_FRONTEND_LIB-NOTFOUND")
|
|
||||||
message(FATAL_ERROR "Could not find OBS Frontend API\'s library !")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(obs-websocket_SOURCES
|
set(obs-websocket_SOURCES
|
||||||
obs-websocket.cpp
|
obs-websocket.cpp
|
||||||
WSServer.cpp
|
WSServer.cpp
|
||||||
@ -51,8 +46,34 @@ include_directories(
|
|||||||
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
|
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
|
||||||
target_link_libraries(obs-websocket
|
target_link_libraries(obs-websocket
|
||||||
libobs
|
libobs
|
||||||
${OBS_FRONTEND_LIB}
|
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::WebSockets
|
Qt5::WebSockets
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
mbedcrypto)
|
mbedcrypto)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
||||||
|
if(OBS_FRONTEND_LIB EQUAL "OBS_FRONTEND_LIB-NOTFOUND")
|
||||||
|
message(FATAL_ERROR "Could not find OBS Frontend API\'s library !")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(obs-websocket
|
||||||
|
"${OBS_FRONTEND_LIB}")
|
||||||
|
add_custom_command(TARGET obs-websocket POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"${QTDIR}/bin/Qt5WebSockets.dll"
|
||||||
|
"${QTDIR}/bin/Qt5Network.dll"
|
||||||
|
"${CMAKE_BINARY_DIR}/$<CONFIG>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
target_compile_options(mbedcrypto PRIVATE -fPIC)
|
||||||
|
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
||||||
|
target_link_libraries(obs-websocket
|
||||||
|
obs-frontend-api)
|
||||||
|
|
||||||
|
install(TARGETS obs-websocket
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/obs-plugins)
|
||||||
|
install(FILES data/locale/en-US.ini data/locale/fr-FR.ini
|
||||||
|
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale")
|
||||||
|
endif()
|
||||||
|
217
PROTOCOL.md
217
PROTOCOL.md
@ -1,70 +1,75 @@
|
|||||||
obs-websocket protocol reference
|
obs-websocket protocol reference
|
||||||
================================
|
================================
|
||||||
|
|
||||||
## General introduction
|
## General Introduction
|
||||||
**This document is still a WIP. Some things are missing (but won't stay like this for long).**
|
|
||||||
|
|
||||||
Messages exchanged between the client and the server are JSON objects.
|
Messages exchanged between the client and the server are JSON objects.
|
||||||
The protocol in general in based on the OBS Remote protoctol created by Bill Hamilton, with new commands specific to OBS Studio.
|
The protocol in general is based on the OBS Remote protocol created by Bill Hamilton, with new commands specific to OBS Studio.
|
||||||
|
|
||||||
|
### Table of contents
|
||||||
|
- [Events](#events)
|
||||||
|
- [Requests](#requests)
|
||||||
|
- [Authentication](#authentication)
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
Events are sent exclusively by the server and broadcasted to every client connected to the server.
|
Events are sent exclusively by the server and broadcast to each connected client.
|
||||||
An event message has at least one field :
|
An event message will contain at least one field :
|
||||||
- **update-type** (string) : the type of event
|
- **update-type** (string) : the type of event
|
||||||
Additional fields will be present in the event message if the event type requires it.
|
|
||||||
|
|
||||||
### Event types
|
Additional fields will be present in the event message depending on the event type.
|
||||||
|
|
||||||
|
### Event Types
|
||||||
#### "SwitchScenes"
|
#### "SwitchScenes"
|
||||||
OBS is switching to another scene.
|
OBS is switching to another scene.
|
||||||
Additional fields :
|
- **scene-name** (string) : The name of the scene being switched to.
|
||||||
- **scene-name** : the name of the scene being switched to
|
|
||||||
|
|
||||||
#### "ScenesChanged"
|
#### "ScenesChanged"
|
||||||
The scene list has been modified (scenes added, removed or moved).
|
The scene list has been modified (Scenes have been added, removed, or renamed).
|
||||||
|
|
||||||
#### "StreamStarting"
|
#### "StreamStarting"
|
||||||
Streaming is starting but isn't completely started yet.
|
A request to start streaming has been issued.
|
||||||
|
- **preview-only** (bool) : Always false.
|
||||||
|
|
||||||
#### "StreamStarted"
|
#### "StreamStarted"
|
||||||
|
Streaming started successfully.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
Streaming has been started successfully.
|
|
||||||
|
|
||||||
#### "StreamStopping"
|
#### "StreamStopping"
|
||||||
Streaming is stopping but isn't completely stopped yet.
|
A request to stop streaming has been issued.
|
||||||
|
- **preview-only** (bool) : Always false.
|
||||||
|
|
||||||
#### "StreamStopped"
|
#### "StreamStopped"
|
||||||
|
Streaming stopped successfully.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
Streaming has been stopped successfully.
|
|
||||||
|
|
||||||
#### "RecordingStarting"
|
#### "RecordingStarting"
|
||||||
|
A request to start recording has been issued.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
Recording is starting but isn't completely started yet.
|
|
||||||
|
|
||||||
#### "RecordingStarted"
|
#### "RecordingStarted"
|
||||||
|
Recording started successfully.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
Recording has been started successfully.
|
|
||||||
|
|
||||||
#### "RecordingStopping"
|
#### "RecordingStopping"
|
||||||
|
A request to stop streaming has been issued.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
Recording is stopping but isn't completely stopped yet.
|
|
||||||
|
|
||||||
#### "RecordingStopped"
|
#### "RecordingStopped"
|
||||||
|
Recording stopped successfully.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
Recording has been stopped successfully.
|
|
||||||
|
|
||||||
#### "StreamStatus"
|
#### "StreamStatus"
|
||||||
Sent every second with those additional fields :
|
Sent every 2 seconds with the following information :
|
||||||
- **streaming** (bool) : streaming state
|
- **streaming** (bool) : Current Streaming state.
|
||||||
- **recording** (bool) : recording state
|
- **recording** (bool) : Current Recording state.
|
||||||
- **preview-only** (bool) : always false.
|
- **preview-only** (bool) : Always false.
|
||||||
- **bytes-per-sec** (integer) : during streaming, amount of data (in bytes) transmitted by the stream encoder
|
- **bytes-per-sec** (integer) : Amount of data per second (in bytes) transmitted by the stream encoder.
|
||||||
- **strain** (double) : i have no idea what this is
|
- **kbits-per-sec** (integer) : "bytes-per-sec" converted to kilobits per second
|
||||||
- **total-stream-time** (integer) : total time since the beginning of streaming
|
- **strain** (double) : Percentage of dropped frames
|
||||||
- **num-total-frames** (integer) : total number of frames transmitted since the beginning
|
- **total-stream-time** (integer) : Total time (in seconds) since the stream started.
|
||||||
- **num-dropped-frames** (integer) : number of frames dropped by the encoder for the current streaming session
|
- **num-total-frames** (integer) : Total number of frames transmitted since the stream started.
|
||||||
- **fps** (double) : current framerate
|
- **num-dropped-frames** (integer) : Number of frames dropped by the encoder since the stream started.
|
||||||
|
- **fps** (double) : Current framerate.
|
||||||
|
|
||||||
#### "Exiting"
|
#### "Exiting"
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
@ -73,32 +78,164 @@ OBS is exiting.
|
|||||||
## Requests
|
## Requests
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
Requests are sent by the client and have at least two fields :
|
Requests are sent by the client and must have at least the following two fields :
|
||||||
- **"request-type"** (string) : one of the request types listed in the sub-section "Requests".
|
- **"request-type"** (string) : One of the request types listed in the sub-section "[Requests Types](#request-types)".
|
||||||
- **"message-id"** (string) : an string defined by the client that will be embedded in the response from the server.
|
- **"message-id"** (string) : An identifier defined by the client which will be embedded in the server response.
|
||||||
Depending on the request type, additional fields are needed in the request message (see the "Request types" section below for more informations).
|
|
||||||
|
|
||||||
Once a request is sent, the server processes it and sends a JSON response to the client with the following fields in it :
|
Depending on the request type additional fields may be required (see the "[Request Types](#request-types)" section below for more information).
|
||||||
- **"message-id"** (string) : the custom string you specified in the request.
|
|
||||||
- **"status"** (string) : two possible values : "ok" or "error".
|
|
||||||
- **"error"** (string) : the error message associated with an error reponse (when "status" equals "error").
|
|
||||||
Additional fields can be sent in the response if a request type requires it.
|
|
||||||
|
|
||||||
### Request types
|
Once a request is sent, the server will return a JSON response with the following fields :
|
||||||
|
- **"message-id"** (string) : The identifier specified in the request.
|
||||||
|
- **"status"** (string) : Response status, will be one of the following : "ok", "error"
|
||||||
|
- **"error"** (string) : The error message associated with an "error" status.
|
||||||
|
|
||||||
|
Depending on the request type additional fields may be present (see the "[Request Types](#request-types)" section below for more information).
|
||||||
|
|
||||||
|
### Request Types
|
||||||
#### "GetVersion"
|
#### "GetVersion"
|
||||||
|
Returns the latest version of the plugin and the API.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK, with these additional fields :
|
||||||
|
- **"version"** (double) : OBSRemote API version. Fixed to 1.1 for retrocompatibility.
|
||||||
|
- **"obs-websocket-version"** (string) : obs-websocket version
|
||||||
|
|
||||||
#### "GetAuthRequired"
|
#### "GetAuthRequired"
|
||||||
|
Tells the client if authentication is required. If it is, authentication parameters "challenge" and "salt" are passed in the response fields (see "Authentication").
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK, with these additional fields :
|
||||||
|
- **"authRequired"** (bool)
|
||||||
|
- **"challenge"** (string)
|
||||||
|
- **"salt"** (string)
|
||||||
|
|
||||||
#### "Authenticate"
|
#### "Authenticate"
|
||||||
|
Try to authenticate the client on the server.
|
||||||
|
|
||||||
|
__Request fields__ :
|
||||||
|
- **"auth"** (string) : response to the auth challenge (see "Authentication").
|
||||||
|
|
||||||
|
__Response__ : OK if auth succeeded, error if invalid credentials. No additional fields.
|
||||||
|
|
||||||
#### "GetCurrentScene"
|
#### "GetCurrentScene"
|
||||||
|
Get the current scene's name and items.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK, with these additional fields :
|
||||||
|
- **"name"** (string) : name of the current scene
|
||||||
|
- **"sources"** (array of objects) : ordered list of the current scene's items descriptions
|
||||||
|
|
||||||
|
Objects in the "sources" array have the following fields :
|
||||||
|
- **"name"** (string) : name of the source associated with the scene item
|
||||||
|
- **"type"** (string) : internal source type name
|
||||||
|
- **"volume"** (double) : audio volume of the source, ranging from 0.0 to 1.0
|
||||||
|
- **"x"** (double) : X coordinate of the top-left corner of the item in the scene
|
||||||
|
- **"y"** (double) : Y coordinate of the top-left corner of the item in the scene
|
||||||
|
- **"cx"** (double) : width of the item (with scale applied)
|
||||||
|
- **"cy"** (double) : height of the item (with scale applied)
|
||||||
|
|
||||||
#### "SetCurrentScene"
|
#### "SetCurrentScene"
|
||||||
|
Switch to the scene specified in "scene-name".
|
||||||
|
|
||||||
|
__Request fields__ :
|
||||||
|
- **"scene-name"** (string) : name of the scene to switch to.
|
||||||
|
|
||||||
|
__Response__ : always OK if scene exists, error if it doesn't. No additional fields
|
||||||
|
|
||||||
#### "GetSceneList"
|
#### "GetSceneList"
|
||||||
|
List OBS' scenes.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK, with these additional fields :
|
||||||
|
- **"current-scene"** (string) : name of the currently active scene
|
||||||
|
- **"scenes"** (array of objects) : ordered list of scene descriptions (see `GetCurrentScene` for reference)
|
||||||
|
|
||||||
#### "SetSourceRender"
|
#### "SetSourceRender"
|
||||||
|
Show or hide a specific source in the current scene.
|
||||||
|
|
||||||
|
__Request fields__ :
|
||||||
|
- **"source"** (string) : name of the source in the currently active scene.
|
||||||
|
- **"render"** (bool) : desired visibility
|
||||||
|
|
||||||
|
__Response__ : OK if source exists in the current scene, error otherwise.
|
||||||
|
|
||||||
#### "StartStopStreaming"
|
#### "StartStopStreaming"
|
||||||
|
Toggle streaming on or off.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK. No additional fields.
|
||||||
|
|
||||||
#### "StartStopRecording"
|
#### "StartStopRecording"
|
||||||
|
Toggle recording on or off.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK. No additional fields.
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
|
|
||||||
#### "GetStreamingStatus"
|
#### "GetStreamingStatus"
|
||||||
|
Get current streaming and recording status.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK, with these additional fields :
|
||||||
|
- **"streaming"** (bool) : streaming status (active or not)
|
||||||
|
- **"recording"** (bool) : recording status (active or not)
|
||||||
|
- **"preview-only"** (bool) : always false. Retrocompat with OBSRemote.
|
||||||
|
|
||||||
#### "GetTransitionList"
|
#### "GetTransitionList"
|
||||||
|
List all transitions available in the frontend's dropdown menu.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Response__ : always OK, with these additional fields :
|
||||||
|
- **"current-transition"** (string) : name of the current transition
|
||||||
|
- **"transitions"** (array of objects) : list of transition descriptions
|
||||||
|
|
||||||
|
Objects in the "transitions" array have only one field :
|
||||||
|
- **"name"** (string) : name of the transition
|
||||||
|
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
|
|
||||||
#### "GetCurrentTransition"
|
#### "GetCurrentTransition"
|
||||||
|
Get the name of the currently selected transition in the frontend's dropdown menu.
|
||||||
|
|
||||||
|
__Request fields__ : none
|
||||||
|
__Request__ : always OK, with these additional fields :
|
||||||
|
- **"name"** (string) : name of the selected transition
|
||||||
|
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
|
|
||||||
#### "SetCurrentTransition"
|
#### "SetCurrentTransition"
|
||||||
|
__Request fields__ :
|
||||||
|
- **"transition-name"** (string) : The name of the transition.
|
||||||
|
|
||||||
|
__Response__ : OK if specified transition exists, error otherwise.
|
||||||
|
|
||||||
*New in OBS Studio*
|
*New in OBS Studio*
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
A call to `GetAuthRequired` gives the client two elements :
|
||||||
|
- A challenge : a random string that will be used to generate the auth response
|
||||||
|
- A salt : applied to the password when generating the auth response
|
||||||
|
|
||||||
|
The client knows a password and must it to authenticate itself to the server.
|
||||||
|
However, it must keep this password secret, and it is the purpose of the authentication mecanism used by obs-websocket.
|
||||||
|
|
||||||
|
After a call to `GetAuthRequired`, the client knows a password (kept secret), a challenge and a salt (sent by the server).
|
||||||
|
To generate the answer to the auth challenge, follow this procedure :
|
||||||
|
- Concatenate the password with the salt sent by the server (in this order : password + server salt), then generate a binary SHA256 hash of the result and encode the resulting SHA256 binary hash to base64.
|
||||||
|
- Concatenate the base64 secret with the challenge sent by the server (in this order : base64 secret + server challenge), then generate a binary SHA256 hash of the result and encode it to base64.
|
||||||
|
- Voilà, this last base64 string is the auth response. You may now use it to authenticate to the server with the `Authenticate` request.
|
||||||
|
|
||||||
|
Here's how it looks in pseudocode :
|
||||||
|
```
|
||||||
|
password = "supersecretpassword"
|
||||||
|
challenge = "ztTBnnuqrqaKDzRM3xcVdbYm"
|
||||||
|
salt = "PZVbYpvAnZut2SS6JNJytDm9"
|
||||||
|
|
||||||
|
secret_string = password + salt
|
||||||
|
secret_hash = binary_sha256(secret_string)
|
||||||
|
secret = base64_encode(secret_hash)
|
||||||
|
|
||||||
|
auth_response_string = secret + challenge
|
||||||
|
auth_response_hash = binary_sha256(auth_response_string)
|
||||||
|
auth_response = base64_encode(auth_response_hash)
|
||||||
|
```
|
||||||
|
42
README.md
42
README.md
@ -2,18 +2,44 @@ obs-websocket
|
|||||||
==============
|
==============
|
||||||
Websocket API for OBS Studio.
|
Websocket API for OBS Studio.
|
||||||
|
|
||||||
## Build prerequisites
|
## Downloads
|
||||||
You need QT 5.7 (with QtWebSockets), CMake, and a working development environment for OBS Studio installed on your computer.
|
Binaries for Windows are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section. Linux and OS X releases coming soon.
|
||||||
|
|
||||||
## How to build
|
## Using obs-websocket
|
||||||
In CMake, you'll need to fill these CMake variables :
|
The Websocket API server runs on port 4444 and a settings window is available in "Websocket server settings" under OBS' "Tools" menu. The obs-websocket protocol is documented in [PROTOCOL.md](PROTOCOL.md).
|
||||||
|
|
||||||
|
Here's a list of available language APIs for obs-websocket :
|
||||||
|
- Javascript (browser & nodejs) : [obs-websocket-js](https://github.com/haganbmj/obs-websocket-js) by haganbmj
|
||||||
|
|
||||||
|
There's currently no frontend available for obs-websocket.
|
||||||
|
|
||||||
|
### Possible use cases
|
||||||
|
- Remote control OBS from a phone or tablet on the same local network
|
||||||
|
- Change your stream overlay/graphics based on the current scene (like the AGDQ overlay does)
|
||||||
|
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
|
||||||
|
|
||||||
|
## Compiling obs-websocket
|
||||||
|
### Prerequisites
|
||||||
|
You'll need QT 5 with QtWebSockets, CMake, and a working development environment for OBS Studio installed on your computer.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
In cmake-gui, you'll have to set the following variables :
|
||||||
- **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture
|
- **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture
|
||||||
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source code of OBS Studio
|
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source code of OBS Studio
|
||||||
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
|
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
|
||||||
- **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file
|
- **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file
|
||||||
|
|
||||||
After building the obs-websocket plugin's binary, copy its Qt dependencies (QtCore, QtNetwork and QtWebSockets library binaries) in the same folder.
|
### Linux
|
||||||
|
On Debian/Ubuntu :
|
||||||
|
```
|
||||||
|
sudo apt-get install libqt5websockets5-dev
|
||||||
|
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
||||||
|
cd obs-websocket
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
make -j4
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
## How to use
|
### OS X
|
||||||
There's currently no frontend or language API available for obs-websocket. However, the full protocol reference is documented in the [PROTOCOL.md](PROTOCOL.md) file.
|
*To do*
|
||||||
A simple websocket client can connect to the plugin's embedded server.
|
|
||||||
|
45
Utils.cpp
45
Utils.cpp
@ -27,11 +27,14 @@ obs_data_array_t* Utils::GetSceneItems(obs_source_t *source) {
|
|||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t *scene, obs_sceneitem_t *currentItem, void *param) {
|
obs_scene_enum_items(scene, [](obs_scene_t *scene, obs_sceneitem_t *currentItem, void *param) {
|
||||||
obs_data_array_t *data = static_cast<obs_data_array_t *>(param);
|
obs_data_array_t *data = static_cast<obs_data_array_t *>(param);
|
||||||
obs_data_array_push_back(data, GetSceneItemData(currentItem));
|
|
||||||
|
obs_data_t *item_data = GetSceneItemData(currentItem);
|
||||||
|
obs_data_array_insert(data, 0, item_data);
|
||||||
|
|
||||||
|
obs_data_release(item_data);
|
||||||
return true;
|
return true;
|
||||||
}, items);
|
}, items);
|
||||||
|
|
||||||
obs_scene_release(scene);
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +62,6 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t *item) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name) {
|
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name) {
|
||||||
struct current_search {
|
struct current_search {
|
||||||
const char* query;
|
const char* query;
|
||||||
@ -91,14 +93,40 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
|
|||||||
return search.result;
|
return search.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_source_t* Utils::GetTransitionFromName(const char *search_name) {
|
||||||
|
obs_source_t *found_transition = NULL;
|
||||||
|
|
||||||
|
obs_frontend_source_list transition_list = {};
|
||||||
|
obs_frontend_get_transitions(&transition_list);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < transition_list.sources.num; i++) {
|
||||||
|
obs_source_t *transition = transition_list.sources.array[i];
|
||||||
|
|
||||||
|
const char *transition_name = obs_source_get_name(transition);
|
||||||
|
if (strcmp(transition_name, search_name) == 0) {
|
||||||
|
found_transition = transition;
|
||||||
|
obs_source_addref(found_transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_source_list_free(&transition_list);
|
||||||
|
|
||||||
|
return found_transition;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_array_t* Utils::GetScenes() {
|
obs_data_array_t* Utils::GetScenes() {
|
||||||
obs_frontend_source_list sceneList = {};
|
obs_frontend_source_list sceneList = {};
|
||||||
obs_frontend_get_scenes(&sceneList);
|
obs_frontend_get_scenes(&sceneList);
|
||||||
|
|
||||||
obs_data_array_t* scenes = obs_data_array_create();
|
obs_data_array_t* scenes = obs_data_array_create();
|
||||||
for (size_t i = 0; i < (&sceneList)->sources.num; i++) {
|
for (size_t i = 0; i < sceneList.sources.num; i++) {
|
||||||
obs_source_t* scene = (&sceneList)->sources.array[i];
|
obs_source_t *scene = sceneList.sources.array[i];
|
||||||
obs_data_array_push_back(scenes, GetSceneData(scene));
|
|
||||||
|
obs_data_t *scene_data = GetSceneData(scene);
|
||||||
|
obs_data_array_push_back(scenes, scene_data);
|
||||||
|
|
||||||
|
obs_data_release(scene_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_source_list_free(&sceneList);
|
obs_frontend_source_list_free(&sceneList);
|
||||||
@ -107,9 +135,12 @@ obs_data_array_t* Utils::GetScenes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
obs_data_t* Utils::GetSceneData(obs_source *source) {
|
obs_data_t* Utils::GetSceneData(obs_source *source) {
|
||||||
|
obs_data_array_t *scene_items = GetSceneItems(source);
|
||||||
|
|
||||||
obs_data_t* sceneData = obs_data_create();
|
obs_data_t* sceneData = obs_data_create();
|
||||||
obs_data_set_string(sceneData, "name", obs_source_get_name(source));
|
obs_data_set_string(sceneData, "name", obs_source_get_name(source));
|
||||||
obs_data_set_array(sceneData, "sources", GetSceneItems(source));
|
obs_data_set_array(sceneData, "sources", scene_items);
|
||||||
|
|
||||||
|
obs_data_array_release(scene_items);
|
||||||
return sceneData;
|
return sceneData;
|
||||||
}
|
}
|
1
Utils.h
1
Utils.h
@ -28,6 +28,7 @@ class Utils
|
|||||||
static obs_data_array_t* GetSceneItems(obs_source_t *source);
|
static obs_data_array_t* GetSceneItems(obs_source_t *source);
|
||||||
static obs_data_t* GetSceneItemData(obs_scene_item *item);
|
static obs_data_t* GetSceneItemData(obs_scene_item *item);
|
||||||
static obs_sceneitem_t* GetSceneItemFromName(obs_source_t *source, const char *name);
|
static obs_sceneitem_t* GetSceneItemFromName(obs_source_t *source, const char *name);
|
||||||
|
static obs_source_t* GetTransitionFromName(const char *search_name);
|
||||||
|
|
||||||
static obs_data_array_t* GetScenes();
|
static obs_data_array_t* GetScenes();
|
||||||
static obs_data_t* GetSceneData(obs_source *source);
|
static obs_data_t* GetSceneData(obs_source *source);
|
||||||
|
76
WSEvents.cpp
76
WSEvents.cpp
@ -24,7 +24,7 @@ WSEvents::WSEvents(WSServer *server) {
|
|||||||
|
|
||||||
QTimer *statusTimer = new QTimer();
|
QTimer *statusTimer = new QTimer();
|
||||||
connect(statusTimer, SIGNAL(timeout()), this, SLOT(StreamStatus()));
|
connect(statusTimer, SIGNAL(timeout()), this, SLOT(StreamStatus()));
|
||||||
statusTimer->start(1000);
|
statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS
|
||||||
}
|
}
|
||||||
|
|
||||||
WSEvents::~WSEvents() {
|
WSEvents::~WSEvents() {
|
||||||
@ -61,8 +61,8 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void *private
|
|||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {
|
||||||
owner->OnRecordingStarted();
|
owner->OnRecordingStarted();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTING) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPING) {
|
||||||
owner->OnRecordingStarting();
|
owner->OnRecordingStopping();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED) {
|
||||||
owner->OnRecordingStopped();
|
owner->OnRecordingStopped();
|
||||||
@ -92,16 +92,21 @@ void WSEvents::broadcastUpdate(const char *updateType, obs_data_t *additionalFie
|
|||||||
|
|
||||||
void WSEvents::OnSceneChange() {
|
void WSEvents::OnSceneChange() {
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
obs_source_t *source = obs_frontend_get_current_scene();
|
obs_source_t *transition = obs_frontend_get_current_transition();
|
||||||
const char *name = obs_source_get_name(source);
|
obs_source_t *new_scene = obs_transition_get_source(transition, OBS_TRANSITION_SOURCE_B);
|
||||||
|
if (!new_scene) {
|
||||||
|
obs_source_release(transition);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
obs_data_t *data = obs_data_create();
|
||||||
obs_data_set_string(data, "scene-name", name);
|
obs_data_set_string(data, "scene-name", obs_source_get_name(new_scene));
|
||||||
|
|
||||||
broadcastUpdate("SwitchScenes", data);
|
broadcastUpdate("SwitchScenes", data);
|
||||||
|
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
obs_source_release(source);
|
obs_source_release(new_scene);
|
||||||
|
obs_source_release(transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneListChange() {
|
void WSEvents::OnSceneListChange() {
|
||||||
@ -167,49 +172,60 @@ void WSEvents::OnExit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::StreamStatus() {
|
void WSEvents::StreamStatus() {
|
||||||
bool streamingActive = obs_frontend_streaming_active();
|
bool streaming_active = obs_frontend_streaming_active();
|
||||||
bool recordingActive = obs_frontend_recording_active();
|
bool recording_active = obs_frontend_recording_active();
|
||||||
|
|
||||||
obs_output_t *streamOutput = obs_frontend_get_streaming_output();
|
obs_output_t *stream_output = obs_frontend_get_streaming_output();
|
||||||
|
|
||||||
if (!streamOutput || !streamingActive || !recordingActive) {
|
if (!stream_output || !streaming_active) {
|
||||||
|
if (stream_output) {
|
||||||
|
obs_output_release(stream_output);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
|
uint64_t bytes_sent = obs_output_get_total_bytes(stream_output);
|
||||||
uint64_t bytesSentTime = os_gettime_ns();
|
uint64_t bytes_sent_time = os_gettime_ns();
|
||||||
|
|
||||||
if (bytesSent < _lastBytesSent) {
|
if (bytes_sent < _lastBytesSent) {
|
||||||
bytesSent = 0;
|
bytes_sent = 0;
|
||||||
}
|
}
|
||||||
if (bytesSent == 0) {
|
if (bytes_sent == 0) {
|
||||||
_lastBytesSent = 0;
|
_lastBytesSent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t bitsBetween = (bytesSent - _lastBytesSent) * 8;
|
uint64_t bytes_between = bytes_sent - _lastBytesSent;
|
||||||
double timePassed = double(bytesSentTime - _lastBytesSentTime) / 1000000000.0;
|
double time_passed = double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
|
||||||
|
|
||||||
uint64_t bitsPerSec = bitsBetween / timePassed;
|
uint64_t bytes_per_sec = bytes_between / time_passed;
|
||||||
uint64_t bytesPerSec = bitsPerSec / 8;
|
|
||||||
|
|
||||||
_lastBytesSent = bytesSent;
|
_lastBytesSent = bytes_sent;
|
||||||
_lastBytesSentTime = bytesSentTime;
|
_lastBytesSentTime = bytes_sent_time;
|
||||||
|
|
||||||
uint64_t totalStreamTime = (os_gettime_ns() - _streamStartTime) / 1000000000;
|
uint64_t totalStreamTime = (os_gettime_ns() - _streamStartTime) / 1000000000;
|
||||||
|
|
||||||
|
int total_frames = obs_output_get_total_frames(stream_output);
|
||||||
|
int dropped_frames = obs_output_get_frames_dropped(stream_output);
|
||||||
|
|
||||||
|
float strain = 0.0;
|
||||||
|
if (total_frames > 0) {
|
||||||
|
strain = (dropped_frames / total_frames) * 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
obs_data_t *data = obs_data_create();
|
||||||
obs_data_set_bool(data, "streaming", streamingActive);
|
obs_data_set_bool(data, "streaming", streaming_active);
|
||||||
obs_data_set_bool(data, "recording", recordingActive);
|
obs_data_set_bool(data, "recording", recording_active);
|
||||||
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote
|
obs_data_set_int(data, "bytes-per-sec", bytes_per_sec);
|
||||||
obs_data_set_int(data, "bytes-per-sec", bytesPerSec); // BUG : Computation seems buggy
|
obs_data_set_int(data, "kbits-per-sec", (bytes_per_sec * 8) / 1024);
|
||||||
obs_data_set_double(data, "strain", 0.0); // dafuq is strain
|
|
||||||
obs_data_set_int(data, "total-stream-time", totalStreamTime);
|
obs_data_set_int(data, "total-stream-time", totalStreamTime);
|
||||||
obs_data_set_int(data, "num-total-frames", obs_output_get_total_frames(streamOutput));
|
obs_data_set_int(data, "num-total-frames", total_frames);
|
||||||
obs_data_set_int(data, "num-dropped-frames", obs_output_get_frames_dropped(streamOutput));
|
obs_data_set_int(data, "num-dropped-frames", dropped_frames);
|
||||||
obs_data_set_double(data, "fps", obs_get_active_fps());
|
obs_data_set_double(data, "fps", obs_get_active_fps());
|
||||||
|
obs_data_set_double(data, "strain", strain);
|
||||||
|
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote
|
||||||
|
|
||||||
broadcastUpdate("StreamStatus", data);
|
broadcastUpdate("StreamStatus", data);
|
||||||
|
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
obs_output_release(streamOutput);
|
obs_output_release(stream_output);
|
||||||
}
|
}
|
@ -90,6 +90,8 @@ void WSRequestHandler::processTextMessage(QString textMessage) {
|
|||||||
else {
|
else {
|
||||||
SendErrorResponse("invalid request type");
|
SendErrorResponse("invalid request type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_data_release(_requestData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::socketDisconnected() {
|
void WSRequestHandler::socketDisconnected() {
|
||||||
@ -104,6 +106,10 @@ void WSRequestHandler::sendTextMessage(QString textMessage) {
|
|||||||
_client->sendTextMessage(textMessage);
|
_client->sendTextMessage(textMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WSRequestHandler::isAuthenticated() {
|
||||||
|
return _authenticated;
|
||||||
|
}
|
||||||
|
|
||||||
WSRequestHandler::~WSRequestHandler() {
|
WSRequestHandler::~WSRequestHandler() {
|
||||||
if (_requestData != NULL) {
|
if (_requestData != NULL) {
|
||||||
obs_data_release(_requestData);
|
obs_data_release(_requestData);
|
||||||
@ -139,7 +145,8 @@ void WSRequestHandler::HandleGetVersion(WSRequestHandler *owner) {
|
|||||||
obs_data_t *data = obs_data_create();
|
obs_data_t *data = obs_data_create();
|
||||||
obs_data_set_double(data, "version", 1.1);
|
obs_data_set_double(data, "version", 1.1);
|
||||||
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
|
obs_data_set_string(data, "obs-websocket-version", OBS_WEBSOCKET_VERSION);
|
||||||
obs_data_set_string(data, "obs-studio-version", OBS_VERSION);
|
//obs_data_set_string(data, "obs-studio-version", OBS_VERSION); // Wrong
|
||||||
|
|
||||||
owner->SendOKResponse(data);
|
owner->SendOKResponse(data);
|
||||||
|
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
@ -192,30 +199,37 @@ void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *owner) {
|
|||||||
obs_source_release(source);
|
obs_source_release(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indirectly causes memory leaks
|
||||||
void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *owner) {
|
||||||
obs_source_t *source = obs_frontend_get_current_scene();
|
obs_source_t *current_scene = obs_frontend_get_current_scene();
|
||||||
const char *name = obs_source_get_name(source);
|
const char *name = obs_source_get_name(current_scene);
|
||||||
|
|
||||||
obs_data_array_t *scene_items = Utils::GetSceneItems(source);
|
obs_data_array_t *scene_items = Utils::GetSceneItems(current_scene); // Causes memory leaks
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
obs_data_t *data = obs_data_create();
|
||||||
obs_data_set_string(data, "name", name);
|
obs_data_set_string(data, "name", name);
|
||||||
obs_data_set_array(data, "sources", scene_items);
|
obs_data_set_array(data, "sources", scene_items);
|
||||||
|
|
||||||
owner->SendOKResponse(data);
|
owner->SendOKResponse(data);
|
||||||
|
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
obs_data_array_release(scene_items);
|
obs_data_array_release(scene_items);
|
||||||
//obs_source_release(source); // causes a source destroy sometimes
|
obs_source_release(current_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::HandleGetSceneList(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleGetSceneList(WSRequestHandler *owner) {
|
||||||
|
obs_source_t *current_scene = obs_frontend_get_current_scene();
|
||||||
|
obs_data_array_t *scenes = Utils::GetScenes();
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
obs_data_t *data = obs_data_create();
|
||||||
obs_data_set_string(data, "current-scene", obs_source_get_name(obs_frontend_get_current_scene()));
|
obs_data_set_string(data, "current-scene", obs_source_get_name(current_scene));
|
||||||
obs_data_set_array(data, "scenes", Utils::GetScenes());
|
obs_data_set_array(data, "scenes", scenes);
|
||||||
|
|
||||||
owner->SendOKResponse(data);
|
owner->SendOKResponse(data);
|
||||||
|
|
||||||
//obs_data_release(data); // da hell ? sometimes causes a crash too, like in GetCurrentScene...
|
obs_data_release(data);
|
||||||
|
obs_data_array_release(scenes);
|
||||||
|
obs_source_release(current_scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::HandleSetSourceRender(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleSetSourceRender(WSRequestHandler *owner) {
|
||||||
@ -274,39 +288,48 @@ void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *owner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner) {
|
||||||
|
obs_source_t *current_transition = obs_frontend_get_current_transition();
|
||||||
obs_frontend_source_list transitionList = {};
|
obs_frontend_source_list transitionList = {};
|
||||||
obs_frontend_get_transitions(&transitionList);
|
obs_frontend_get_transitions(&transitionList);
|
||||||
|
|
||||||
obs_data_array_t* transitions = obs_data_array_create();
|
obs_data_array_t* transitions = obs_data_array_create();
|
||||||
for (size_t i = 0; i < (&transitionList)->sources.num; i++) {
|
for (size_t i = 0; i < transitionList.sources.num; i++) {
|
||||||
obs_source_t* transition = (&transitionList)->sources.array[i];
|
obs_source_t* transition = transitionList.sources.array[i];
|
||||||
|
|
||||||
obs_data_t *obj = obs_data_create();
|
obs_data_t *obj = obs_data_create();
|
||||||
obs_data_set_string(obj, "name", obs_source_get_name(transition));
|
obs_data_set_string(obj, "name", obs_source_get_name(transition));
|
||||||
|
|
||||||
obs_data_array_push_back(transitions, obj);
|
obs_data_array_push_back(transitions, obj);
|
||||||
|
obs_data_release(obj);
|
||||||
}
|
}
|
||||||
obs_frontend_source_list_free(&transitionList);
|
obs_frontend_source_list_free(&transitionList);
|
||||||
|
|
||||||
obs_data_t *response = obs_data_create();
|
obs_data_t *response = obs_data_create();
|
||||||
obs_data_set_string(response, "current-transition", obs_source_get_name(obs_frontend_get_current_transition()));
|
obs_data_set_string(response, "current-transition", obs_source_get_name(current_transition));
|
||||||
obs_data_set_array(response, "transitions", transitions);
|
obs_data_set_array(response, "transitions", transitions);
|
||||||
|
|
||||||
owner->SendOKResponse(response);
|
owner->SendOKResponse(response);
|
||||||
|
|
||||||
obs_data_release(response);
|
obs_data_release(response);
|
||||||
|
obs_data_array_release(transitions);
|
||||||
|
obs_source_release(current_transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) {
|
||||||
|
obs_source_t *current_transition = obs_frontend_get_current_transition();
|
||||||
|
|
||||||
obs_data_t *response = obs_data_create();
|
obs_data_t *response = obs_data_create();
|
||||||
obs_data_set_string(response, "name", obs_source_get_name(obs_frontend_get_current_transition()));
|
obs_data_set_string(response, "name", obs_source_get_name(current_transition));
|
||||||
|
|
||||||
owner->SendOKResponse(response);
|
owner->SendOKResponse(response);
|
||||||
|
|
||||||
obs_data_release(response);
|
obs_data_release(response);
|
||||||
|
obs_source_release(current_transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner) {
|
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner) {
|
||||||
const char *name = obs_data_get_string(owner->_requestData, "transition-name");
|
const char *name = obs_data_get_string(owner->_requestData, "transition-name");
|
||||||
obs_source_t *transition = obs_get_source_by_name(name);
|
obs_source_t *transition = Utils::GetTransitionFromName(name);
|
||||||
|
|
||||||
if (transition) {
|
if (transition) {
|
||||||
obs_frontend_set_current_transition(transition);
|
obs_frontend_set_current_transition(transition);
|
||||||
|
@ -32,6 +32,7 @@ class WSRequestHandler : public QObject
|
|||||||
explicit WSRequestHandler(QWebSocket *client);
|
explicit WSRequestHandler(QWebSocket *client);
|
||||||
~WSRequestHandler();
|
~WSRequestHandler();
|
||||||
void sendTextMessage(QString textMessage);
|
void sendTextMessage(QString textMessage);
|
||||||
|
bool isAuthenticated();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void processTextMessage(QString textMessage);
|
void processTextMessage(QString textMessage);
|
||||||
|
@ -18,6 +18,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
#include "WSRequestHandler.h"
|
#include "WSRequestHandler.h"
|
||||||
|
#include "Config.h"
|
||||||
#include <QtWebSockets/QWebSocketServer>
|
#include <QtWebSockets/QWebSocketServer>
|
||||||
#include <QtWebSockets/QWebSocket>
|
#include <QtWebSockets/QWebSocket>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
@ -54,6 +55,12 @@ WSServer::~WSServer()
|
|||||||
void WSServer::broadcast(QString message)
|
void WSServer::broadcast(QString message)
|
||||||
{
|
{
|
||||||
Q_FOREACH(WSRequestHandler *pClient, _clients) {
|
Q_FOREACH(WSRequestHandler *pClient, _clients) {
|
||||||
|
if (Config::Current()->AuthRequired == true
|
||||||
|
&& pClient->isAuthenticated() == false) {
|
||||||
|
// Skip this client if unauthenticated
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pClient->sendTextMessage(message);
|
pClient->sendTextMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#ifndef OBSWEBSOCKET_H
|
#ifndef OBSWEBSOCKET_H
|
||||||
#define OBSWEBSOCKET_H
|
#define OBSWEBSOCKET_H
|
||||||
|
|
||||||
#define OBS_WEBSOCKET_VERSION "0.3-alpha"
|
#define OBS_WEBSOCKET_VERSION "0.3.1"
|
||||||
|
|
||||||
#endif // OBSWEBSOCKET_H
|
#endif // OBSWEBSOCKET_H
|
Reference in New Issue
Block a user