Automatic function-scope OBS _release() + refactor to camelCase (#149)

- Refactor all internal variables to camelCase
- Use obs pointers with *AutoRelease classes for automatic refcounting
    - Important: some pointers require prior investigation to determine if refcounting is needed, or which 
      between OBSRef and *AutoRelease should be used.
This commit is contained in:
Stéphane L 2017-11-13 08:44:26 +01:00 committed by GitHub
parent 53936a4f76
commit 66a059ecdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 684 additions and 859 deletions

View File

@ -32,6 +32,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#define PARAM_SALT "AuthSalt"
#include "Config.h"
#include "Utils.h"
Config* Config::_instance = new Config();
@ -46,24 +47,24 @@ Config::Config() :
SettingsLoaded(false)
{
// OBS Config defaults
config_t* obs_config = obs_frontend_get_global_config();
if (obs_config) {
config_set_default_bool(obs_config,
config_t* obsConfig = obs_frontend_get_global_config();
if (obsConfig) {
config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_default_uint(obs_config,
config_set_default_uint(obsConfig,
SECTION_NAME, PARAM_PORT, ServerPort);
config_set_default_bool(obs_config,
config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
config_set_default_bool(obs_config,
config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_ALERT, AlertsEnabled);
config_set_default_bool(obs_config,
config_set_default_bool(obsConfig,
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_default_string(obs_config,
SECTION_NAME, PARAM_SECRET, Secret);
config_set_default_string(obs_config,
SECTION_NAME, PARAM_SALT, Salt);
config_set_default_string(obsConfig,
SECTION_NAME, PARAM_SECRET, qstring_data_copy(Secret));
config_set_default_string(obsConfig,
SECTION_NAME, PARAM_SALT, qstring_data_copy(Salt));
}
mbedtls_entropy_init(&entropy);
@ -79,110 +80,112 @@ Config::~Config() {
}
void Config::Load() {
config_t* obs_config = obs_frontend_get_global_config();
config_t* obsConfig = obs_frontend_get_global_config();
ServerEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_ENABLE);
ServerPort = config_get_uint(obs_config, SECTION_NAME, PARAM_PORT);
ServerEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ENABLE);
ServerPort = config_get_uint(obsConfig, SECTION_NAME, PARAM_PORT);
DebugEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_DEBUG);
AlertsEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_ALERT);
DebugEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_DEBUG);
AlertsEnabled = config_get_bool(obsConfig, SECTION_NAME, PARAM_ALERT);
AuthRequired = config_get_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED);
Secret = config_get_string(obs_config, SECTION_NAME, PARAM_SECRET);
Salt = config_get_string(obs_config, SECTION_NAME, PARAM_SALT);
AuthRequired = config_get_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED);
Secret = config_get_string(obsConfig, SECTION_NAME, PARAM_SECRET);
Salt = config_get_string(obsConfig, SECTION_NAME, PARAM_SALT);
}
void Config::Save() {
config_t* obs_config = obs_frontend_get_global_config();
config_t* obsConfig = obs_frontend_get_global_config();
config_set_bool(obs_config, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_uint(obs_config, SECTION_NAME, PARAM_PORT, ServerPort);
config_set_bool(obsConfig, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
config_set_uint(obsConfig, SECTION_NAME, PARAM_PORT, ServerPort);
config_set_bool(obs_config, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
config_set_bool(obs_config, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
config_set_bool(obsConfig, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
config_set_bool(obsConfig, SECTION_NAME, PARAM_ALERT, AlertsEnabled);
config_set_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_string(obs_config, SECTION_NAME, PARAM_SECRET, Secret);
config_set_string(obs_config, SECTION_NAME, PARAM_SALT, Salt);
config_set_bool(obsConfig, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
config_set_string(obsConfig, SECTION_NAME, PARAM_SECRET,
qstring_data_copy(Secret));
config_set_string(obsConfig, SECTION_NAME, PARAM_SALT,
qstring_data_copy(Salt));
config_save(obs_config);
config_save(obsConfig);
}
const char* Config::GenerateSalt() {
QString Config::GenerateSalt() {
// Generate 32 random chars
unsigned char* random_chars = (unsigned char*)bzalloc(32);
mbedtls_ctr_drbg_random(&rng, random_chars, 32);
unsigned char* randomChars = (unsigned char*)bzalloc(32);
mbedtls_ctr_drbg_random(&rng, randomChars, 32);
// Convert the 32 random chars to a base64 string
char* salt = (char*)bzalloc(64);
size_t salt_bytes;
size_t saltBytes;
mbedtls_base64_encode(
(unsigned char*)salt, 64, &salt_bytes,
random_chars, 32);
(unsigned char*)salt, 64, &saltBytes,
randomChars, 32);
bfree(random_chars);
bfree(randomChars);
return salt;
}
const char* Config::GenerateSecret(const char* password, const char* salt) {
QString Config::GenerateSecret(QString password, QString salt) {
// Concatenate the password and the salt
std::string passAndSalt = "";
QString passAndSalt = "";
passAndSalt += password;
passAndSalt += salt;
// Generate a SHA256 hash of the password
unsigned char* challengeHash = (unsigned char*)bzalloc(32);
mbedtls_sha256(
(unsigned char*)passAndSalt.c_str(), passAndSalt.length(),
(unsigned char*)passAndSalt.toUtf8().constData(), passAndSalt.length(),
challengeHash, 0);
// Encode SHA256 hash to Base64
char* challenge = (char*)bzalloc(64);
size_t challenge_bytes = 0;
size_t challengeBytes = 0;
mbedtls_base64_encode(
(unsigned char*)challenge, 64, &challenge_bytes,
(unsigned char*)challenge, 64, &challengeBytes,
challengeHash, 32);
bfree(challengeHash);
return challenge;
}
void Config::SetPassword(const char* password) {
const char* new_salt = GenerateSalt();
const char* new_challenge = GenerateSecret(password, new_salt);
void Config::SetPassword(QString password) {
QString newSalt = GenerateSalt();
QString newChallenge = GenerateSecret(password, newSalt);
this->Salt = new_salt;
this->Secret = new_challenge;
this->Salt = newSalt;
this->Secret = newChallenge;
}
bool Config::CheckAuth(const char* response) {
bool Config::CheckAuth(QString response) {
// Concatenate auth secret with the challenge sent to the user
std::string challengeAndResponse = "";
challengeAndResponse += this->Secret;
challengeAndResponse += this->SessionChallenge;
QString challengeAndResponse = "";
challengeAndResponse += Secret;
challengeAndResponse += SessionChallenge;
// Generate a SHA256 hash of challengeAndResponse
unsigned char* hash = (unsigned char*)bzalloc(32);
mbedtls_sha256(
(unsigned char*)challengeAndResponse.c_str(),
(unsigned char*)challengeAndResponse.toUtf8().constData(),
challengeAndResponse.length(),
hash, 0);
// Encode the SHA256 hash to Base64
char* expected_response = (char*)bzalloc(64);
char* expectedResponse = (char*)bzalloc(64);
size_t base64_size = 0;
mbedtls_base64_encode(
(unsigned char*)expected_response, 64, &base64_size,
(unsigned char*)expectedResponse, 64, &base64_size,
hash, 32);
bool authSuccess = false;
if (strcmp(expected_response, response) == 0) {
if (response == QString(expectedResponse)) {
SessionChallenge = GenerateSalt();
authSuccess = true;
}
bfree(hash);
bfree(expected_response);
bfree(expectedResponse);
return authSuccess;
}

View File

@ -19,6 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#ifndef CONFIG_H
#define CONFIG_H
#include <QString>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
@ -29,11 +31,11 @@ class Config {
void Load();
void Save();
void SetPassword(const char* password);
bool CheckAuth(const char* userChallenge);
const char* GenerateSalt();
static const char* GenerateSecret(
const char* password, const char* salt);
void SetPassword(QString password);
bool CheckAuth(QString userChallenge);
QString GenerateSalt();
static QString GenerateSecret(
QString password, QString salt);
bool ServerEnabled;
uint64_t ServerPort;
@ -42,9 +44,9 @@ class Config {
bool AlertsEnabled;
bool AuthRequired;
const char* Secret;
const char* Salt;
const char* SessionChallenge;
QString Secret;
QString Salt;
QString SessionChallenge;
bool SettingsLoaded;
static Config* Current();

130
Utils.cpp
View File

@ -28,7 +28,14 @@ with this program. If not, see <https://www.gnu.org/licenses/>
Q_DECLARE_METATYPE(OBSScene);
obs_data_array_t* string_list_to_array(char** strings, char* key) {
const char* qstring_data_copy(QString value) {
QByteArray stringData = value.toUtf8();
const char* constStringData = new const char[stringData.size()]();
memcpy((void*)constStringData, stringData.constData(), stringData.size());
return constStringData;
}
obs_data_array_t* stringListToArray(char** strings, char* key) {
if (!strings)
return obs_data_array_create();
@ -38,13 +45,11 @@ obs_data_array_t* string_list_to_array(char** strings, char* key) {
for (int i = 0; value != nullptr; i++) {
value = strings[i];
obs_data_t* item = obs_data_create();
OBSDataAutoRelease item = obs_data_create();
obs_data_set_string(item, key, value);
if (value)
obs_data_array_push_back(list, item);
obs_data_release(item);
}
return list;
@ -52,7 +57,7 @@ obs_data_array_t* string_list_to_array(char** strings, char* key) {
obs_data_array_t* Utils::GetSceneItems(obs_source_t* source) {
obs_data_array_t* items = obs_data_array_create();
obs_scene_t* scene = obs_scene_from_source(source);
OBSScene scene = obs_scene_from_source(source);
if (!scene)
return nullptr;
@ -64,10 +69,8 @@ obs_data_array_t* Utils::GetSceneItems(obs_source_t* source) {
{
obs_data_array_t* data = static_cast<obs_data_array_t*>(param);
obs_data_t* item_data = GetSceneItemData(currentItem);
obs_data_array_insert(data, 0, item_data);
obs_data_release(item_data);
OBSDataAutoRelease itemData = GetSceneItemData(currentItem);
obs_data_array_insert(data, 0, itemData);
return true;
}, items);
@ -84,9 +87,10 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
vec2 scale;
obs_sceneitem_get_scale(item, &scale);
obs_source_t* item_source = obs_sceneitem_get_source(item);
float item_width = float(obs_source_get_width(item_source));
float item_height = float(obs_source_get_height(item_source));
// obs_sceneitem_get_source doesn't increase the refcount
OBSSource itemSource = obs_sceneitem_get_source(item);
float item_width = float(obs_source_get_width(itemSource));
float item_height = float(obs_source_get_height(itemSource));
obs_data_t* data = obs_data_create();
obs_data_set_string(data, "name",
@ -106,9 +110,9 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
return data;
}
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name) {
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name) {
struct current_search {
const char* query;
QString query;
obs_sceneitem_t* result;
};
@ -116,8 +120,8 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
search.query = name;
search.result = nullptr;
obs_scene_t* scene = obs_scene_from_source(source);
if (scene == nullptr)
OBSScene scene = obs_scene_from_source(source);
if (!scene)
return nullptr;
obs_scene_enum_items(scene, [](
@ -127,10 +131,10 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
{
current_search* search = static_cast<current_search*>(param);
const char* currentItemName =
QString currentItemName =
obs_source_get_name(obs_sceneitem_get_source(currentItem));
if (strcmp(currentItemName, search->query) == 0) {
if (currentItemName == search->query) {
search->result = currentItem;
obs_sceneitem_addref(search->result);
return false;
@ -159,36 +163,36 @@ bool Utils::IsValidAlignment(const uint32_t alignment) {
return false;
}
obs_source_t* Utils::GetTransitionFromName(const char* search_name) {
obs_source_t* found_transition = NULL;
obs_source_t* Utils::GetTransitionFromName(QString searchName) {
obs_source_t* foundTransition = nullptr;
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];
QString transitionName = obs_source_get_name(transition);
const char* transition_name = obs_source_get_name(transition);
if (strcmp(transition_name, search_name) == 0)
{
found_transition = transition;
obs_source_addref(found_transition);
if (transitionName == searchName) {
foundTransition = transition;
obs_source_addref(foundTransition);
break;
}
}
obs_frontend_source_list_free(&transition_list);
return found_transition;
return foundTransition;
}
obs_source_t* Utils::GetSceneFromNameOrCurrent(const char* scene_name) {
obs_source_t* Utils::GetSceneFromNameOrCurrent(QString sceneName) {
// Both obs_frontend_get_current_scene() and obs_get_source_by_name()
// do addref on the return source, so no need to use an OBSSource helper
obs_source_t* scene = nullptr;
if (!scene_name || !strlen(scene_name))
if (sceneName.isEmpty() || sceneName.isNull())
scene = obs_frontend_get_current_scene();
else
scene = obs_get_source_by_name(scene_name);
scene = obs_get_source_by_name(sceneName.toUtf8());
return scene;
}
@ -200,40 +204,35 @@ obs_data_array_t* Utils::GetScenes() {
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_t* scene_data = GetSceneData(scene);
obs_data_array_push_back(scenes, scene_data);
obs_data_release(scene_data);
OBSDataAutoRelease sceneData = GetSceneData(scene);
obs_data_array_push_back(scenes, sceneData);
}
obs_frontend_source_list_free(&sceneList);
return scenes;
}
obs_data_t* Utils::GetSceneData(obs_source* source) {
obs_data_array_t* scene_items = GetSceneItems(source);
obs_data_t* Utils::GetSceneData(obs_source_t* source) {
OBSDataArrayAutoRelease sceneItems = 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", scene_items);
obs_data_set_array(sceneData, "sources", sceneItems);
obs_data_array_release(scene_items);
return sceneData;
}
obs_data_array_t* Utils::GetSceneCollections() {
char** scene_collections = obs_frontend_get_scene_collections();
obs_data_array_t* list = string_list_to_array(scene_collections, "sc-name");
char** sceneCollections = obs_frontend_get_scene_collections();
obs_data_array_t* list = stringListToArray(sceneCollections, "sc-name");
bfree(scene_collections);
bfree(sceneCollections);
return list;
}
obs_data_array_t* Utils::GetProfiles() {
char** profiles = obs_frontend_get_profiles();
obs_data_array_t* list = string_list_to_array(profiles, "profile-name");
obs_data_array_t* list = stringListToArray(profiles, "profile-name");
bfree(profiles);
return list;
@ -258,12 +257,11 @@ void Utils::SetTransitionDuration(int ms) {
control->setValue(ms);
}
bool Utils::SetTransitionByName(const char* transition_name) {
obs_source_t* transition = GetTransitionFromName(transition_name);
bool Utils::SetTransitionByName(QString transitionName) {
OBSSourceAutoRelease transition = GetTransitionFromName(transitionName);
if (transition) {
obs_frontend_set_current_transition(transition);
obs_source_release(transition);
return true;
} else {
return false;
@ -284,8 +282,8 @@ obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) {
if (!item)
return nullptr;
QVariant item_data = item->data(static_cast<int>(Qt::UserRole));
return item_data.value<OBSScene>();
QVariant itemData = item->data(static_cast<int>(Qt::UserRole));
return itemData.value<OBSScene>();
}
QLayout* Utils::GetPreviewLayout() {
@ -357,9 +355,9 @@ QString Utils::FormatIPAddress(QHostAddress &addr) {
const char* Utils::GetRecordingFolder() {
config_t* profile = obs_frontend_get_profile_config();
const char* outputMode = config_get_string(profile, "Output", "Mode");
QString outputMode = config_get_string(profile, "Output", "Mode");
if (strcmp(outputMode, "Advanced") == 0) {
if (outputMode == "Advanced") {
// Advanced mode
return config_get_string(profile, "AdvOut", "RecFilePath");
} else {
@ -373,9 +371,9 @@ bool Utils::SetRecordingFolder(const char* path) {
return false;
config_t* profile = obs_frontend_get_profile_config();
const char* outputMode = config_get_string(profile, "Output", "Mode");
QString outputMode = config_get_string(profile, "Output", "Mode");
if (strcmp(outputMode, "Advanced") == 0) {
if (outputMode == "Advanced") {
config_set_string(profile, "AdvOut", "RecFilePath", path);
} else {
config_set_string(profile, "SimpleOutput", "FilePath", path);
@ -442,9 +440,9 @@ QString Utils::ParseDataToQueryString(obs_data_t* data) {
return query;
}
obs_hotkey_t* Utils::FindHotkeyByName(const char* name) {
obs_hotkey_t* Utils::FindHotkeyByName(QString name) {
struct current_search {
const char* query;
QString query;
obs_hotkey_t* result;
};
@ -456,11 +454,11 @@ obs_hotkey_t* Utils::FindHotkeyByName(const char* name) {
current_search* search = static_cast<current_search*>(data);
const char* hk_name = obs_hotkey_get_name(hotkey);
if (strcmp(hk_name, search->query) == 0) {
if (hk_name == search->query) {
search->result = hotkey;
blog(LOG_INFO, "Utils::FindHotkeyByName: found %s", hk_name);
return false;
}
return true;
}, &search);
@ -469,12 +467,12 @@ obs_hotkey_t* Utils::FindHotkeyByName(const char* name) {
bool Utils::ReplayBufferEnabled() {
config_t* profile = obs_frontend_get_profile_config();
const char* outputMode = config_get_string(profile, "Output", "Mode");
QString outputMode = config_get_string(profile, "Output", "Mode");
if (strcmp(outputMode, "Simple") == 0) {
if (outputMode == "Simple") {
return config_get_bool(profile, "SimpleOutput", "RecRB");
}
else if (strcmp(outputMode, "Advanced") == 0) {
else if (outputMode == "Advanced") {
return config_get_bool(profile, "AdvOut", "RecRB");
}
@ -511,17 +509,11 @@ void Utils::StartReplayBuffer() {
}
bool Utils::IsRPHotkeySet() {
obs_output_t* rp_output = obs_frontend_get_replay_buffer_output();
obs_data_t *hotkeys = obs_hotkeys_save_output(rp_output);
obs_data_array_t *bindings = obs_data_get_array(hotkeys,
OBSOutputAutoRelease rpOutput = obs_frontend_get_replay_buffer_output();
OBSDataAutoRelease hotkeys = obs_hotkeys_save_output(rpOutput);
OBSDataArrayAutoRelease bindings = obs_data_get_array(hotkeys,
"ReplayBuffer.Save");
size_t count = obs_data_array_count(bindings);
obs_data_array_release(bindings);
obs_data_release(hotkeys);
obs_output_release(rp_output);
return (count > 0);
}

22
Utils.h
View File

@ -19,6 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#ifndef UTILS_H
#define UTILS_H
#include <stdio.h>
#include <QSpinBox>
#include <QPushButton>
#include <QLayout>
@ -26,23 +28,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QSystemTrayIcon>
#include <QHostAddress>
#include <stdio.h>
#include <obs.hpp>
#include <obs-module.h>
#include <util/config-file.h>
const char* qstring_data_copy(QString value);
class Utils {
public:
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_sceneitem_t* item);
static obs_sceneitem_t* GetSceneItemFromName(
obs_source_t* source, const char* name);
static obs_source_t* GetTransitionFromName(const char* search_name);
static obs_source_t* GetSceneFromNameOrCurrent(const char* scene_name);
obs_source_t* source, QString name);
static obs_source_t* GetTransitionFromName(QString transitionName);
static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
static bool IsValidAlignment(const uint32_t alignment);
static obs_data_array_t* GetScenes();
static obs_data_t* GetSceneData(obs_source* source);
static obs_data_t* GetSceneData(obs_source_t* source);
static obs_data_array_t* GetSceneCollections();
static obs_data_array_t* GetProfiles();
@ -51,7 +55,7 @@ class Utils {
static int GetTransitionDuration();
static void SetTransitionDuration(int ms);
static bool SetTransitionByName(const char* transition_name);
static bool SetTransitionByName(QString transitionName);
static QPushButton* GetPreviewModeButtonControl();
static QLayout* GetPreviewLayout();
@ -72,8 +76,8 @@ class Utils {
static const char* GetRecordingFolder();
static bool SetRecordingFolder(const char* path);
static QString ParseDataToQueryString(obs_data_t * data);
static obs_hotkey_t* FindHotkeyByName(const char* name);
static QString ParseDataToQueryString(obs_data_t* data);
static obs_hotkey_t* FindHotkeyByName(QString name);
static bool ReplayBufferEnabled();
static void StartReplayBuffer();
static bool IsRPHotkeySet();

View File

@ -28,42 +28,48 @@
#include "obs-websocket.h"
bool transition_is_cut(obs_source_t* transition) {
bool transitionIsCut(obs_source_t* transition) {
if (!transition)
return false;
if (obs_source_get_type(transition) == OBS_SOURCE_TYPE_TRANSITION
&& strcmp(obs_source_get_id(transition), "cut_transition") == 0) {
&& QString(obs_source_get_id(transition)) == "cut_transition") {
return true;
}
return false;
}
const char* ns_to_timestamp(uint64_t ns) {
const char* nsToTimestamp(uint64_t ns) {
uint64_t ms = ns / (1000 * 1000);
uint64_t secs = ms / 1000;
uint64_t minutes = secs / 60;
uint64_t hours_part = minutes / 60;
uint64_t minutes_part = minutes % 60;
uint64_t secs_part = secs % 60;
uint64_t ms_part = ms % 1000;
uint64_t hoursPart = minutes / 60;
uint64_t minutesPart = minutes % 60;
uint64_t secsPart = secs % 60;
uint64_t msPart = ms % 1000;
char* ts = (char*)bmalloc(64);
sprintf(ts, "%02d:%02d:%02d.%03d",
hours_part, minutes_part, secs_part, ms_part);
hoursPart, minutesPart, secsPart, msPart);
return ts;
}
void* calldata_get_ptr(const calldata_t* data, const char* name) {
void* ptr = nullptr;
calldata_get_ptr(data, name, &ptr);
return ptr;
}
WSEvents* WSEvents::Instance = nullptr;
WSEvents::WSEvents(WSServer* srv) {
_srv = srv;
obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this);
QSpinBox* duration_control = Utils::GetTransitionDurationControl();
connect(duration_control, SIGNAL(valueChanged(int)),
QSpinBox* durationControl = Utils::GetTransitionDurationControl();
connect(durationControl, SIGNAL(valueChanged(int)),
this, SLOT(TransitionDurationChanged(int)));
QTimer* statusTimer = new QTimer();
@ -78,18 +84,18 @@ WSEvents::WSEvents(WSServer* srv) {
connect(sceneList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
this, SLOT(SelectedSceneChanged(QListWidgetItem*, QListWidgetItem*)));
transition_handler = nullptr;
scene_handler = nullptr;
transitionHandler = nullptr;
sceneHandler = nullptr;
QTimer::singleShot(1000, this, SLOT(deferredInitOperations()));
Heartbeat_active = false;
HeartbeatIsActive = false;
_streaming_active = false;
_recording_active = false;
_streamingActive = false;
_recordingActive = false;
_stream_starttime = 0;
_rec_starttime = 0;
_streamStarttime = 0;
_recStarttime = 0;
}
WSEvents::~WSEvents() {
@ -97,13 +103,11 @@ WSEvents::~WSEvents() {
}
void WSEvents::deferredInitOperations() {
obs_source_t* transition = obs_frontend_get_current_transition();
OBSSourceAutoRelease transition = obs_frontend_get_current_transition();
connectTransitionSignals(transition);
obs_source_release(transition);
obs_source_t* scene = obs_frontend_get_current_scene();
OBSSourceAutoRelease scene = obs_frontend_get_current_scene();
connectSceneSignals(scene);
obs_source_release(scene);
}
void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private_data) {
@ -140,28 +144,28 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
owner->OnStreamStarting();
}
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTED) {
owner->_streaming_active = true;
owner->_streamingActive = true;
owner->OnStreamStarted();
}
else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPING) {
owner->OnStreamStopping();
}
else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPED) {
owner->_streaming_active = false;
owner->_streamingActive = false;
owner->OnStreamStopped();
}
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTING) {
owner->OnRecordingStarting();
}
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {
owner->_recording_active = true;
owner->_recordingActive = true;
owner->OnRecordingStarted();
}
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPING) {
owner->OnRecordingStopping();
}
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED) {
owner->_recording_active = false;
owner->_recordingActive = false;
owner->OnRecordingStopped();
}
else if (event == OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING) {
@ -188,93 +192,93 @@ void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private
}
void WSEvents::broadcastUpdate(const char* updateType,
obs_data_t* additionalFields = NULL) {
obs_data_t* update = obs_data_create();
obs_data_t* additionalFields = nullptr)
{
OBSDataAutoRelease update = obs_data_create();
obs_data_set_string(update, "update-type", updateType);
const char* ts = nullptr;
if (_streaming_active) {
ts = ns_to_timestamp(os_gettime_ns() - _stream_starttime);
if (_streamingActive) {
ts = nsToTimestamp(os_gettime_ns() - _streamStarttime);
obs_data_set_string(update, "stream-timecode", ts);
bfree((void*)ts);
}
if (_recording_active) {
ts = ns_to_timestamp(os_gettime_ns() - _rec_starttime);
if (_recordingActive) {
ts = nsToTimestamp(os_gettime_ns() - _recStarttime);
obs_data_set_string(update, "rec-timecode", ts);
bfree((void*)ts);
}
if (additionalFields != NULL)
if (additionalFields)
obs_data_apply(update, additionalFields);
const char *json = obs_data_get_json(update);
QString json = obs_data_get_json(update);
_srv->broadcast(json);
if (Config::Current()->DebugEnabled)
blog(LOG_DEBUG, "Update << '%s'", json);
obs_data_release(update);
if (Config::Current()->DebugEnabled)
blog(LOG_DEBUG, "Update << '%s'", json.toUtf8().constData());
}
void WSEvents::connectTransitionSignals(obs_source_t* transition) {
if (transition_handler) {
signal_handler_disconnect(transition_handler,
if (transitionHandler) {
signal_handler_disconnect(transitionHandler,
"transition_start", OnTransitionBegin, this);
}
if (!transition_is_cut(transition)) {
transition_handler = obs_source_get_signal_handler(transition);
signal_handler_connect(transition_handler,
if (!transitionIsCut(transition)) {
transitionHandler = obs_source_get_signal_handler(transition);
signal_handler_connect(transitionHandler,
"transition_start", OnTransitionBegin, this);
} else {
transition_handler = nullptr;
transitionHandler = nullptr;
}
}
void WSEvents::connectSceneSignals(obs_source_t* scene) {
if (scene_handler) {
signal_handler_disconnect(scene_handler,
if (sceneHandler) {
signal_handler_disconnect(sceneHandler,
"reorder", OnSceneReordered, this);
signal_handler_disconnect(scene_handler,
signal_handler_disconnect(sceneHandler,
"item_add", OnSceneItemAdd, this);
signal_handler_disconnect(scene_handler,
signal_handler_disconnect(sceneHandler,
"item_remove", OnSceneItemDelete, this);
signal_handler_disconnect(scene_handler,
signal_handler_disconnect(sceneHandler,
"item_visible", OnSceneItemVisibilityChanged, this);
}
// TODO : connect to all scenes, not just the current one.
scene_handler = obs_source_get_signal_handler(scene);
signal_handler_connect(scene_handler,
sceneHandler = obs_source_get_signal_handler(scene);
signal_handler_connect(sceneHandler,
"reorder", OnSceneReordered, this);
signal_handler_connect(scene_handler,
signal_handler_connect(sceneHandler,
"item_add", OnSceneItemAdd, this);
signal_handler_connect(scene_handler,
signal_handler_connect(sceneHandler,
"item_remove", OnSceneItemDelete, this);
signal_handler_connect(scene_handler,
signal_handler_connect(sceneHandler,
"item_visible", OnSceneItemVisibilityChanged, this);
}
uint64_t WSEvents::GetStreamingTime() {
if (_streaming_active)
return (os_gettime_ns() - _stream_starttime);
if (_streamingActive)
return (os_gettime_ns() - _streamStarttime);
else
return 0;
}
const char* WSEvents::GetStreamingTimecode() {
return ns_to_timestamp(GetStreamingTime());
return nsToTimestamp(GetStreamingTime());
}
uint64_t WSEvents::GetRecordingTime() {
if (_recording_active)
return (os_gettime_ns() - _rec_starttime);
if (_recordingActive)
return (os_gettime_ns() - _recStarttime);
else
return 0;
}
const char* WSEvents::GetRecordingTimecode() {
return ns_to_timestamp(GetRecordingTime());
return nsToTimestamp(GetRecordingTime());
}
/**
@ -289,21 +293,16 @@ const char* WSEvents::GetRecordingTimecode() {
* @since 0.3
*/
void WSEvents::OnSceneChange() {
obs_data_t* data = obs_data_create();
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(currentScene);
connectSceneSignals(currentScene);
obs_source_t* current_scene = obs_frontend_get_current_scene();
obs_data_array_t* scene_items = Utils::GetSceneItems(current_scene);
connectSceneSignals(current_scene);
obs_data_set_string(data, "scene-name", obs_source_get_name(current_scene));
obs_data_set_array(data, "sources", scene_items);
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "scene-name", obs_source_get_name(currentScene));
obs_data_set_array(data, "sources", sceneItems);
broadcastUpdate("SwitchScenes", data);
obs_data_array_release(scene_items);
obs_source_release(current_scene);
obs_data_release(data);
// Dirty fix : OBS blocks signals when swapping scenes in Studio Mode
// after transition end, so SelectedSceneChanged is never called...
if (obs_frontend_preview_program_mode_active()) {
@ -336,8 +335,8 @@ void WSEvents::OnSceneListChange() {
void WSEvents::OnSceneCollectionChange() {
broadcastUpdate("SceneCollectionChanged");
scene_handler = nullptr;
transition_handler = nullptr;
sceneHandler = nullptr;
transitionHandler = nullptr;
OnTransitionListChange();
OnTransitionChange();
@ -369,17 +368,14 @@ void WSEvents::OnSceneCollectionListChange() {
* @since 4.0.0
*/
void WSEvents::OnTransitionChange() {
obs_source_t* current_transition = obs_frontend_get_current_transition();
connectTransitionSignals(current_transition);
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
connectTransitionSignals(currentTransition);
obs_data_t* data = obs_data_create();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "transition-name",
obs_source_get_name(current_transition));
obs_source_get_name(currentTransition));
broadcastUpdate("SwitchTransition", data);
obs_data_release(data);
obs_source_release(current_transition);
}
/**
@ -430,12 +426,10 @@ void WSEvents::OnProfileListChange() {
* @since 0.3
*/
void WSEvents::OnStreamStarting() {
obs_data_t* data = obs_data_create();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "preview-only", false);
broadcastUpdate("StreamStarting", data);
obs_data_release(data);
}
/**
@ -447,7 +441,7 @@ void WSEvents::OnStreamStarting() {
* @since 0.3
*/
void WSEvents::OnStreamStarted() {
_stream_starttime = os_gettime_ns();
_streamStarttime = os_gettime_ns();
_lastBytesSent = 0;
broadcastUpdate("StreamStarted");
}
@ -463,12 +457,10 @@ void WSEvents::OnStreamStarted() {
* @since 0.3
*/
void WSEvents::OnStreamStopping() {
obs_data_t* data = obs_data_create();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "preview-only", false);
broadcastUpdate("StreamStopping", data);
obs_data_release(data);
}
/**
@ -480,7 +472,7 @@ void WSEvents::OnStreamStopping() {
* @since 0.3
*/
void WSEvents::OnStreamStopped() {
_stream_starttime = 0;
_streamStarttime = 0;
broadcastUpdate("StreamStopped");
}
@ -505,7 +497,7 @@ void WSEvents::OnRecordingStarting() {
* @since 0.3
*/
void WSEvents::OnRecordingStarted() {
_rec_starttime = os_gettime_ns();
_recStarttime = os_gettime_ns();
broadcastUpdate("RecordingStarted");
}
@ -530,7 +522,7 @@ void WSEvents::OnRecordingStopping() {
* @since 0.3
*/
void WSEvents::OnRecordingStopped() {
_rec_starttime = 0;
_recStarttime = 0;
broadcastUpdate("RecordingStopped");
}
@ -614,60 +606,54 @@ void WSEvents::OnExit() {
* @since 0.3
*/
void WSEvents::StreamStatus() {
bool streaming_active = obs_frontend_streaming_active();
bool recording_active = obs_frontend_recording_active();
bool streamingActive = obs_frontend_streaming_active();
bool recordingActive = obs_frontend_recording_active();
obs_output_t* stream_output = obs_frontend_get_streaming_output();
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
if (!stream_output || !streaming_active) {
if (stream_output) {
obs_output_release(stream_output);
}
if (!streamOutput || !streamingActive) {
return;
}
uint64_t bytes_sent = obs_output_get_total_bytes(stream_output);
uint64_t bytes_sent_time = os_gettime_ns();
uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
uint64_t bytesSentTime = os_gettime_ns();
if (bytes_sent < _lastBytesSent)
bytes_sent = 0;
if (bytesSent < _lastBytesSent)
bytesSent = 0;
if (bytes_sent == 0)
if (bytesSent == 0)
_lastBytesSent = 0;
uint64_t bytes_between = bytes_sent - _lastBytesSent;
double time_passed =
double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
uint64_t bytesBetween = bytesSent - _lastBytesSent;
double timePassed =
double(bytesSentTime - _lastBytesSentTime) / 1000000000.0;
uint64_t bytes_per_sec = bytes_between / time_passed;
uint64_t bytesPerSec = bytesBetween / timePassed;
_lastBytesSent = bytes_sent;
_lastBytesSentTime = bytes_sent_time;
_lastBytesSent = bytesSent;
_lastBytesSentTime = bytesSentTime;
uint64_t totalStreamTime =
(os_gettime_ns() - _stream_starttime) / 1000000000;
(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);
int totalFrames = obs_output_get_total_frames(streamOutput);
int droppedFrames = obs_output_get_frames_dropped(streamOutput);
float strain = obs_output_get_congestion(stream_output);
float strain = obs_output_get_congestion(streamOutput);
obs_data_t* data = obs_data_create();
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);
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "streaming", streamingActive);
obs_data_set_bool(data, "recording", recordingActive);
obs_data_set_int(data, "bytes-per-sec", bytesPerSec);
obs_data_set_int(data, "kbits-per-sec", (bytesPerSec * 8) / 1024);
obs_data_set_int(data, "total-stream-time", totalStreamTime);
obs_data_set_int(data, "num-total-frames", total_frames);
obs_data_set_int(data, "num-dropped-frames", dropped_frames);
obs_data_set_int(data, "num-total-frames", totalFrames);
obs_data_set_int(data, "num-dropped-frames", droppedFrames);
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(stream_output);
}
/**
@ -691,44 +677,40 @@ void WSEvents::StreamStatus() {
*/
void WSEvents::Heartbeat() {
if (!Heartbeat_active) return;
if (!HeartbeatIsActive) return;
bool streaming_active = obs_frontend_streaming_active();
bool recording_active = obs_frontend_recording_active();
obs_data_t* data = obs_data_create();
obs_output_t* record_output = obs_frontend_get_recording_output();
obs_output_t* stream_output = obs_frontend_get_streaming_output();
bool streamingActive = obs_frontend_streaming_active();
bool recordingActive = obs_frontend_recording_active();
OBSDataAutoRelease data = obs_data_create();
OBSOutputAutoRelease recordOutput = obs_frontend_get_recording_output();
OBSOutputAutoRelease streamOutput = obs_frontend_get_streaming_output();
pulse = !pulse;
obs_data_set_bool(data, "pulse", pulse);
obs_data_set_string(data, "current-profile", obs_frontend_get_current_profile());
obs_source_t* current_scene = obs_frontend_get_current_scene();
const char* name = obs_source_get_name(current_scene);
obs_source_release(current_scene);
obs_data_set_string(data, "current-scene", name);
OBSSourceAutoRelease currentScene = obs_frontend_get_current_scene();
obs_data_set_string(data, "current-scene", obs_source_get_name(currentScene));
obs_data_set_bool(data, "streaming", streaming_active);
if (streaming_active) {
uint64_t totalStreamTime = (os_gettime_ns() - _stream_starttime) / 1000000000;
obs_data_set_bool(data, "streaming", streamingActive);
if (streamingActive) {
uint64_t totalStreamTime = (os_gettime_ns() - _streamStarttime) / 1000000000;
obs_data_set_int(data, "total-stream-time", totalStreamTime);
obs_data_set_int(data, "total-stream-bytes", (uint64_t)obs_output_get_total_bytes(stream_output));
obs_data_set_int(data, "total-stream-frames", obs_output_get_total_frames(stream_output));
obs_data_set_int(data, "total-stream-bytes", (uint64_t)obs_output_get_total_bytes(streamOutput));
obs_data_set_int(data, "total-stream-frames", obs_output_get_total_frames(streamOutput));
}
obs_data_set_bool(data, "recording", recording_active);
if (recording_active) {
uint64_t totalRecordTime = (os_gettime_ns() - _rec_starttime) / 1000000000;
obs_data_set_bool(data, "recording", recordingActive);
if (recordingActive) {
uint64_t totalRecordTime = (os_gettime_ns() - _recStarttime) / 1000000000;
obs_data_set_int(data, "total-record-time", totalRecordTime);
obs_data_set_int(data, "total-record-bytes", (uint64_t)obs_output_get_total_bytes(record_output));
obs_data_set_int(data, "total-record-frames", obs_output_get_total_frames(record_output));
obs_data_set_int(data, "total-record-bytes", (uint64_t)obs_output_get_total_bytes(recordOutput));
obs_data_set_int(data, "total-record-frames", obs_output_get_total_frames(recordOutput));
}
broadcastUpdate("Heartbeat", data);
obs_data_release(data);
obs_output_release(record_output);
obs_output_release(stream_output);
}
/**
@ -742,11 +724,10 @@ void WSEvents::Heartbeat() {
* @since 4.0.0
*/
void WSEvents::TransitionDurationChanged(int ms) {
obs_data_t* fields = obs_data_create();
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_int(fields, "new-duration", ms);
broadcastUpdate("TransitionDurationChanged", fields);
obs_data_release(fields);
}
/**
@ -764,17 +745,13 @@ void WSEvents::OnTransitionBegin(void* param, calldata_t* data) {
UNUSED_PARAMETER(data);
WSEvents* instance = static_cast<WSEvents*>(param);
obs_source_t* current_transition = obs_frontend_get_current_transition();
const char* name = obs_source_get_name(current_transition);
int duration = Utils::GetTransitionDuration();
OBSSourceAutoRelease currentTransition = obs_frontend_get_current_transition();
obs_data_t* fields = obs_data_create();
obs_data_set_string(fields, "name", name);
obs_data_set_int(fields, "duration", duration);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "name", obs_source_get_name(currentTransition));
obs_data_set_int(fields, "duration", Utils::GetTransitionDuration());
instance->broadcastUpdate("TransitionBegin", fields);
obs_data_release(fields);
obs_source_release(current_transition);
}
/**
@ -793,12 +770,11 @@ void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
obs_scene_t* scene = nullptr;
calldata_get_ptr(data, "scene", &scene);
obs_data_t* fields = obs_data_create();
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "scene-name",
obs_source_get_name(obs_scene_get_source(scene)));
instance->broadcastUpdate("SourceOrderChanged", fields);
obs_data_release(fields);
}
/**
@ -818,20 +794,19 @@ void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
obs_scene_t* scene = nullptr;
calldata_get_ptr(data, "scene", &scene);
obs_sceneitem_t* scene_item = nullptr;
calldata_get_ptr(data, "item", &scene_item);
obs_sceneitem_t* sceneItem = nullptr;
calldata_get_ptr(data, "item", &sceneItem);
const char* scene_name =
const char* sceneName =
obs_source_get_name(obs_scene_get_source(scene));
const char* sceneitem_name =
obs_source_get_name(obs_sceneitem_get_source(scene_item));
const char* sceneItemName =
obs_source_get_name(obs_sceneitem_get_source(sceneItem));
obs_data_t* fields = obs_data_create();
obs_data_set_string(fields, "scene-name", scene_name);
obs_data_set_string(fields, "item-name", sceneitem_name);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "scene-name", sceneName);
obs_data_set_string(fields, "item-name", sceneItemName);
instance->broadcastUpdate("SceneItemAdded", fields);
obs_data_release(fields);
}
/**
@ -851,20 +826,19 @@ void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
obs_scene_t* scene = nullptr;
calldata_get_ptr(data, "scene", &scene);
obs_sceneitem_t* scene_item = nullptr;
calldata_get_ptr(data, "item", &scene_item);
obs_sceneitem_t* sceneItem = nullptr;
calldata_get_ptr(data, "item", &sceneItem);
const char* scene_name =
const char* sceneName =
obs_source_get_name(obs_scene_get_source(scene));
const char* sceneitem_name =
obs_source_get_name(obs_sceneitem_get_source(scene_item));
const char* sceneItemName =
obs_source_get_name(obs_sceneitem_get_source(sceneItem));
obs_data_t* fields = obs_data_create();
obs_data_set_string(fields, "scene-name", scene_name);
obs_data_set_string(fields, "item-name", sceneitem_name);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "scene-name", sceneName);
obs_data_set_string(fields, "item-name", sceneItemName);
instance->broadcastUpdate("SceneItemRemoved", fields);
obs_data_release(fields);
}
/**
@ -885,24 +859,23 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
obs_scene_t* scene = nullptr;
calldata_get_ptr(data, "scene", &scene);
obs_sceneitem_t* scene_item = nullptr;
calldata_get_ptr(data, "item", &scene_item);
obs_sceneitem_t* sceneItem = nullptr;
calldata_get_ptr(data, "item", &sceneItem);
bool visible = false;
calldata_get_bool(data, "visible", &visible);
const char* scene_name =
const char* sceneName =
obs_source_get_name(obs_scene_get_source(scene));
const char* sceneitem_name =
obs_source_get_name(obs_sceneitem_get_source(scene_item));
const char* sceneItemName =
obs_source_get_name(obs_sceneitem_get_source(sceneItem));
obs_data_t* fields = obs_data_create();
obs_data_set_string(fields, "scene-name", scene_name);
obs_data_set_string(fields, "item-name", sceneitem_name);
OBSDataAutoRelease fields = obs_data_create();
obs_data_set_string(fields, "scene-name", sceneName);
obs_data_set_string(fields, "item-name", sceneItemName);
obs_data_set_bool(fields, "item-visible", visible);
instance->broadcastUpdate("SceneItemVisibilityChanged", fields);
obs_data_release(fields);
}
/**
@ -918,20 +891,18 @@ void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
*/
void WSEvents::SelectedSceneChanged(QListWidgetItem* current, QListWidgetItem* prev) {
if (obs_frontend_preview_program_mode_active()) {
obs_scene_t* scene = Utils::SceneListItemToScene(current);
if (!scene) return;
OBSScene scene = Utils::SceneListItemToScene(current);
if (!scene)
return;
obs_source_t* scene_source = obs_scene_get_source(scene);
obs_data_array_t* scene_items = Utils::GetSceneItems(scene_source);
OBSSource sceneSource = obs_scene_get_source(scene);
OBSDataArrayAutoRelease sceneItems = Utils::GetSceneItems(sceneSource);
obs_data_t* data = obs_data_create();
obs_data_set_string(data, "scene-name", obs_source_get_name(scene_source));
obs_data_set_array(data, "sources", scene_items);
OBSDataAutoRelease data = obs_data_create();
obs_data_set_string(data, "scene-name", obs_source_get_name(sceneSource));
obs_data_set_array(data, "sources", sceneItems);
broadcastUpdate("PreviewSceneChanged", data);
obs_data_array_release(scene_items);
obs_data_release(data);
}
}
@ -946,9 +917,8 @@ void WSEvents::SelectedSceneChanged(QListWidgetItem* current, QListWidgetItem* p
* @since 4.1.0
*/
void WSEvents::OnStudioModeSwitched(bool checked) {
obs_data_t* data = obs_data_create();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "new-state", checked);
broadcastUpdate("StudioModeSwitched", data);
obs_data_release(data);
}

View File

@ -20,6 +20,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#ifndef WSEVENTS_H
#define WSEVENTS_H
#include <obs.hpp>
#include <obs-frontend-api.h>
#include <QListWidgetItem>
#include "WSServer.h"
@ -30,7 +31,7 @@ class WSEvents : public QObject {
explicit WSEvents(WSServer* srv);
~WSEvents();
static void FrontendEventHandler(
enum obs_frontend_event event, void* private_data);
enum obs_frontend_event event, void* privateData);
static WSEvents* Instance;
void connectTransitionSignals(obs_source_t* transition);
void connectSceneSignals(obs_source_t* scene);
@ -40,7 +41,7 @@ class WSEvents : public QObject {
uint64_t GetRecordingTime();
const char* GetRecordingTimecode();
bool Heartbeat_active;
bool HeartbeatIsActive;
private slots:
void deferredInitOperations();
@ -52,16 +53,16 @@ class WSEvents : public QObject {
private:
WSServer* _srv;
signal_handler_t* transition_handler;
signal_handler_t* scene_handler;
signal_handler_t* transitionHandler;
signal_handler_t* sceneHandler;
bool pulse;
bool _streaming_active;
bool _recording_active;
bool _streamingActive;
bool _recordingActive;
uint64_t _stream_starttime;
uint64_t _rec_starttime;
uint64_t _streamStarttime;
uint64_t _recStarttime;
uint64_t _lastBytesSent;
uint64_t _lastBytesSentTime;

File diff suppressed because it is too large Load Diff

View File

@ -24,8 +24,11 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QWebSocket>
#include <QWebSocketServer>
#include <obs.hpp>
#include <obs-frontend-api.h>
#include "obs-websocket.h"
class WSRequestHandler : public QObject {
Q_OBJECT
@ -33,13 +36,13 @@ class WSRequestHandler : public QObject {
explicit WSRequestHandler(QWebSocket* client);
~WSRequestHandler();
void processIncomingMessage(QString textMessage);
bool hasField(const char* name);
bool hasField(QString name);
private:
QWebSocket* _client;
const char* _messageId;
const char* _requestType;
obs_data_t* data;
OBSDataAutoRelease data;
void SendOKResponse(obs_data_t* additionalFields = NULL);
void SendErrorResponse(const char* errorMessage);

View File

@ -77,17 +77,12 @@ void SettingsDialog::FormAccepted() {
conf->DebugEnabled = ui->debugEnabled->isChecked();
conf->AlertsEnabled = ui->alertsEnabled->isChecked();
if (ui->authRequired->isChecked())
{
if (ui->password->text() != CHANGE_ME)
{
QByteArray pwd = ui->password->text().toUtf8();
const char *new_password = pwd;
conf->SetPassword(new_password);
if (ui->authRequired->isChecked()) {
if (ui->password->text() != CHANGE_ME) {
conf->SetPassword(ui->password->text());
}
if (strcmp(Config::Current()->Secret, "") != 0)
if (!Config::Current()->Secret.isEmpty())
conf->AuthRequired = true;
else
conf->AuthRequired = false;

View File

@ -28,6 +28,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Config.h"
#include "forms/settings-dialog.h"
void ___source_dummy_addref(obs_source_t*) {}
void ___sceneitem_dummy_addref(obs_sceneitem_t*) {}
void ___data_dummy_addref(obs_data_t*) {}
void ___data_array_dummy_addref(obs_data_array_t*) {}
void ___output_dummy_addref(obs_output_t*) {}
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")

View File

@ -19,6 +19,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#ifndef OBSWEBSOCKET_H
#define OBSWEBSOCKET_H
#include <obs.hpp>
void ___source_dummy_addref(obs_source_t*);
void ___sceneitem_dummy_addref(obs_sceneitem_t*);
void ___data_dummy_addref(obs_data_t*);
void ___data_array_dummy_addref(obs_data_array_t*);
void ___output_dummy_addref(obs_output_t*);
using OBSSourceAutoRelease =
OBSRef<obs_source_t*, ___source_dummy_addref, obs_source_release>;
using OBSSceneItemAutoRelease =
OBSRef<obs_sceneitem_t*, ___sceneitem_dummy_addref, obs_sceneitem_release>;
using OBSDataAutoRelease =
OBSRef<obs_data_t*, ___data_dummy_addref, obs_data_release>;
using OBSDataArrayAutoRelease =
OBSRef<obs_data_array_t*, ___data_array_dummy_addref, obs_data_array_release>;
using OBSOutputAutoRelease =
OBSRef<obs_output_t*, ___output_dummy_addref, obs_output_release>;
#define PROP_AUTHENTICATED "wsclient_authenticated"
#define OBS_WEBSOCKET_VERSION "4.2.1"