mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
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:
parent
53936a4f76
commit
66a059ecdf
111
Config.cpp
111
Config.cpp
@ -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;
|
||||
}
|
||||
|
||||
|
18
Config.h
18
Config.h
@ -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
130
Utils.cpp
@ -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
22
Utils.h
@ -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();
|
||||
|
358
WSEvents.cpp
358
WSEvents.cpp
@ -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);
|
||||
}
|
||||
|
17
WSEvents.h
17
WSEvents.h
@ -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
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user