mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Request types: get/set/save streaming settings (PR #100)
* Adding support for changing streaming server settings * Updates after initial code review for customized rtmp settings * Updating PROTOCOL.MD documentment for streaming service settings * Changes based on code review
This commit is contained in:
parent
acffacd67d
commit
e3ad148c15
73
PROTOCOL.md
73
PROTOCOL.md
@ -104,6 +104,10 @@ The protocol in general is based on the OBS Remote protocol created by Bill Hami
|
||||
- ["ListSceneCollections"](#listscenecollections)
|
||||
- ["SetCurrentSceneCollection"](#setcurrentscenecollection)
|
||||
- ["GetCurrentSceneCollection"](#getcurrentscenecollection)
|
||||
- **Streaming Server Settings**
|
||||
- ["GetStreamSettings"](#getstreamsettings)
|
||||
- ["SetStreamSettings"](#setstreamsettings)
|
||||
- ["SaveStreamSettings"](#savestreamsettings)
|
||||
- **Profiles**
|
||||
- ["ListProfiles"](#listprofiles)
|
||||
- ["SetCurrentProfile"](#setcurrentprofile)
|
||||
@ -489,7 +493,9 @@ __Response__ : always OK. No additional fields.
|
||||
#### "StartStopRecording"
|
||||
Toggles recording on or off.
|
||||
|
||||
__Request fields__ : none
|
||||
__Request fields__ :
|
||||
- **"stream"** (object; optional) : See 'stream' parameter in 'StartStreaming'. Ignored if stream is already started.
|
||||
|
||||
__Response__ : always OK. No additional fields.
|
||||
|
||||
---
|
||||
@ -497,7 +503,23 @@ __Response__ : always OK. No additional fields.
|
||||
#### "StartStreaming"
|
||||
Start streaming.
|
||||
|
||||
__Request fields__ : none
|
||||
__Request fields__ :
|
||||
- **"stream"** (object; optional) : If specified allows for special configuration of the stream
|
||||
|
||||
The 'stream' object has the following fields:
|
||||
- **"settings"** (object; optional) : The settings for the stream
|
||||
- **"type"** (string; optional) : If specified ensures the type of the stream matches the given type (usually 'rtmp\_custom' or 'rtmp\_common'). If the currently configured stream type does not match the given stream type, all settings must be specified in the 'settings' object or an error will occur starting the stream.
|
||||
- **"metadata"** (object; optional) : Adds the given object parameters as encoded query string parameters to the 'key' of the RTMP stream. Used to pass data to the RTMP service about the stream.
|
||||
|
||||
The 'settings' object has the following fields:
|
||||
- **"server"** (string; optional) : The publish URL
|
||||
- **"key"** (string; optional) : The publish key of the stream
|
||||
- **"use-auth"** (bool; optional) : should authentication be used when connecting to the streaming server
|
||||
- **"username"** (string; optional) : if authentication is enabled, the username for access to the streaming server. Ignored if 'use-auth' is not specified as 'true'.
|
||||
- **"password"** (string; optional) : if authentication is enabled, the password for access to the streaming server. Ignored if 'use-auth' is not specified as 'true'.
|
||||
|
||||
The 'metadata' object supports passing any string, numeric or boolean field.
|
||||
|
||||
__Response__ : Error if streaming is already active, OK otherwise. No additional fields.
|
||||
|
||||
---
|
||||
@ -746,6 +768,53 @@ __Response__ : OK with these additional fields :
|
||||
|
||||
---
|
||||
|
||||
#### "GetStreamSettings"
|
||||
Gets the current streaming server settings
|
||||
|
||||
__Request fields__ : none
|
||||
|
||||
__Response__ : OK with these additional fields :
|
||||
- **"type"** (string) : The type of streaming service configuration usually 'rtmp\_custom' or 'rtmp\_common'
|
||||
- **"settings"** (object) : The actual settings of the stream (i.e. server, key, use-auth, username, password)
|
||||
|
||||
The 'settings' object has the following fields however they may vary by 'type':
|
||||
- **"server"** (string) : The publish URL
|
||||
- **"key"** (string) : The publish key of the stream
|
||||
- **"use-auth"** (bool) : should authentication be used when connecting to the streaming server
|
||||
- **"username"** (string) : if authentication is enabled, the username for access to the streaming server
|
||||
- **"password"** (string) : if authentication is enabled, the password for access to the streaming server
|
||||
|
||||
--
|
||||
|
||||
#### "SetStreamSettings"
|
||||
Sets one or more attributes of the current streaming server settings. Any options not passed will remain unchanged. Returns the updated settings in response.
|
||||
If 'type' is different than the current streaming service type, all settings are required.
|
||||
Returns the full settings of the stream (i.e. the same as GetStreamSettings)
|
||||
|
||||
__Request fields__ :
|
||||
- **"type"** (string) : The type of streaming service configuration usually 'rtmp\_custom' or 'rtmp\_common'
|
||||
- **"settings"** (object) : The actual settings of the stream (i.e. server, key, use-auth, username, password)
|
||||
- **"save"** (bool) : If specified as true, saves the settings to disk
|
||||
|
||||
The 'settings' object has the following fields however they may vary by 'type':
|
||||
- **"server"** (string; optional) : The publish URL
|
||||
- **"key"** (string; optional) : The publish key of the stream
|
||||
- **"use-auth"** (bool; optional) : should authentication be used when connecting to the streaming server
|
||||
- **"username"** (string; optional) : if authentication is enabled, the username for access to the streaming server
|
||||
- **"password"** (string; optional) : if authentication is enabled, the password for access to the streaming server
|
||||
|
||||
__Response__ : OK with the same fields as the request (except 'save')
|
||||
|
||||
---
|
||||
|
||||
#### "SaveStreamSettings"
|
||||
Saves the current streaming server settings to disk
|
||||
|
||||
__Request fields__ : none
|
||||
|
||||
__Response__ : OK
|
||||
|
||||
|
||||
#### "SetCurrentProfile"
|
||||
Change the current profile.
|
||||
|
||||
|
57
Utils.cpp
57
Utils.cpp
@ -20,6 +20,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
#include <obs.hpp>
|
||||
#include <QMainWindow>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include "Utils.h"
|
||||
#include "obs-websocket.h"
|
||||
|
||||
@ -464,4 +465,58 @@ bool Utils::SetRecordingFolder(const char* path)
|
||||
|
||||
config_save(profile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QString* Utils::ParseDataToQueryString(obs_data_t * data)
|
||||
{
|
||||
QString* query = nullptr;
|
||||
if (data)
|
||||
{
|
||||
obs_data_item_t* item = obs_data_first(data);
|
||||
if (item)
|
||||
{
|
||||
query = new QString();
|
||||
bool isFirst = true;
|
||||
do
|
||||
{
|
||||
if (!obs_data_item_has_user_value(item))
|
||||
continue;
|
||||
|
||||
if (!isFirst)
|
||||
query->append('&');
|
||||
else
|
||||
isFirst = false;
|
||||
|
||||
const char* attrName = obs_data_item_get_name(item);
|
||||
query->append(attrName).append("=");
|
||||
switch (obs_data_item_gettype(item))
|
||||
{
|
||||
case OBS_DATA_BOOLEAN:
|
||||
query->append(obs_data_item_get_bool(item)?"true":"false");
|
||||
break;
|
||||
case OBS_DATA_NUMBER:
|
||||
switch (obs_data_item_numtype(item))
|
||||
{
|
||||
case OBS_DATA_NUM_DOUBLE:
|
||||
query->append(QString::number(obs_data_item_get_double(item)));
|
||||
break;
|
||||
case OBS_DATA_NUM_INT:
|
||||
query->append(QString::number(obs_data_item_get_int(item)));
|
||||
break;
|
||||
case OBS_DATA_NUM_INVALID:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case OBS_DATA_STRING:
|
||||
query->append(QUrl::toPercentEncoding(QString(obs_data_item_get_string(item))));
|
||||
break;
|
||||
default:
|
||||
//other types are not supported
|
||||
break;
|
||||
}
|
||||
} while ( obs_data_item_next( &item ) );
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
2
Utils.h
2
Utils.h
@ -76,6 +76,8 @@ class Utils
|
||||
static QString FormatIPAddress(QHostAddress &addr);
|
||||
static const char* GetRecordingFolder();
|
||||
static bool SetRecordingFolder(const char* path);
|
||||
|
||||
static QString* ParseDataToQueryString(obs_data_t * data);
|
||||
};
|
||||
|
||||
#endif // UTILS_H
|
||||
|
@ -17,17 +17,22 @@ You should have received a copy of the GNU General Public License along
|
||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <obs-data.h>
|
||||
#include "WSRequestHandler.h"
|
||||
#include "WSEvents.h"
|
||||
#include "obs-websocket.h"
|
||||
#include "Config.h"
|
||||
#include "Utils.h"
|
||||
#include <qstring.h>
|
||||
|
||||
bool str_valid(const char* str)
|
||||
{
|
||||
return (str != nullptr && strlen(str) > 0);
|
||||
}
|
||||
|
||||
|
||||
obs_service_t* WSRequestHandler::_service = nullptr;
|
||||
|
||||
WSRequestHandler::WSRequestHandler(QWebSocket* client) :
|
||||
_messageId(0),
|
||||
_requestType(""),
|
||||
@ -81,6 +86,10 @@ WSRequestHandler::WSRequestHandler(QWebSocket* client) :
|
||||
messageMap["GetCurrentProfile"] = WSRequestHandler::HandleGetCurrentProfile;
|
||||
messageMap["ListProfiles"] = WSRequestHandler::HandleListProfiles;
|
||||
|
||||
messageMap["SetStreamSettings"] = WSRequestHandler::HandleSetStreamSettings;
|
||||
messageMap["GetStreamSettings"] = WSRequestHandler::HandleGetStreamSettings;
|
||||
messageMap["SaveStreamSettings"] = WSRequestHandler::HandleSaveStreamSettings;
|
||||
|
||||
messageMap["GetStudioModeStatus"] = WSRequestHandler::HandleGetStudioModeStatus;
|
||||
messageMap["GetPreviewScene"] = WSRequestHandler::HandleGetPreviewScene;
|
||||
messageMap["SetPreviewScene"] = WSRequestHandler::HandleSetPreviewScene;
|
||||
@ -91,6 +100,7 @@ WSRequestHandler::WSRequestHandler(QWebSocket* client) :
|
||||
|
||||
messageMap["SetTextGDIPlusProperties"] = WSRequestHandler::HandleSetTextGDIPlusProperties;
|
||||
messageMap["GetTextGDIPlusProperties"] = WSRequestHandler::HandleGetTextGDIPlusProperties;
|
||||
|
||||
messageMap["GetBrowserSourceProperties"] = WSRequestHandler::HandleGetBrowserSourceProperties;
|
||||
messageMap["SetBrowserSourceProperties"] = WSRequestHandler::HandleSetBrowserSourceProperties;
|
||||
|
||||
@ -380,11 +390,13 @@ void WSRequestHandler::HandleGetStreamingStatus(WSRequestHandler* req)
|
||||
void WSRequestHandler::HandleStartStopStreaming(WSRequestHandler* req)
|
||||
{
|
||||
if (obs_frontend_streaming_active())
|
||||
obs_frontend_streaming_stop();
|
||||
{
|
||||
HandleStopStreaming(req);
|
||||
}
|
||||
else
|
||||
obs_frontend_streaming_start();
|
||||
|
||||
req->SendOKResponse();
|
||||
{
|
||||
HandleStartStreaming(req);
|
||||
}
|
||||
}
|
||||
|
||||
void WSRequestHandler::HandleStartStopRecording(WSRequestHandler* req)
|
||||
@ -401,8 +413,97 @@ void WSRequestHandler::HandleStartStreaming(WSRequestHandler* req)
|
||||
{
|
||||
if (obs_frontend_streaming_active() == false)
|
||||
{
|
||||
obs_data_t* streamData = obs_data_get_obj(req->data, "stream");
|
||||
obs_service_t* currentService = nullptr;
|
||||
|
||||
if (streamData)
|
||||
{
|
||||
currentService = obs_frontend_get_streaming_service();
|
||||
obs_service_addref(currentService);
|
||||
|
||||
obs_service_t* service = _service;
|
||||
const char* currentServiceType = obs_service_get_type(currentService);
|
||||
|
||||
const char* requestedType = obs_data_has_user_value(streamData, "type") ? obs_data_get_string(streamData, "type") : currentServiceType;
|
||||
const char* serviceType = service != nullptr ? obs_service_get_type(service) : currentServiceType;
|
||||
obs_data_t* settings = obs_data_get_obj(streamData, "settings");
|
||||
|
||||
|
||||
obs_data_t* metadata = obs_data_get_obj(streamData, "metadata");
|
||||
QString* query = Utils::ParseDataToQueryString(metadata);
|
||||
|
||||
if (strcmp(requestedType, serviceType) != 0)
|
||||
{
|
||||
if (settings)
|
||||
{
|
||||
obs_service_release(service);
|
||||
service = nullptr; //different type so we can't reuse the existing service instance
|
||||
}
|
||||
else
|
||||
{
|
||||
req->SendErrorResponse("Service type requested does not match currently configured type and no 'settings' were provided");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if type isn't changing we should overlay the settings we got with the existing settings
|
||||
obs_data_t* existingSettings = obs_service_get_settings(currentService);
|
||||
obs_data_t* newSettings = obs_data_create(); //by doing this you can send a request to the websocket that only contains a setting you want to change instead of having to do a get and then change them
|
||||
|
||||
obs_data_apply(newSettings, existingSettings); //first apply the existing settings
|
||||
|
||||
obs_data_apply(newSettings, settings); //then apply the settings from the request should they exist
|
||||
obs_data_release(settings);
|
||||
|
||||
settings = newSettings;
|
||||
obs_data_release(existingSettings);
|
||||
}
|
||||
|
||||
if (service == nullptr)
|
||||
{ //create the new custom service setup by the websocket
|
||||
service = obs_service_create(requestedType, "websocket_custom_service", settings, nullptr);
|
||||
}
|
||||
|
||||
//Supporting adding metadata parameters to key query string
|
||||
if (query && query->length() > 0) {
|
||||
const char* key = obs_data_get_string(settings, "key");
|
||||
int keylen = strlen(key);
|
||||
bool hasQuestionMark = false;
|
||||
for (int i = 0; i < keylen; i++) {
|
||||
if (key[i] == '?') {
|
||||
hasQuestionMark = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasQuestionMark) {
|
||||
query->prepend('&');
|
||||
} else {
|
||||
query->prepend('?');
|
||||
}
|
||||
query->prepend(key);
|
||||
key = query->toUtf8();
|
||||
obs_data_set_string(settings, "key", key);
|
||||
}
|
||||
|
||||
obs_service_update(service, settings);
|
||||
obs_data_release(settings);
|
||||
obs_data_release(metadata);
|
||||
_service = service;
|
||||
obs_frontend_set_streaming_service(_service);
|
||||
} else if (_service != nullptr) {
|
||||
obs_service_release(_service);
|
||||
_service = nullptr;
|
||||
}
|
||||
|
||||
obs_frontend_streaming_start();
|
||||
|
||||
if (_service != nullptr) {
|
||||
obs_frontend_set_streaming_service(currentService);
|
||||
}
|
||||
|
||||
req->SendOKResponse();
|
||||
obs_service_release(currentService);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -909,6 +1010,77 @@ void WSRequestHandler::HandleGetCurrentProfile(WSRequestHandler* req)
|
||||
obs_data_release(response);
|
||||
}
|
||||
|
||||
void WSRequestHandler::HandleSetStreamSettings(WSRequestHandler* req)
|
||||
{
|
||||
obs_service_t* service = obs_frontend_get_streaming_service();
|
||||
|
||||
obs_data_t* settings = obs_data_get_obj(req->data, "settings");
|
||||
if (!settings)
|
||||
{
|
||||
req->SendErrorResponse("'settings' are required'");
|
||||
return;
|
||||
}
|
||||
|
||||
const char* serviceType = obs_service_get_type(service);
|
||||
const char* requestedType = obs_data_get_string(req->data, "type");
|
||||
|
||||
if (requestedType != nullptr && strcmp(requestedType, serviceType) != 0)
|
||||
{
|
||||
obs_data_t* hotkeys = obs_hotkeys_save_service(service);
|
||||
obs_service_release(service);
|
||||
service = obs_service_create(requestedType, "websocket_custom_service", settings, hotkeys);
|
||||
obs_data_release(hotkeys);
|
||||
}
|
||||
else
|
||||
{
|
||||
obs_data_t* existingSettings = obs_service_get_settings(service); //if type isn't changing we should overlay the settings we got with the existing settings
|
||||
obs_data_t* newSettings = obs_data_create(); //by doing this you can send a request to the websocket that only contains a setting you want to change instead of having to do a get and then change them
|
||||
obs_data_apply(newSettings, existingSettings); //first apply the existing settings
|
||||
obs_data_apply(newSettings, settings); //then apply the settings from the request
|
||||
obs_data_release(settings);
|
||||
obs_data_release(existingSettings);
|
||||
obs_service_update(service, settings);
|
||||
settings = newSettings;
|
||||
}
|
||||
|
||||
if (obs_data_get_bool(req->data, "save")) //if save is specified we should immediately save the streaming service
|
||||
{
|
||||
obs_frontend_save_streaming_service();
|
||||
}
|
||||
|
||||
obs_data_t* response = obs_data_create();
|
||||
obs_data_set_string(response, "type", requestedType);
|
||||
obs_data_set_obj(response, "settings", settings);
|
||||
|
||||
req->SendOKResponse(response);
|
||||
|
||||
obs_data_release(settings);
|
||||
obs_data_release(response);
|
||||
}
|
||||
|
||||
void WSRequestHandler::HandleGetStreamSettings(WSRequestHandler* req)
|
||||
{
|
||||
obs_service_t* service = obs_frontend_get_streaming_service();
|
||||
|
||||
const char* serviceType = obs_service_get_type(service);
|
||||
obs_data_t* settings = obs_service_get_settings(service);
|
||||
|
||||
obs_data_t* response = obs_data_create();
|
||||
obs_data_set_string(response, "type", serviceType);
|
||||
obs_data_set_obj(response, "settings", settings);
|
||||
|
||||
req->SendOKResponse(response);
|
||||
|
||||
obs_data_release(settings);
|
||||
obs_data_release(response);
|
||||
}
|
||||
|
||||
void WSRequestHandler::HandleSaveStreamSettings(WSRequestHandler* req)
|
||||
{
|
||||
obs_frontend_save_streaming_service();
|
||||
req->SendOKResponse();
|
||||
}
|
||||
|
||||
void WSRequestHandler::HandleListProfiles(WSRequestHandler* req)
|
||||
{
|
||||
obs_data_array_t* profiles = Utils::GetProfiles();
|
||||
|
@ -21,6 +21,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
#define WSREQUESTHANDLER_H
|
||||
|
||||
#include <obs-frontend-api.h>
|
||||
|
||||
#include <QtWebSockets/QWebSocket>
|
||||
#include <QtWebSockets/QWebSocketServer>
|
||||
|
||||
@ -35,6 +36,7 @@ class WSRequestHandler : public QObject
|
||||
bool hasField(const char* name);
|
||||
|
||||
private:
|
||||
static obs_service_t* _service;
|
||||
QWebSocket* _client;
|
||||
const char* _messageId;
|
||||
const char* _requestType;
|
||||
@ -90,6 +92,10 @@ class WSRequestHandler : public QObject
|
||||
static void HandleGetCurrentProfile(WSRequestHandler* req);
|
||||
static void HandleListProfiles(WSRequestHandler* req);
|
||||
|
||||
static void HandleSetStreamSettings(WSRequestHandler* req);
|
||||
static void HandleGetStreamSettings(WSRequestHandler* req);
|
||||
static void HandleSaveStreamSettings(WSRequestHandler* req);
|
||||
|
||||
static void HandleSetTransitionDuration(WSRequestHandler* req);
|
||||
static void HandleGetTransitionDuration(WSRequestHandler* req);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user