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

View File

@ -19,6 +19,8 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#ifndef CONFIG_H #ifndef CONFIG_H
#define CONFIG_H #define CONFIG_H
#include <QString>
#include <mbedtls/entropy.h> #include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h> #include <mbedtls/ctr_drbg.h>
@ -29,11 +31,11 @@ class Config {
void Load(); void Load();
void Save(); void Save();
void SetPassword(const char* password); void SetPassword(QString password);
bool CheckAuth(const char* userChallenge); bool CheckAuth(QString userChallenge);
const char* GenerateSalt(); QString GenerateSalt();
static const char* GenerateSecret( static QString GenerateSecret(
const char* password, const char* salt); QString password, QString salt);
bool ServerEnabled; bool ServerEnabled;
uint64_t ServerPort; uint64_t ServerPort;
@ -42,9 +44,9 @@ class Config {
bool AlertsEnabled; bool AlertsEnabled;
bool AuthRequired; bool AuthRequired;
const char* Secret; QString Secret;
const char* Salt; QString Salt;
const char* SessionChallenge; QString SessionChallenge;
bool SettingsLoaded; bool SettingsLoaded;
static Config* Current(); 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); 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) if (!strings)
return obs_data_array_create(); 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++) { for (int i = 0; value != nullptr; i++) {
value = strings[i]; value = strings[i];
obs_data_t* item = obs_data_create(); OBSDataAutoRelease item = obs_data_create();
obs_data_set_string(item, key, value); obs_data_set_string(item, key, value);
if (value) if (value)
obs_data_array_push_back(list, item); obs_data_array_push_back(list, item);
obs_data_release(item);
} }
return list; 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* Utils::GetSceneItems(obs_source_t* source) {
obs_data_array_t* items = obs_data_array_create(); 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) if (!scene)
return nullptr; 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_array_t* data = static_cast<obs_data_array_t*>(param);
obs_data_t* item_data = GetSceneItemData(currentItem); OBSDataAutoRelease itemData = GetSceneItemData(currentItem);
obs_data_array_insert(data, 0, item_data); obs_data_array_insert(data, 0, itemData);
obs_data_release(item_data);
return true; return true;
}, items); }, items);
@ -84,9 +87,10 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
vec2 scale; vec2 scale;
obs_sceneitem_get_scale(item, &scale); obs_sceneitem_get_scale(item, &scale);
obs_source_t* item_source = obs_sceneitem_get_source(item); // obs_sceneitem_get_source doesn't increase the refcount
float item_width = float(obs_source_get_width(item_source)); OBSSource itemSource = obs_sceneitem_get_source(item);
float item_height = float(obs_source_get_height(item_source)); 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_t* data = obs_data_create();
obs_data_set_string(data, "name", obs_data_set_string(data, "name",
@ -106,9 +110,9 @@ obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
return data; return data;
} }
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name) { obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, QString name) {
struct current_search { struct current_search {
const char* query; QString query;
obs_sceneitem_t* result; obs_sceneitem_t* result;
}; };
@ -116,8 +120,8 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
search.query = name; search.query = name;
search.result = nullptr; search.result = nullptr;
obs_scene_t* scene = obs_scene_from_source(source); OBSScene scene = obs_scene_from_source(source);
if (scene == nullptr) if (!scene)
return nullptr; return nullptr;
obs_scene_enum_items(scene, []( 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); current_search* search = static_cast<current_search*>(param);
const char* currentItemName = QString currentItemName =
obs_source_get_name(obs_sceneitem_get_source(currentItem)); obs_source_get_name(obs_sceneitem_get_source(currentItem));
if (strcmp(currentItemName, search->query) == 0) { if (currentItemName == search->query) {
search->result = currentItem; search->result = currentItem;
obs_sceneitem_addref(search->result); obs_sceneitem_addref(search->result);
return false; return false;
@ -159,36 +163,36 @@ bool Utils::IsValidAlignment(const uint32_t alignment) {
return false; return false;
} }
obs_source_t* Utils::GetTransitionFromName(const char* search_name) { obs_source_t* Utils::GetTransitionFromName(QString searchName) {
obs_source_t* found_transition = NULL; obs_source_t* foundTransition = nullptr;
obs_frontend_source_list transition_list = {}; obs_frontend_source_list transition_list = {};
obs_frontend_get_transitions(&transition_list); obs_frontend_get_transitions(&transition_list);
for (size_t i = 0; i < transition_list.sources.num; i++) { for (size_t i = 0; i < transition_list.sources.num; i++) {
obs_source_t* transition = transition_list.sources.array[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 (transitionName == searchName) {
if (strcmp(transition_name, search_name) == 0) foundTransition = transition;
{ obs_source_addref(foundTransition);
found_transition = transition;
obs_source_addref(found_transition);
break; break;
} }
} }
obs_frontend_source_list_free(&transition_list); obs_frontend_source_list_free(&transition_list);
return foundTransition;
return found_transition;
} }
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; obs_source_t* scene = nullptr;
if (!scene_name || !strlen(scene_name)) if (sceneName.isEmpty() || sceneName.isNull())
scene = obs_frontend_get_current_scene(); scene = obs_frontend_get_current_scene();
else else
scene = obs_get_source_by_name(scene_name); scene = obs_get_source_by_name(sceneName.toUtf8());
return scene; return scene;
} }
@ -200,40 +204,35 @@ obs_data_array_t* Utils::GetScenes() {
obs_data_array_t* scenes = obs_data_array_create(); obs_data_array_t* scenes = obs_data_array_create();
for (size_t i = 0; i < sceneList.sources.num; i++) { for (size_t i = 0; i < sceneList.sources.num; i++) {
obs_source_t* scene = sceneList.sources.array[i]; obs_source_t* scene = sceneList.sources.array[i];
OBSDataAutoRelease sceneData = GetSceneData(scene);
obs_data_t* scene_data = GetSceneData(scene); obs_data_array_push_back(scenes, sceneData);
obs_data_array_push_back(scenes, scene_data);
obs_data_release(scene_data);
} }
obs_frontend_source_list_free(&sceneList); obs_frontend_source_list_free(&sceneList);
return scenes; return scenes;
} }
obs_data_t* Utils::GetSceneData(obs_source* source) { obs_data_t* Utils::GetSceneData(obs_source_t* source) {
obs_data_array_t* scene_items = GetSceneItems(source); OBSDataArrayAutoRelease sceneItems = GetSceneItems(source);
obs_data_t* sceneData = obs_data_create(); obs_data_t* sceneData = obs_data_create();
obs_data_set_string(sceneData, "name", obs_source_get_name(source)); obs_data_set_string(sceneData, "name", obs_source_get_name(source));
obs_data_set_array(sceneData, "sources", scene_items); obs_data_set_array(sceneData, "sources", sceneItems);
obs_data_array_release(scene_items);
return sceneData; return sceneData;
} }
obs_data_array_t* Utils::GetSceneCollections() { obs_data_array_t* Utils::GetSceneCollections() {
char** scene_collections = obs_frontend_get_scene_collections(); char** sceneCollections = obs_frontend_get_scene_collections();
obs_data_array_t* list = string_list_to_array(scene_collections, "sc-name"); obs_data_array_t* list = stringListToArray(sceneCollections, "sc-name");
bfree(scene_collections); bfree(sceneCollections);
return list; return list;
} }
obs_data_array_t* Utils::GetProfiles() { obs_data_array_t* Utils::GetProfiles() {
char** profiles = obs_frontend_get_profiles(); 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); bfree(profiles);
return list; return list;
@ -258,12 +257,11 @@ void Utils::SetTransitionDuration(int ms) {
control->setValue(ms); control->setValue(ms);
} }
bool Utils::SetTransitionByName(const char* transition_name) { bool Utils::SetTransitionByName(QString transitionName) {
obs_source_t* transition = GetTransitionFromName(transition_name); OBSSourceAutoRelease transition = GetTransitionFromName(transitionName);
if (transition) { if (transition) {
obs_frontend_set_current_transition(transition); obs_frontend_set_current_transition(transition);
obs_source_release(transition);
return true; return true;
} else { } else {
return false; return false;
@ -284,8 +282,8 @@ obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) {
if (!item) if (!item)
return nullptr; return nullptr;
QVariant item_data = item->data(static_cast<int>(Qt::UserRole)); QVariant itemData = item->data(static_cast<int>(Qt::UserRole));
return item_data.value<OBSScene>(); return itemData.value<OBSScene>();
} }
QLayout* Utils::GetPreviewLayout() { QLayout* Utils::GetPreviewLayout() {
@ -357,9 +355,9 @@ QString Utils::FormatIPAddress(QHostAddress &addr) {
const char* Utils::GetRecordingFolder() { const char* Utils::GetRecordingFolder() {
config_t* profile = obs_frontend_get_profile_config(); 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 // Advanced mode
return config_get_string(profile, "AdvOut", "RecFilePath"); return config_get_string(profile, "AdvOut", "RecFilePath");
} else { } else {
@ -373,9 +371,9 @@ bool Utils::SetRecordingFolder(const char* path) {
return false; return false;
config_t* profile = obs_frontend_get_profile_config(); 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); config_set_string(profile, "AdvOut", "RecFilePath", path);
} else { } else {
config_set_string(profile, "SimpleOutput", "FilePath", path); config_set_string(profile, "SimpleOutput", "FilePath", path);
@ -442,9 +440,9 @@ QString Utils::ParseDataToQueryString(obs_data_t* data) {
return query; return query;
} }
obs_hotkey_t* Utils::FindHotkeyByName(const char* name) { obs_hotkey_t* Utils::FindHotkeyByName(QString name) {
struct current_search { struct current_search {
const char* query; QString query;
obs_hotkey_t* result; obs_hotkey_t* result;
}; };
@ -456,11 +454,11 @@ obs_hotkey_t* Utils::FindHotkeyByName(const char* name) {
current_search* search = static_cast<current_search*>(data); current_search* search = static_cast<current_search*>(data);
const char* hk_name = obs_hotkey_get_name(hotkey); const char* hk_name = obs_hotkey_get_name(hotkey);
if (strcmp(hk_name, search->query) == 0) { if (hk_name == search->query) {
search->result = hotkey; search->result = hotkey;
blog(LOG_INFO, "Utils::FindHotkeyByName: found %s", hk_name);
return false; return false;
} }
return true; return true;
}, &search); }, &search);
@ -469,12 +467,12 @@ obs_hotkey_t* Utils::FindHotkeyByName(const char* name) {
bool Utils::ReplayBufferEnabled() { bool Utils::ReplayBufferEnabled() {
config_t* profile = obs_frontend_get_profile_config(); 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"); return config_get_bool(profile, "SimpleOutput", "RecRB");
} }
else if (strcmp(outputMode, "Advanced") == 0) { else if (outputMode == "Advanced") {
return config_get_bool(profile, "AdvOut", "RecRB"); return config_get_bool(profile, "AdvOut", "RecRB");
} }
@ -511,17 +509,11 @@ void Utils::StartReplayBuffer() {
} }
bool Utils::IsRPHotkeySet() { bool Utils::IsRPHotkeySet() {
obs_output_t* rp_output = obs_frontend_get_replay_buffer_output(); OBSOutputAutoRelease rpOutput = obs_frontend_get_replay_buffer_output();
OBSDataAutoRelease hotkeys = obs_hotkeys_save_output(rpOutput);
obs_data_t *hotkeys = obs_hotkeys_save_output(rp_output); OBSDataArrayAutoRelease bindings = obs_data_get_array(hotkeys,
obs_data_array_t *bindings = obs_data_get_array(hotkeys,
"ReplayBuffer.Save"); "ReplayBuffer.Save");
size_t count = obs_data_array_count(bindings); 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); 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 #ifndef UTILS_H
#define UTILS_H #define UTILS_H
#include <stdio.h>
#include <QSpinBox> #include <QSpinBox>
#include <QPushButton> #include <QPushButton>
#include <QLayout> #include <QLayout>
@ -26,23 +28,25 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QHostAddress> #include <QHostAddress>
#include <stdio.h> #include <obs.hpp>
#include <obs-module.h> #include <obs-module.h>
#include <util/config-file.h> #include <util/config-file.h>
const char* qstring_data_copy(QString value);
class Utils { class Utils {
public: public:
static obs_data_array_t* GetSceneItems(obs_source_t* source); static obs_data_array_t* GetSceneItems(obs_source_t* source);
static obs_data_t* GetSceneItemData(obs_scene_item* item); static obs_data_t* GetSceneItemData(obs_sceneitem_t* item);
static obs_sceneitem_t* GetSceneItemFromName( static obs_sceneitem_t* GetSceneItemFromName(
obs_source_t* source, const char* name); obs_source_t* source, QString name);
static obs_source_t* GetTransitionFromName(const char* search_name); static obs_source_t* GetTransitionFromName(QString transitionName);
static obs_source_t* GetSceneFromNameOrCurrent(const char* scene_name); static obs_source_t* GetSceneFromNameOrCurrent(QString sceneName);
static bool IsValidAlignment(const uint32_t alignment); static bool IsValidAlignment(const uint32_t alignment);
static obs_data_array_t* GetScenes(); static obs_data_array_t* GetScenes();
static obs_data_t* GetSceneData(obs_source* source); static obs_data_t* GetSceneData(obs_source_t* source);
static obs_data_array_t* GetSceneCollections(); static obs_data_array_t* GetSceneCollections();
static obs_data_array_t* GetProfiles(); static obs_data_array_t* GetProfiles();
@ -51,7 +55,7 @@ class Utils {
static int GetTransitionDuration(); static int GetTransitionDuration();
static void SetTransitionDuration(int ms); static void SetTransitionDuration(int ms);
static bool SetTransitionByName(const char* transition_name); static bool SetTransitionByName(QString transitionName);
static QPushButton* GetPreviewModeButtonControl(); static QPushButton* GetPreviewModeButtonControl();
static QLayout* GetPreviewLayout(); static QLayout* GetPreviewLayout();
@ -72,8 +76,8 @@ class Utils {
static const char* GetRecordingFolder(); static const char* GetRecordingFolder();
static bool SetRecordingFolder(const char* path); static bool SetRecordingFolder(const char* path);
static QString ParseDataToQueryString(obs_data_t * data); static QString ParseDataToQueryString(obs_data_t* data);
static obs_hotkey_t* FindHotkeyByName(const char* name); static obs_hotkey_t* FindHotkeyByName(QString name);
static bool ReplayBufferEnabled(); static bool ReplayBufferEnabled();
static void StartReplayBuffer(); static void StartReplayBuffer();
static bool IsRPHotkeySet(); static bool IsRPHotkeySet();

View File

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

View File

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

View File

@ -28,6 +28,12 @@ with this program. If not, see <https://www.gnu.org/licenses/>
#include "Config.h" #include "Config.h"
#include "forms/settings-dialog.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_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US") 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 #ifndef OBSWEBSOCKET_H
#define 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 PROP_AUTHENTICATED "wsclient_authenticated"
#define OBS_WEBSOCKET_VERSION "4.2.1" #define OBS_WEBSOCKET_VERSION "4.2.1"