Compare commits

..

30 Commits

Author SHA1 Message Date
cfb490b7e3 Bump version to 0.3.1 2016-11-30 20:05:20 +01:00
1947a3cfd5 Fixes #14 2016-11-30 20:04:24 +01:00
fca6774d4a Fixes #13 2016-11-30 18:54:46 +01:00
5fd1e978ec Fixed more memory leaks and crashes 2016-11-30 11:33:08 +01:00
4985c549dd Fixed #12 and identified several memory leaks 2016-11-30 11:09:29 +01:00
e6c378aef0 New Linux build instructions 2016-11-30 09:10:04 +01:00
0657835ba3 Update README.md 2016-11-29 14:52:05 +01:00
36934de04a Described authentication procedure in PROTOCOL.md 2016-11-28 16:01:20 +01:00
e735ab80b1 Better PROTOCOL.md 2016-11-28 11:55:20 +01:00
9904ca89d4 Fixes #11 2016-11-27 23:50:59 +01:00
554ab54690 Security fix : don't broadcast messages to unauthenticated clients 2016-11-27 17:40:57 +01:00
5abcd18ba0 Fixes #8 and #9 2016-11-27 17:32:37 +01:00
03bf41b250 Update README.md 2016-11-27 16:12:52 +01:00
0df6bde5cb Update README.md 2016-11-27 16:11:19 +01:00
e3a28b640f Update CMakeLists.txt 2016-11-23 21:45:25 +01:00
94576fae44 Better build process on Windows 2016-11-22 22:02:46 +01:00
d091d9c3e4 Better build process on Linux 2016-11-22 19:51:47 +01:00
01c9e48f4f Update README.md 2016-11-22 18:40:27 +01:00
5639c18c2f Wth did i do 2016-11-22 18:34:19 +01:00
25b835f4d8 Update README.md 2016-11-22 17:43:56 +01:00
78fb5b3093 Update README.md 2016-11-22 17:38:30 +01:00
73b85ef9df Update CMakeLists.txt 2016-11-22 17:38:04 +01:00
93c604a639 Update README.md 2016-11-22 17:01:29 +01:00
053676a320 Update PROTOCOL.md 2016-11-22 10:11:52 +01:00
d1a142cefd Fixed typo 2016-11-22 10:05:35 +01:00
36cf2a4126 Update README.md 2016-11-20 23:31:07 +01:00
a3bc9ae8d0 Update README.md 2016-11-20 23:24:47 +01:00
6d77ae24b3 Merge pull request #7 from haganbmj/master
[API-docs] Update Event/Request Fields
2016-11-20 04:32:46 +01:00
d9bbed5f69 [API-docs] Update Event/Request Fields 2016-11-19 21:06:47 -05:00
d6ce405e71 GetVersion : don't get OBS Studio's version number at compile time... 2016-11-19 17:58:13 +01:00
10 changed files with 393 additions and 130 deletions

View File

@ -15,11 +15,6 @@ find_package(Qt5Widgets REQUIRED)
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
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
obs-websocket.cpp
WSServer.cpp
@ -51,8 +46,34 @@ include_directories(
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
target_link_libraries(obs-websocket
libobs
${OBS_FRONTEND_LIB}
Qt5::Core
Qt5::WebSockets
Qt5::Widgets
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()

View File

@ -1,70 +1,75 @@
obs-websocket protocol reference
================================
## General introduction
**This document is still a WIP. Some things are missing (but won't stay like this for long).**
## General Introduction
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
### Description
Events are sent exclusively by the server and broadcasted to every client connected to the server.
An event message has at least one field :
Events are sent exclusively by the server and broadcast to each connected client.
An event message will contain at least one field :
- **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"
OBS is switching to another scene.
Additional fields :
- **scene-name** : the name of the scene being switched to
- **scene-name** (string) : The name of the scene being switched to.
#### "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"
Streaming is starting but isn't completely started yet.
A request to start streaming has been issued.
- **preview-only** (bool) : Always false.
#### "StreamStarted"
Streaming started successfully.
*New in OBS Studio*
Streaming has been started successfully.
#### "StreamStopping"
Streaming is stopping but isn't completely stopped yet.
A request to stop streaming has been issued.
- **preview-only** (bool) : Always false.
#### "StreamStopped"
Streaming stopped successfully.
*New in OBS Studio*
Streaming has been stopped successfully.
#### "RecordingStarting"
A request to start recording has been issued.
*New in OBS Studio*
Recording is starting but isn't completely started yet.
#### "RecordingStarted"
Recording started successfully.
*New in OBS Studio*
Recording has been started successfully.
#### "RecordingStopping"
A request to stop streaming has been issued.
*New in OBS Studio*
Recording is stopping but isn't completely stopped yet.
#### "RecordingStopped"
Recording stopped successfully.
*New in OBS Studio*
Recording has been stopped successfully.
#### "StreamStatus"
Sent every second with those additional fields :
- **streaming** (bool) : streaming state
- **recording** (bool) : recording state
- **preview-only** (bool) : always false.
- **bytes-per-sec** (integer) : during streaming, amount of data (in bytes) transmitted by the stream encoder
- **strain** (double) : i have no idea what this is
- **total-stream-time** (integer) : total time since the beginning of streaming
- **num-total-frames** (integer) : total number of frames transmitted since the beginning
- **num-dropped-frames** (integer) : number of frames dropped by the encoder for the current streaming session
- **fps** (double) : current framerate
Sent every 2 seconds with the following information :
- **streaming** (bool) : Current Streaming state.
- **recording** (bool) : Current Recording state.
- **preview-only** (bool) : Always false.
- **bytes-per-sec** (integer) : Amount of data per second (in bytes) transmitted by the stream encoder.
- **kbits-per-sec** (integer) : "bytes-per-sec" converted to kilobits per second
- **strain** (double) : Percentage of dropped frames
- **total-stream-time** (integer) : Total time (in seconds) since the stream started.
- **num-total-frames** (integer) : Total number of frames transmitted since the stream started.
- **num-dropped-frames** (integer) : Number of frames dropped by the encoder since the stream started.
- **fps** (double) : Current framerate.
#### "Exiting"
*New in OBS Studio*
@ -73,32 +78,164 @@ OBS is exiting.
## Requests
### Description
Requests are sent by the client and have at least two fields :
- **"request-type"** (string) : one of the request types listed in the sub-section "Requests".
- **"message-id"** (string) : an string defined by the client that will be embedded in the response from the server.
Depending on the request type, additional fields are needed in the request message (see the "Request types" section below for more informations).
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 Types](#request-types)".
- **"message-id"** (string) : An identifier defined by the client which will be embedded in the server response.
Once a request is sent, the server processes it and sends a JSON response to the client with the following fields in it :
- **"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.
Depending on the request type additional fields may be required (see the "[Request Types](#request-types)" section below for more information).
### 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"
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"
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"
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"
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"
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"
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"
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"
Toggle streaming on or off.
__Request fields__ : none
__Response__ : always OK. No additional fields.
#### "StartStopRecording"
Toggle recording on or off.
__Request fields__ : none
__Response__ : always OK. No additional fields.
*New in OBS Studio*
#### "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"
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*
#### "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*
#### "SetCurrentTransition"
__Request fields__ :
- **"transition-name"** (string) : The name of the transition.
__Response__ : OK if specified transition exists, error otherwise.
*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)
```

View File

@ -2,18 +2,44 @@ obs-websocket
==============
Websocket API for OBS Studio.
## Build prerequisites
You need QT 5.7 (with QtWebSockets), CMake, and a working development environment for OBS Studio installed on your computer.
## Downloads
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
In CMake, you'll need to fill these CMake variables :
## Using obs-websocket
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
- **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
- **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
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.
A simple websocket client can connect to the plugin's embedded server.
### OS X
*To do*

View File

@ -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_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;
}, items);
obs_scene_release(scene);
return items;
}
@ -59,7 +62,6 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t *item) {
return data;
}
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name) {
struct current_search {
const char* query;
@ -91,14 +93,40 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
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_frontend_source_list sceneList = {};
obs_frontend_get_scenes(&sceneList);
obs_data_array_t* scenes = obs_data_array_create();
for (size_t i = 0; i < (&sceneList)->sources.num; i++) {
obs_source_t* scene = (&sceneList)->sources.array[i];
obs_data_array_push_back(scenes, GetSceneData(scene));
for (size_t i = 0; i < sceneList.sources.num; i++) {
obs_source_t *scene = sceneList.sources.array[i];
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);
@ -107,9 +135,12 @@ obs_data_array_t* Utils::GetScenes() {
}
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_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;
}

View File

@ -27,7 +27,8 @@ class Utils
public:
static obs_data_array_t* GetSceneItems(obs_source_t *source);
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_t* GetSceneData(obs_source *source);

View File

@ -24,7 +24,7 @@ WSEvents::WSEvents(WSServer *server) {
QTimer *statusTimer = new QTimer();
connect(statusTimer, SIGNAL(timeout()), this, SLOT(StreamStatus()));
statusTimer->start(1000);
statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS
}
WSEvents::~WSEvents() {
@ -61,8 +61,8 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void *private
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {
owner->OnRecordingStarted();
}
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTING) {
owner->OnRecordingStarting();
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPING) {
owner->OnRecordingStopping();
}
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED) {
owner->OnRecordingStopped();
@ -92,16 +92,21 @@ void WSEvents::broadcastUpdate(const char *updateType, obs_data_t *additionalFie
void WSEvents::OnSceneChange() {
// Implements an existing update type from bilhamil's OBS Remote
obs_source_t *source = obs_frontend_get_current_scene();
const char *name = obs_source_get_name(source);
obs_source_t *transition = obs_frontend_get_current_transition();
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_set_string(data, "scene-name", name);
obs_data_set_string(data, "scene-name", obs_source_get_name(new_scene));
broadcastUpdate("SwitchScenes", data);
obs_data_release(data);
obs_source_release(source);
obs_source_release(new_scene);
obs_source_release(transition);
}
void WSEvents::OnSceneListChange() {
@ -167,49 +172,60 @@ void WSEvents::OnExit() {
}
void WSEvents::StreamStatus() {
bool streamingActive = obs_frontend_streaming_active();
bool recordingActive = obs_frontend_recording_active();
bool streaming_active = obs_frontend_streaming_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;
}
uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
uint64_t bytesSentTime = os_gettime_ns();
uint64_t bytes_sent = obs_output_get_total_bytes(stream_output);
uint64_t bytes_sent_time = os_gettime_ns();
if (bytesSent < _lastBytesSent) {
bytesSent = 0;
if (bytes_sent < _lastBytesSent) {
bytes_sent = 0;
}
if (bytesSent == 0) {
if (bytes_sent == 0) {
_lastBytesSent = 0;
}
uint64_t bitsBetween = (bytesSent - _lastBytesSent) * 8;
double timePassed = double(bytesSentTime - _lastBytesSentTime) / 1000000000.0;
uint64_t bytes_between = bytes_sent - _lastBytesSent;
double time_passed = double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
uint64_t bitsPerSec = bitsBetween / timePassed;
uint64_t bytesPerSec = bitsPerSec / 8;
uint64_t bytes_per_sec = bytes_between / time_passed;
_lastBytesSent = bytesSent;
_lastBytesSentTime = bytesSentTime;
_lastBytesSent = bytes_sent;
_lastBytesSentTime = bytes_sent_time;
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_set_bool(data, "streaming", streamingActive);
obs_data_set_bool(data, "recording", recordingActive);
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote
obs_data_set_int(data, "bytes-per-sec", bytesPerSec); // BUG : Computation seems buggy
obs_data_set_double(data, "strain", 0.0); // dafuq is strain
obs_data_set_bool(data, "streaming", streaming_active);
obs_data_set_bool(data, "recording", recording_active);
obs_data_set_int(data, "bytes-per-sec", bytes_per_sec);
obs_data_set_int(data, "kbits-per-sec", (bytes_per_sec * 8) / 1024);
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-dropped-frames", obs_output_get_frames_dropped(streamOutput));
obs_data_set_int(data, "num-total-frames", total_frames);
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, "strain", strain);
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote
broadcastUpdate("StreamStatus", data);
obs_data_release(data);
obs_output_release(streamOutput);
obs_output_release(stream_output);
}

View File

@ -90,6 +90,8 @@ void WSRequestHandler::processTextMessage(QString textMessage) {
else {
SendErrorResponse("invalid request type");
}
obs_data_release(_requestData);
}
void WSRequestHandler::socketDisconnected() {
@ -104,6 +106,10 @@ void WSRequestHandler::sendTextMessage(QString textMessage) {
_client->sendTextMessage(textMessage);
}
bool WSRequestHandler::isAuthenticated() {
return _authenticated;
}
WSRequestHandler::~WSRequestHandler() {
if (_requestData != NULL) {
obs_data_release(_requestData);
@ -139,7 +145,8 @@ void WSRequestHandler::HandleGetVersion(WSRequestHandler *owner) {
obs_data_t *data = obs_data_create();
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-studio-version", OBS_VERSION);
//obs_data_set_string(data, "obs-studio-version", OBS_VERSION); // Wrong
owner->SendOKResponse(data);
obs_data_release(data);
@ -192,30 +199,37 @@ void WSRequestHandler::HandleSetCurrentScene(WSRequestHandler *owner) {
obs_source_release(source);
}
// Indirectly causes memory leaks
void WSRequestHandler::HandleGetCurrentScene(WSRequestHandler *owner) {
obs_source_t *source = obs_frontend_get_current_scene();
const char *name = obs_source_get_name(source);
obs_source_t *current_scene = obs_frontend_get_current_scene();
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_set_string(data, "name", name);
obs_data_set_array(data, "sources", scene_items);
owner->SendOKResponse(data);
obs_data_release(data);
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) {
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_set_string(data, "current-scene", obs_source_get_name(obs_frontend_get_current_scene()));
obs_data_set_array(data, "scenes", Utils::GetScenes());
obs_data_set_string(data, "current-scene", obs_source_get_name(current_scene));
obs_data_set_array(data, "scenes", scenes);
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) {
@ -274,39 +288,48 @@ void WSRequestHandler::HandleStartStopRecording(WSRequestHandler *owner) {
}
void WSRequestHandler::HandleGetTransitionList(WSRequestHandler *owner) {
obs_source_t *current_transition = obs_frontend_get_current_transition();
obs_frontend_source_list transitionList = {};
obs_frontend_get_transitions(&transitionList);
obs_data_array_t* transitions = obs_data_array_create();
for (size_t i = 0; i < (&transitionList)->sources.num; i++) {
obs_source_t* transition = (&transitionList)->sources.array[i];
for (size_t i = 0; i < transitionList.sources.num; i++) {
obs_source_t* transition = transitionList.sources.array[i];
obs_data_t *obj = obs_data_create();
obs_data_set_string(obj, "name", obs_source_get_name(transition));
obs_data_array_push_back(transitions, obj);
obs_data_release(obj);
}
obs_frontend_source_list_free(&transitionList);
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);
owner->SendOKResponse(response);
obs_data_release(response);
obs_data_array_release(transitions);
obs_source_release(current_transition);
}
void WSRequestHandler::HandleGetCurrentTransition(WSRequestHandler *owner) {
obs_source_t *current_transition = obs_frontend_get_current_transition();
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);
obs_data_release(response);
obs_source_release(current_transition);
}
void WSRequestHandler::HandleSetCurrentTransition(WSRequestHandler *owner) {
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) {
obs_frontend_set_current_transition(transition);

View File

@ -32,6 +32,7 @@ class WSRequestHandler : public QObject
explicit WSRequestHandler(QWebSocket *client);
~WSRequestHandler();
void sendTextMessage(QString textMessage);
bool isAuthenticated();
private Q_SLOTS:
void processTextMessage(QString textMessage);

View File

@ -18,6 +18,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "WSServer.h"
#include "WSRequestHandler.h"
#include "Config.h"
#include <QtWebSockets/QWebSocketServer>
#include <QtWebSockets/QWebSocket>
#include <QtCore/QDebug>
@ -54,6 +55,12 @@ WSServer::~WSServer()
void WSServer::broadcast(QString message)
{
Q_FOREACH(WSRequestHandler *pClient, _clients) {
if (Config::Current()->AuthRequired == true
&& pClient->isAuthenticated() == false) {
// Skip this client if unauthenticated
continue;
}
pClient->sendTextMessage(message);
}
}

View File

@ -19,6 +19,6 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#ifndef OBSWEBSOCKET_H
#define OBSWEBSOCKET_H
#define OBS_WEBSOCKET_VERSION "0.3-alpha"
#define OBS_WEBSOCKET_VERSION "0.3.1"
#endif // OBSWEBSOCKET_H