mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
General: refactor continued
This commit is contained in:
parent
586f9076f0
commit
dba599c127
188
Config.cpp
188
Config.cpp
@ -42,143 +42,143 @@ Config::Config() :
|
|||||||
Secret(""),
|
Secret(""),
|
||||||
Salt(""),
|
Salt(""),
|
||||||
SettingsLoaded(false) {
|
SettingsLoaded(false) {
|
||||||
// OBS Config defaults
|
// OBS Config defaults
|
||||||
config_t* obs_config = obs_frontend_get_global_config();
|
config_t* obs_config = obs_frontend_get_global_config();
|
||||||
if (obs_config) {
|
if (obs_config) {
|
||||||
config_set_default_bool(obs_config,
|
config_set_default_bool(obs_config,
|
||||||
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
config_set_default_uint(obs_config,
|
config_set_default_uint(obs_config,
|
||||||
SECTION_NAME, PARAM_PORT, ServerPort);
|
SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
config_set_default_bool(obs_config,
|
config_set_default_bool(obs_config,
|
||||||
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
|
|
||||||
config_set_default_bool(obs_config,
|
config_set_default_bool(obs_config,
|
||||||
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
config_set_default_string(obs_config,
|
config_set_default_string(obs_config,
|
||||||
SECTION_NAME, PARAM_SECRET, Secret);
|
SECTION_NAME, PARAM_SECRET, Secret);
|
||||||
config_set_default_string(obs_config,
|
config_set_default_string(obs_config,
|
||||||
SECTION_NAME, PARAM_SALT, Salt);
|
SECTION_NAME, PARAM_SALT, Salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
mbedtls_entropy_init(&entropy);
|
mbedtls_entropy_init(&entropy);
|
||||||
mbedtls_ctr_drbg_init(&rng);
|
mbedtls_ctr_drbg_init(&rng);
|
||||||
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
|
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
|
||||||
|
|
||||||
SessionChallenge = GenerateSalt();
|
SessionChallenge = GenerateSalt();
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() {
|
Config::~Config() {
|
||||||
mbedtls_ctr_drbg_free(&rng);
|
mbedtls_ctr_drbg_free(&rng);
|
||||||
mbedtls_entropy_free(&entropy);
|
mbedtls_entropy_free(&entropy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Load() {
|
void Config::Load() {
|
||||||
config_t* obs_config = obs_frontend_get_global_config();
|
config_t* obs_config = obs_frontend_get_global_config();
|
||||||
|
|
||||||
ServerEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_ENABLE);
|
ServerEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_ENABLE);
|
||||||
ServerPort = config_get_uint(obs_config, SECTION_NAME, PARAM_PORT);
|
ServerPort = config_get_uint(obs_config, SECTION_NAME, PARAM_PORT);
|
||||||
|
|
||||||
DebugEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_DEBUG);
|
DebugEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_DEBUG);
|
||||||
|
|
||||||
AuthRequired = config_get_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED);
|
AuthRequired = config_get_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED);
|
||||||
Secret = config_get_string(obs_config, SECTION_NAME, PARAM_SECRET);
|
Secret = config_get_string(obs_config, SECTION_NAME, PARAM_SECRET);
|
||||||
Salt = config_get_string(obs_config, SECTION_NAME, PARAM_SALT);
|
Salt = config_get_string(obs_config, SECTION_NAME, PARAM_SALT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Save() {
|
void Config::Save() {
|
||||||
config_t* obs_config = obs_frontend_get_global_config();
|
config_t* obs_config = obs_frontend_get_global_config();
|
||||||
|
|
||||||
config_set_bool(obs_config, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
config_set_bool(obs_config, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
config_set_uint(obs_config, SECTION_NAME, PARAM_PORT, ServerPort);
|
config_set_uint(obs_config, SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
config_set_bool(obs_config, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
config_set_bool(obs_config, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
|
|
||||||
config_set_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
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_SECRET, Secret);
|
||||||
config_set_string(obs_config, SECTION_NAME, PARAM_SALT, Salt);
|
config_set_string(obs_config, SECTION_NAME, PARAM_SALT, Salt);
|
||||||
|
|
||||||
config_save(obs_config);
|
config_save(obs_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Config::GenerateSalt() {
|
const char* Config::GenerateSalt() {
|
||||||
// Generate 32 random chars
|
// Generate 32 random chars
|
||||||
unsigned char* random_chars = (unsigned char*)bzalloc(32);
|
unsigned char* random_chars = (unsigned char*)bzalloc(32);
|
||||||
mbedtls_ctr_drbg_random(&rng, random_chars, 32);
|
mbedtls_ctr_drbg_random(&rng, random_chars, 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 salt_bytes;
|
||||||
mbedtls_base64_encode(
|
mbedtls_base64_encode(
|
||||||
(unsigned char*)salt, 64, &salt_bytes,
|
(unsigned char*)salt, 64, &salt_bytes,
|
||||||
random_chars, 32);
|
random_chars, 32);
|
||||||
|
|
||||||
bfree(random_chars);
|
bfree(random_chars);
|
||||||
return salt;
|
return salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Config::GenerateSecret(const char* password, const char* salt) {
|
const char* Config::GenerateSecret(const char* password, const char* salt) {
|
||||||
// Concatenate the password and the salt
|
// Concatenate the password and the salt
|
||||||
std::string passAndSalt = "";
|
std::string 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.c_str(), 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 challenge_bytes = 0;
|
||||||
mbedtls_base64_encode(
|
mbedtls_base64_encode(
|
||||||
(unsigned char*)challenge, 64, &challenge_bytes,
|
(unsigned char*)challenge, 64, &challenge_bytes,
|
||||||
challengeHash, 32);
|
challengeHash, 32);
|
||||||
|
|
||||||
bfree(challengeHash);
|
bfree(challengeHash);
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SetPassword(const char* password) {
|
void Config::SetPassword(const char* password) {
|
||||||
const char* new_salt = GenerateSalt();
|
const char* new_salt = GenerateSalt();
|
||||||
const char* new_challenge = GenerateSecret(password, new_salt);
|
const char* new_challenge = GenerateSecret(password, new_salt);
|
||||||
|
|
||||||
this->Salt = new_salt;
|
this->Salt = new_salt;
|
||||||
this->Secret = new_challenge;
|
this->Secret = new_challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Config::CheckAuth(const char* response) {
|
bool Config::CheckAuth(const char* response) {
|
||||||
// Concatenate auth secret with the challenge sent to the user
|
// Concatenate auth secret with the challenge sent to the user
|
||||||
std::string challengeAndResponse = "";
|
std::string challengeAndResponse = "";
|
||||||
challengeAndResponse += this->Secret;
|
challengeAndResponse += this->Secret;
|
||||||
challengeAndResponse += this->SessionChallenge;
|
challengeAndResponse += this->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.c_str(),
|
||||||
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* expected_response = (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*)expected_response, 64, &base64_size,
|
||||||
hash, 32);
|
hash, 32);
|
||||||
|
|
||||||
bool authSuccess = false;
|
bool authSuccess = false;
|
||||||
if (strcmp(expected_response, response) == 0) {
|
if (strcmp(expected_response, response) == 0) {
|
||||||
SessionChallenge = GenerateSalt();
|
SessionChallenge = GenerateSalt();
|
||||||
authSuccess = true;
|
authSuccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bfree(hash);
|
bfree(hash);
|
||||||
bfree(expected_response);
|
bfree(expected_response);
|
||||||
return authSuccess;
|
return authSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config* Config::Current() {
|
Config* Config::Current() {
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
548
Utils.cpp
548
Utils.cpp
@ -28,448 +28,448 @@ 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) {
|
obs_data_array_t* string_list_to_array(char** strings, char* key) {
|
||||||
if (!strings)
|
if (!strings)
|
||||||
return obs_data_array_create();
|
return obs_data_array_create();
|
||||||
|
|
||||||
obs_data_array_t* list = obs_data_array_create();
|
obs_data_array_t* list = obs_data_array_create();
|
||||||
|
|
||||||
char* value = "";
|
char* value = "";
|
||||||
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();
|
obs_data_t* 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);
|
obs_data_release(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
obs_scene_t* scene = obs_scene_from_source(source);
|
||||||
|
|
||||||
if (!scene)
|
if (!scene)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](
|
obs_scene_enum_items(scene, [](
|
||||||
obs_scene_t* scene,
|
obs_scene_t* scene,
|
||||||
obs_sceneitem_t* currentItem,
|
obs_sceneitem_t* currentItem,
|
||||||
void* param) {
|
void* param) {
|
||||||
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);
|
obs_data_t* item_data = GetSceneItemData(currentItem);
|
||||||
obs_data_array_insert(data, 0, item_data);
|
obs_data_array_insert(data, 0, item_data);
|
||||||
|
|
||||||
obs_data_release(item_data);
|
obs_data_release(item_data);
|
||||||
return true;
|
return true;
|
||||||
}, items);
|
}, items);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
|
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item) {
|
||||||
if (!item)
|
if (!item)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
obs_sceneitem_get_pos(item, &pos);
|
obs_sceneitem_get_pos(item, &pos);
|
||||||
|
|
||||||
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_source_t* item_source = obs_sceneitem_get_source(item);
|
||||||
float item_width = float(obs_source_get_width(item_source));
|
float item_width = float(obs_source_get_width(item_source));
|
||||||
float item_height = float(obs_source_get_height(item_source));
|
float item_height = float(obs_source_get_height(item_source));
|
||||||
|
|
||||||
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",
|
||||||
obs_source_get_name(obs_sceneitem_get_source(item)));
|
obs_source_get_name(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_string(data, "type",
|
obs_data_set_string(data, "type",
|
||||||
obs_source_get_id(obs_sceneitem_get_source(item)));
|
obs_source_get_id(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_double(data, "volume",
|
obs_data_set_double(data, "volume",
|
||||||
obs_source_get_volume(obs_sceneitem_get_source(item)));
|
obs_source_get_volume(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_double(data, "x", pos.x);
|
obs_data_set_double(data, "x", pos.x);
|
||||||
obs_data_set_double(data, "y", pos.y);
|
obs_data_set_double(data, "y", pos.y);
|
||||||
obs_data_set_int(data, "source_cx", (int)item_width);
|
obs_data_set_int(data, "source_cx", (int)item_width);
|
||||||
obs_data_set_int(data, "source_cy", (int)item_height);
|
obs_data_set_int(data, "source_cy", (int)item_height);
|
||||||
obs_data_set_double(data, "cx", item_width* scale.x);
|
obs_data_set_double(data, "cx", item_width* scale.x);
|
||||||
obs_data_set_double(data, "cy", item_height* scale.y);
|
obs_data_set_double(data, "cy", item_height* scale.y);
|
||||||
obs_data_set_bool(data, "render", obs_sceneitem_visible(item));
|
obs_data_set_bool(data, "render", obs_sceneitem_visible(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, const char* name) {
|
||||||
struct current_search {
|
struct current_search {
|
||||||
const char* query;
|
const char* query;
|
||||||
obs_sceneitem_t* result;
|
obs_sceneitem_t* result;
|
||||||
};
|
};
|
||||||
|
|
||||||
current_search search;
|
current_search search;
|
||||||
search.query = name;
|
search.query = name;
|
||||||
search.result = nullptr;
|
search.result = nullptr;
|
||||||
|
|
||||||
obs_scene_t* scene = obs_scene_from_source(source);
|
obs_scene_t* scene = obs_scene_from_source(source);
|
||||||
if (scene == nullptr)
|
if (scene == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](
|
obs_scene_enum_items(scene, [](
|
||||||
obs_scene_t* scene,
|
obs_scene_t* scene,
|
||||||
obs_sceneitem_t* currentItem,
|
obs_sceneitem_t* currentItem,
|
||||||
void* param) {
|
void* param) {
|
||||||
current_search* search = static_cast<current_search*>(param);
|
current_search* search = static_cast<current_search*>(param);
|
||||||
|
|
||||||
const char* currentItemName =
|
const char* 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 (strcmp(currentItemName, search->query) == 0) {
|
||||||
search->result = currentItem;
|
search->result = currentItem;
|
||||||
obs_sceneitem_addref(search->result);
|
obs_sceneitem_addref(search->result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, &search);
|
}, &search);
|
||||||
|
|
||||||
return search.result;
|
return search.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t* Utils::GetTransitionFromName(const char* search_name) {
|
obs_source_t* Utils::GetTransitionFromName(const char* search_name) {
|
||||||
obs_source_t* found_transition = NULL;
|
obs_source_t* found_transition = NULL;
|
||||||
|
|
||||||
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];
|
||||||
|
|
||||||
const char* transition_name = obs_source_get_name(transition);
|
const char* transition_name = obs_source_get_name(transition);
|
||||||
if (strcmp(transition_name, search_name) == 0)
|
if (strcmp(transition_name, search_name) == 0)
|
||||||
{
|
{
|
||||||
found_transition = transition;
|
found_transition = transition;
|
||||||
obs_source_addref(found_transition);
|
obs_source_addref(found_transition);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_source_list_free(&transition_list);
|
obs_frontend_source_list_free(&transition_list);
|
||||||
|
|
||||||
return found_transition;
|
return found_transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t* Utils::GetSceneFromNameOrCurrent(const char* scene_name) {
|
obs_source_t* Utils::GetSceneFromNameOrCurrent(const char* scene_name) {
|
||||||
obs_source_t* scene = nullptr;
|
obs_source_t* scene = nullptr;
|
||||||
|
|
||||||
if (!scene_name || !strlen(scene_name))
|
if (!scene_name || !strlen(scene_name))
|
||||||
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(scene_name);
|
||||||
|
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_array_t* Utils::GetScenes() {
|
obs_data_array_t* Utils::GetScenes() {
|
||||||
obs_frontend_source_list sceneList = {};
|
obs_frontend_source_list sceneList = {};
|
||||||
obs_frontend_get_scenes(&sceneList);
|
obs_frontend_get_scenes(&sceneList);
|
||||||
|
|
||||||
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];
|
||||||
|
|
||||||
obs_data_t* scene_data = GetSceneData(scene);
|
obs_data_t* scene_data = GetSceneData(scene);
|
||||||
obs_data_array_push_back(scenes, scene_data);
|
obs_data_array_push_back(scenes, scene_data);
|
||||||
|
|
||||||
obs_data_release(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* source) {
|
||||||
obs_data_array_t* scene_items = GetSceneItems(source);
|
obs_data_array_t* scene_items = 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", scene_items);
|
||||||
|
|
||||||
obs_data_array_release(scene_items);
|
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** scene_collections = obs_frontend_get_scene_collections();
|
||||||
obs_data_array_t* list = string_list_to_array(scene_collections, "sc-name");
|
obs_data_array_t* list = string_list_to_array(scene_collections, "sc-name");
|
||||||
|
|
||||||
bfree(scene_collections);
|
bfree(scene_collections);
|
||||||
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 = string_list_to_array(profiles, "profile-name");
|
||||||
|
|
||||||
bfree(profiles);
|
bfree(profiles);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSpinBox* Utils::GetTransitionDurationControl() {
|
QSpinBox* Utils::GetTransitionDurationControl() {
|
||||||
QMainWindow* window = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* window = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
return window->findChild<QSpinBox*>("transitionDuration");
|
return window->findChild<QSpinBox*>("transitionDuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
int Utils::GetTransitionDuration() {
|
int Utils::GetTransitionDuration() {
|
||||||
QSpinBox* control = GetTransitionDurationControl();
|
QSpinBox* control = GetTransitionDurationControl();
|
||||||
if (control)
|
if (control)
|
||||||
return control->value();
|
return control->value();
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::SetTransitionDuration(int ms) {
|
void Utils::SetTransitionDuration(int ms) {
|
||||||
QSpinBox* control = GetTransitionDurationControl();
|
QSpinBox* control = GetTransitionDurationControl();
|
||||||
if (control && ms >= 0)
|
if (control && ms >= 0)
|
||||||
control->setValue(ms);
|
control->setValue(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::SetTransitionByName(const char* transition_name) {
|
bool Utils::SetTransitionByName(const char* transition_name) {
|
||||||
obs_source_t* transition = GetTransitionFromName(transition_name);
|
obs_source_t* transition = GetTransitionFromName(transition_name);
|
||||||
|
|
||||||
if (transition) {
|
if (transition) {
|
||||||
obs_frontend_set_current_transition(transition);
|
obs_frontend_set_current_transition(transition);
|
||||||
obs_source_release(transition);
|
obs_source_release(transition);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton* Utils::GetPreviewModeButtonControl() {
|
QPushButton* Utils::GetPreviewModeButtonControl() {
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
return main->findChild<QPushButton*>("modeSwitch");
|
return main->findChild<QPushButton*>("modeSwitch");
|
||||||
}
|
}
|
||||||
|
|
||||||
QListWidget* Utils::GetSceneListControl() {
|
QListWidget* Utils::GetSceneListControl() {
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
return main->findChild<QListWidget*>("scenes");
|
return main->findChild<QListWidget*>("scenes");
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item) {
|
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 item_data = item->data(static_cast<int>(Qt::UserRole));
|
||||||
return item_data.value<OBSScene>();
|
return item_data.value<OBSScene>();
|
||||||
}
|
}
|
||||||
|
|
||||||
QLayout* Utils::GetPreviewLayout() {
|
QLayout* Utils::GetPreviewLayout() {
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
return main->findChild<QLayout*>("previewLayout");
|
return main->findChild<QLayout*>("previewLayout");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::IsPreviewModeActive() {
|
bool Utils::IsPreviewModeActive() {
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
|
||||||
// Clue 1 : "Studio Mode" button is toggled on
|
// Clue 1 : "Studio Mode" button is toggled on
|
||||||
bool buttonToggledOn = GetPreviewModeButtonControl()->isChecked();
|
bool buttonToggledOn = GetPreviewModeButtonControl()->isChecked();
|
||||||
|
|
||||||
// Clue 2 : Preview layout has more than one item
|
// Clue 2 : Preview layout has more than one item
|
||||||
int previewChildCount = GetPreviewLayout()->count();
|
int previewChildCount = GetPreviewLayout()->count();
|
||||||
blog(LOG_INFO, "preview layout children count : %d", previewChildCount);
|
blog(LOG_INFO, "preview layout children count : %d", previewChildCount);
|
||||||
|
|
||||||
return buttonToggledOn || (previewChildCount >= 2);
|
return buttonToggledOn || (previewChildCount >= 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::EnablePreviewMode() {
|
void Utils::EnablePreviewMode() {
|
||||||
if (!IsPreviewModeActive())
|
if (!IsPreviewModeActive())
|
||||||
GetPreviewModeButtonControl()->click();
|
GetPreviewModeButtonControl()->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::DisablePreviewMode() {
|
void Utils::DisablePreviewMode() {
|
||||||
if (IsPreviewModeActive())
|
if (IsPreviewModeActive())
|
||||||
GetPreviewModeButtonControl()->click();
|
GetPreviewModeButtonControl()->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::TogglePreviewMode() {
|
void Utils::TogglePreviewMode() {
|
||||||
GetPreviewModeButtonControl()->click();
|
GetPreviewModeButtonControl()->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_t* Utils::GetPreviewScene() {
|
obs_scene_t* Utils::GetPreviewScene() {
|
||||||
if (IsPreviewModeActive()) {
|
if (IsPreviewModeActive()) {
|
||||||
QListWidget* sceneList = GetSceneListControl();
|
QListWidget* sceneList = GetSceneListControl();
|
||||||
QList<QListWidgetItem*> selected = sceneList->selectedItems();
|
QList<QListWidgetItem*> selected = sceneList->selectedItems();
|
||||||
|
|
||||||
// Qt::UserRole == QtUserRole::OBSRef
|
// Qt::UserRole == QtUserRole::OBSRef
|
||||||
obs_scene_t* scene = Utils::SceneListItemToScene(selected.first());
|
obs_scene_t* scene = Utils::SceneListItemToScene(selected.first());
|
||||||
|
|
||||||
obs_scene_addref(scene);
|
obs_scene_addref(scene);
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::SetPreviewScene(const char* name) {
|
bool Utils::SetPreviewScene(const char* name) {
|
||||||
if (IsPreviewModeActive()) {
|
if (IsPreviewModeActive()) {
|
||||||
QListWidget* sceneList = GetSceneListControl();
|
QListWidget* sceneList = GetSceneListControl();
|
||||||
QList<QListWidgetItem*> matchingItems =
|
QList<QListWidgetItem*> matchingItems =
|
||||||
sceneList->findItems(name, Qt::MatchExactly);
|
sceneList->findItems(name, Qt::MatchExactly);
|
||||||
|
|
||||||
if (matchingItems.count() > 0) {
|
if (matchingItems.count() > 0) {
|
||||||
sceneList->setCurrentItem(matchingItems.first());
|
sceneList->setCurrentItem(matchingItems.first());
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::TransitionToProgram() {
|
void Utils::TransitionToProgram() {
|
||||||
if (!IsPreviewModeActive())
|
if (!IsPreviewModeActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// WARNING : if the layout created in OBS' CreateProgramOptions() changes
|
// WARNING : if the layout created in OBS' CreateProgramOptions() changes
|
||||||
// then this won't work as expected
|
// then this won't work as expected
|
||||||
|
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
|
||||||
// The program options widget is the second item in the left-to-right layout
|
// The program options widget is the second item in the left-to-right layout
|
||||||
QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget();
|
QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget();
|
||||||
|
|
||||||
// The "Transition" button lies in the mainButtonLayout
|
// The "Transition" button lies in the mainButtonLayout
|
||||||
// which is the first itemin the program options' layout
|
// which is the first itemin the program options' layout
|
||||||
QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout();
|
QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout();
|
||||||
QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget();
|
QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget();
|
||||||
|
|
||||||
// Try to cast that widget into a button
|
// Try to cast that widget into a button
|
||||||
QPushButton* transitionBtn = qobject_cast<QPushButton*>(transitionBtnWidget);
|
QPushButton* transitionBtn = qobject_cast<QPushButton*>(transitionBtnWidget);
|
||||||
|
|
||||||
// Perform a click on that button
|
// Perform a click on that button
|
||||||
transitionBtn->click();
|
transitionBtn->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Utils::OBSVersionString() {
|
const char* Utils::OBSVersionString() {
|
||||||
uint32_t version = obs_get_version();
|
uint32_t version = obs_get_version();
|
||||||
|
|
||||||
uint8_t major, minor, patch;
|
uint8_t major, minor, patch;
|
||||||
major = (version >> 24) & 0xFF;
|
major = (version >> 24) & 0xFF;
|
||||||
minor = (version >> 16) & 0xFF;
|
minor = (version >> 16) & 0xFF;
|
||||||
patch = version & 0xFF;
|
patch = version & 0xFF;
|
||||||
|
|
||||||
char* result = (char*)bmalloc(sizeof(char) * 12);
|
char* result = (char*)bmalloc(sizeof(char) * 12);
|
||||||
sprintf(result, "%d.%d.%d", major, minor, patch);
|
sprintf(result, "%d.%d.%d", major, minor, patch);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSystemTrayIcon* Utils::GetTrayIcon() {
|
QSystemTrayIcon* Utils::GetTrayIcon() {
|
||||||
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
return main->findChildren<QSystemTrayIcon*>().first();
|
return main->findChildren<QSystemTrayIcon*>().first();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::SysTrayNotify(QString &text,
|
void Utils::SysTrayNotify(QString &text,
|
||||||
QSystemTrayIcon::MessageIcon icon, QString title) {
|
QSystemTrayIcon::MessageIcon icon, QString title) {
|
||||||
if (!QSystemTrayIcon::supportsMessages())
|
if (!QSystemTrayIcon::supportsMessages())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QSystemTrayIcon* trayIcon = GetTrayIcon();
|
QSystemTrayIcon* trayIcon = GetTrayIcon();
|
||||||
if (trayIcon)
|
if (trayIcon)
|
||||||
trayIcon->showMessage(title, text, icon);
|
trayIcon->showMessage(title, text, icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Utils::FormatIPAddress(QHostAddress &addr) {
|
QString Utils::FormatIPAddress(QHostAddress &addr) {
|
||||||
if (addr.protocol() == QAbstractSocket::IPv4Protocol)
|
if (addr.protocol() == QAbstractSocket::IPv4Protocol)
|
||||||
QString v4addr = addr.toString().replace("::fff:", "");
|
QString v4addr = addr.toString().replace("::fff:", "");
|
||||||
|
|
||||||
return addr.toString();
|
return addr.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
const char* outputMode = config_get_string(profile, "Output", "Mode");
|
||||||
|
|
||||||
if (strcmp(outputMode, "Advanced") == 0) {
|
if (strcmp(outputMode, "Advanced") == 0) {
|
||||||
// Advanced mode
|
// Advanced mode
|
||||||
return config_get_string(profile, "AdvOut", "RecFilePath");
|
return config_get_string(profile, "AdvOut", "RecFilePath");
|
||||||
} else {
|
} else {
|
||||||
// Simple mode
|
// Simple mode
|
||||||
return config_get_string(profile, "SimpleOutput", "FilePath");
|
return config_get_string(profile, "SimpleOutput", "FilePath");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Utils::SetRecordingFolder(const char* path) {
|
bool Utils::SetRecordingFolder(const char* path) {
|
||||||
if (!QDir(path).exists())
|
if (!QDir(path).exists())
|
||||||
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");
|
const char* outputMode = config_get_string(profile, "Output", "Mode");
|
||||||
|
|
||||||
if (strcmp(outputMode, "Advanced") == 0) {
|
if (strcmp(outputMode, "Advanced") == 0) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
config_save(profile);
|
config_save(profile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString* Utils::ParseDataToQueryString(obs_data_t* data) {
|
QString* Utils::ParseDataToQueryString(obs_data_t* data) {
|
||||||
QString* query = nullptr;
|
QString* query = nullptr;
|
||||||
if (data) {
|
if (data) {
|
||||||
obs_data_item_t* item = obs_data_first(data);
|
obs_data_item_t* item = obs_data_first(data);
|
||||||
if (item) {
|
if (item) {
|
||||||
query = new QString();
|
query = new QString();
|
||||||
bool isFirst = true;
|
bool isFirst = true;
|
||||||
do {
|
do {
|
||||||
if (!obs_data_item_has_user_value(item))
|
if (!obs_data_item_has_user_value(item))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!isFirst)
|
if (!isFirst)
|
||||||
query->append('&');
|
query->append('&');
|
||||||
else
|
else
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
|
|
||||||
const char* attrName = obs_data_item_get_name(item);
|
const char* attrName = obs_data_item_get_name(item);
|
||||||
query->append(attrName).append("=");
|
query->append(attrName).append("=");
|
||||||
|
|
||||||
switch (obs_data_item_gettype(item)) {
|
switch (obs_data_item_gettype(item)) {
|
||||||
case OBS_DATA_BOOLEAN:
|
case OBS_DATA_BOOLEAN:
|
||||||
query->append(obs_data_item_get_bool(item)?"true":"false");
|
query->append(obs_data_item_get_bool(item)?"true":"false");
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_NUMBER:
|
case OBS_DATA_NUMBER:
|
||||||
switch (obs_data_item_numtype(item))
|
switch (obs_data_item_numtype(item))
|
||||||
{
|
{
|
||||||
case OBS_DATA_NUM_DOUBLE:
|
case OBS_DATA_NUM_DOUBLE:
|
||||||
query->append(
|
query->append(
|
||||||
QString::number(obs_data_item_get_double(item)));
|
QString::number(obs_data_item_get_double(item)));
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_NUM_INT:
|
case OBS_DATA_NUM_INT:
|
||||||
query->append(
|
query->append(
|
||||||
QString::number(obs_data_item_get_int(item)));
|
QString::number(obs_data_item_get_int(item)));
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_NUM_INVALID:
|
case OBS_DATA_NUM_INVALID:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OBS_DATA_STRING:
|
case OBS_DATA_STRING:
|
||||||
query->append(QUrl::toPercentEncoding(
|
query->append(QUrl::toPercentEncoding(
|
||||||
QString(obs_data_item_get_string(item))));
|
QString(obs_data_item_get_string(item))));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
//other types are not supported
|
//other types are not supported
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (obs_data_item_next(&item));
|
} while (obs_data_item_next(&item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
2
Utils.h
2
Utils.h
@ -49,7 +49,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(const char* transition_name);
|
||||||
|
|
||||||
static QPushButton* GetPreviewModeButtonControl();
|
static QPushButton* GetPreviewModeButtonControl();
|
||||||
static QLayout* GetPreviewLayout();
|
static QLayout* GetPreviewLayout();
|
||||||
|
692
WSEvents.cpp
692
WSEvents.cpp
@ -29,555 +29,555 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
bool transition_is_cut(obs_source_t* transition) {
|
bool transition_is_cut(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) {
|
&& strcmp(obs_source_get_id(transition), "cut_transition") == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ns_to_timestamp(uint64_t ns) {
|
const char* ns_to_timestamp(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 hours_part = minutes / 60;
|
||||||
uint64_t minutes_part = minutes % 60;
|
uint64_t minutes_part = minutes % 60;
|
||||||
uint64_t secs_part = secs % 60;
|
uint64_t secs_part = secs % 60;
|
||||||
uint64_t ms_part = ms % 1000;
|
uint64_t ms_part = 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);
|
hours_part, minutes_part, secs_part, ms_part);
|
||||||
|
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
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* duration_control = Utils::GetTransitionDurationControl();
|
||||||
connect(duration_control, SIGNAL(valueChanged(int)),
|
connect(duration_control, SIGNAL(valueChanged(int)),
|
||||||
this, SLOT(TransitionDurationChanged(int)));
|
this, SLOT(TransitionDurationChanged(int)));
|
||||||
|
|
||||||
QTimer* statusTimer = new QTimer();
|
QTimer* statusTimer = new QTimer();
|
||||||
connect(statusTimer, SIGNAL(timeout()),
|
connect(statusTimer, SIGNAL(timeout()),
|
||||||
this, SLOT(StreamStatus()));
|
this, SLOT(StreamStatus()));
|
||||||
statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS
|
statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS
|
||||||
|
|
||||||
QListWidget* sceneList = Utils::GetSceneListControl();
|
QListWidget* sceneList = Utils::GetSceneListControl();
|
||||||
connect(sceneList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
|
connect(sceneList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
|
||||||
this, SLOT(SelectedSceneChanged(QListWidgetItem*, QListWidgetItem*)));
|
this, SLOT(SelectedSceneChanged(QListWidgetItem*, QListWidgetItem*)));
|
||||||
|
|
||||||
QPushButton* modeSwitch = Utils::GetPreviewModeButtonControl();
|
QPushButton* modeSwitch = Utils::GetPreviewModeButtonControl();
|
||||||
connect(modeSwitch, SIGNAL(clicked(bool)), this, SLOT(ModeSwitchClicked(bool)));
|
connect(modeSwitch, SIGNAL(clicked(bool)), this, SLOT(ModeSwitchClicked(bool)));
|
||||||
|
|
||||||
transition_handler = nullptr;
|
transition_handler = nullptr;
|
||||||
scene_handler = nullptr;
|
scene_handler = nullptr;
|
||||||
|
|
||||||
QTimer::singleShot(1000, this, SLOT(deferredInitOperations()));
|
QTimer::singleShot(1000, this, SLOT(deferredInitOperations()));
|
||||||
|
|
||||||
_streaming_active = false;
|
_streaming_active = false;
|
||||||
_recording_active = false;
|
_recording_active = false;
|
||||||
|
|
||||||
_stream_starttime = 0;
|
_stream_starttime = 0;
|
||||||
_rec_starttime = 0;
|
_rec_starttime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WSEvents::~WSEvents() {
|
WSEvents::~WSEvents() {
|
||||||
obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this);
|
obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::deferredInitOperations() {
|
void WSEvents::deferredInitOperations() {
|
||||||
obs_source_t* transition = obs_frontend_get_current_transition();
|
obs_source_t* transition = obs_frontend_get_current_transition();
|
||||||
connectTransitionSignals(transition);
|
connectTransitionSignals(transition);
|
||||||
obs_source_release(transition);
|
obs_source_release(transition);
|
||||||
|
|
||||||
obs_source_t* scene = obs_frontend_get_current_scene();
|
obs_source_t* scene = obs_frontend_get_current_scene();
|
||||||
connectSceneSignals(scene);
|
connectSceneSignals(scene);
|
||||||
obs_source_release(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) {
|
||||||
WSEvents* owner = static_cast<WSEvents*>(private_data);
|
WSEvents* owner = static_cast<WSEvents*>(private_data);
|
||||||
|
|
||||||
if (!owner->_srv)
|
if (!owner->_srv)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO : implement SourceOrderChanged and RepopulateSources
|
// TODO : implement SourceOrderChanged and RepopulateSources
|
||||||
|
|
||||||
if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) {
|
if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) {
|
||||||
owner->OnSceneChange();
|
owner->OnSceneChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED) {
|
||||||
owner->OnSceneListChange();
|
owner->OnSceneListChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED) {
|
||||||
owner->OnSceneCollectionChange();
|
owner->OnSceneCollectionChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED) {
|
||||||
owner->OnSceneCollectionListChange();
|
owner->OnSceneCollectionListChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_TRANSITION_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_TRANSITION_CHANGED) {
|
||||||
owner->OnTransitionChange();
|
owner->OnTransitionChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED) {
|
||||||
owner->OnTransitionListChange();
|
owner->OnTransitionListChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_PROFILE_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_PROFILE_CHANGED) {
|
||||||
owner->OnProfileChange();
|
owner->OnProfileChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED) {
|
||||||
owner->OnProfileListChange();
|
owner->OnProfileListChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTING) {
|
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTING) {
|
||||||
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->_streaming_active = 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->_streaming_active = 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->_recording_active = 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->_recording_active = false;
|
||||||
owner->OnRecordingStopped();
|
owner->OnRecordingStopped();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_EXIT) {
|
else if (event == OBS_FRONTEND_EVENT_EXIT) {
|
||||||
owner->OnExit();
|
owner->OnExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::broadcastUpdate(const char* updateType,
|
void WSEvents::broadcastUpdate(const char* updateType,
|
||||||
obs_data_t* additionalFields = NULL) {
|
obs_data_t* additionalFields = NULL) {
|
||||||
obs_data_t* update = obs_data_create();
|
obs_data_t* 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 (_streaming_active) {
|
||||||
ts = ns_to_timestamp(os_gettime_ns() - _stream_starttime);
|
ts = ns_to_timestamp(os_gettime_ns() - _stream_starttime);
|
||||||
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 (_recording_active) {
|
||||||
ts = ns_to_timestamp(os_gettime_ns() - _rec_starttime);
|
ts = ns_to_timestamp(os_gettime_ns() - _rec_starttime);
|
||||||
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 != NULL)
|
||||||
obs_data_apply(update, additionalFields);
|
obs_data_apply(update, additionalFields);
|
||||||
|
|
||||||
const char *json = obs_data_get_json(update);
|
const char *json = obs_data_get_json(update);
|
||||||
_srv->broadcast(json);
|
_srv->broadcast(json);
|
||||||
if (Config::Current()->DebugEnabled)
|
if (Config::Current()->DebugEnabled)
|
||||||
blog(LOG_DEBUG, "Update << '%s'", json);
|
blog(LOG_DEBUG, "Update << '%s'", json);
|
||||||
|
|
||||||
obs_data_release(update);
|
obs_data_release(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::connectTransitionSignals(obs_source_t* transition) {
|
void WSEvents::connectTransitionSignals(obs_source_t* transition) {
|
||||||
if (transition_handler) {
|
if (transition_handler) {
|
||||||
signal_handler_disconnect(transition_handler,
|
signal_handler_disconnect(transition_handler,
|
||||||
"transition_start", OnTransitionBegin, this);
|
"transition_start", OnTransitionBegin, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!transition_is_cut(transition)) {
|
if (!transition_is_cut(transition)) {
|
||||||
transition_handler = obs_source_get_signal_handler(transition);
|
transition_handler = obs_source_get_signal_handler(transition);
|
||||||
signal_handler_connect(transition_handler,
|
signal_handler_connect(transition_handler,
|
||||||
"transition_start", OnTransitionBegin, this);
|
"transition_start", OnTransitionBegin, this);
|
||||||
} else {
|
} else {
|
||||||
transition_handler = nullptr;
|
transition_handler = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::connectSceneSignals(obs_source_t* scene) {
|
void WSEvents::connectSceneSignals(obs_source_t* scene) {
|
||||||
if (scene_handler) {
|
if (scene_handler) {
|
||||||
signal_handler_disconnect(scene_handler,
|
signal_handler_disconnect(scene_handler,
|
||||||
"reorder", OnSceneReordered, this);
|
"reorder", OnSceneReordered, this);
|
||||||
signal_handler_disconnect(scene_handler,
|
signal_handler_disconnect(scene_handler,
|
||||||
"item_add", OnSceneItemAdd, this);
|
"item_add", OnSceneItemAdd, this);
|
||||||
signal_handler_disconnect(scene_handler,
|
signal_handler_disconnect(scene_handler,
|
||||||
"item_remove", OnSceneItemDelete, this);
|
"item_remove", OnSceneItemDelete, this);
|
||||||
signal_handler_disconnect(scene_handler,
|
signal_handler_disconnect(scene_handler,
|
||||||
"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);
|
scene_handler = obs_source_get_signal_handler(scene);
|
||||||
signal_handler_connect(scene_handler,
|
signal_handler_connect(scene_handler,
|
||||||
"reorder", OnSceneReordered, this);
|
"reorder", OnSceneReordered, this);
|
||||||
signal_handler_connect(scene_handler,
|
signal_handler_connect(scene_handler,
|
||||||
"item_add", OnSceneItemAdd, this);
|
"item_add", OnSceneItemAdd, this);
|
||||||
signal_handler_connect(scene_handler,
|
signal_handler_connect(scene_handler,
|
||||||
"item_remove", OnSceneItemDelete, this);
|
"item_remove", OnSceneItemDelete, this);
|
||||||
signal_handler_connect(scene_handler,
|
signal_handler_connect(scene_handler,
|
||||||
"item_visible", OnSceneItemVisibilityChanged, this);
|
"item_visible", OnSceneItemVisibilityChanged, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t WSEvents::GetStreamingTime() {
|
uint64_t WSEvents::GetStreamingTime() {
|
||||||
if (_streaming_active)
|
if (_streaming_active)
|
||||||
return (os_gettime_ns() - _stream_starttime);
|
return (os_gettime_ns() - _stream_starttime);
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* WSEvents::GetStreamingTimecode() {
|
const char* WSEvents::GetStreamingTimecode() {
|
||||||
return ns_to_timestamp(GetStreamingTime());
|
return ns_to_timestamp(GetStreamingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t WSEvents::GetRecordingTime() {
|
uint64_t WSEvents::GetRecordingTime() {
|
||||||
if (_recording_active)
|
if (_recording_active)
|
||||||
return (os_gettime_ns() - _rec_starttime);
|
return (os_gettime_ns() - _rec_starttime);
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* WSEvents::GetRecordingTimecode() {
|
const char* WSEvents::GetRecordingTimecode() {
|
||||||
return ns_to_timestamp(GetRecordingTime());
|
return ns_to_timestamp(GetRecordingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneChange() {
|
void WSEvents::OnSceneChange() {
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* data = obs_data_create();
|
||||||
|
|
||||||
obs_source_t* current_scene = obs_frontend_get_current_scene();
|
obs_source_t* current_scene = obs_frontend_get_current_scene();
|
||||||
obs_data_array_t* scene_items = Utils::GetSceneItems(current_scene);
|
obs_data_array_t* scene_items = Utils::GetSceneItems(current_scene);
|
||||||
connectSceneSignals(current_scene);
|
connectSceneSignals(current_scene);
|
||||||
|
|
||||||
obs_data_set_string(data, "scene-name", obs_source_get_name(current_scene));
|
obs_data_set_string(data, "scene-name", obs_source_get_name(current_scene));
|
||||||
obs_data_set_array(data, "sources", scene_items);
|
obs_data_set_array(data, "sources", scene_items);
|
||||||
|
|
||||||
broadcastUpdate("SwitchScenes", data);
|
broadcastUpdate("SwitchScenes", data);
|
||||||
|
|
||||||
obs_data_array_release(scene_items);
|
obs_data_array_release(scene_items);
|
||||||
obs_source_release(current_scene);
|
obs_source_release(current_scene);
|
||||||
obs_data_release(data);
|
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 (Utils::IsPreviewModeActive()) {
|
if (Utils::IsPreviewModeActive()) {
|
||||||
QListWidget* list = Utils::GetSceneListControl();
|
QListWidget* list = Utils::GetSceneListControl();
|
||||||
SelectedSceneChanged(list->currentItem(), nullptr);
|
SelectedSceneChanged(list->currentItem(), nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneListChange() {
|
void WSEvents::OnSceneListChange() {
|
||||||
broadcastUpdate("ScenesChanged");
|
broadcastUpdate("ScenesChanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneCollectionChange() {
|
void WSEvents::OnSceneCollectionChange() {
|
||||||
broadcastUpdate("SceneCollectionChanged");
|
broadcastUpdate("SceneCollectionChanged");
|
||||||
|
|
||||||
scene_handler = nullptr;
|
scene_handler = nullptr;
|
||||||
transition_handler = nullptr;
|
transition_handler = nullptr;
|
||||||
|
|
||||||
OnTransitionListChange();
|
OnTransitionListChange();
|
||||||
OnTransitionChange();
|
OnTransitionChange();
|
||||||
|
|
||||||
OnSceneListChange();
|
OnSceneListChange();
|
||||||
OnSceneChange();
|
OnSceneChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneCollectionListChange() {
|
void WSEvents::OnSceneCollectionListChange() {
|
||||||
broadcastUpdate("SceneCollectionListChanged");
|
broadcastUpdate("SceneCollectionListChanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnTransitionChange() {
|
void WSEvents::OnTransitionChange() {
|
||||||
obs_source_t* current_transition = obs_frontend_get_current_transition();
|
obs_source_t* current_transition = obs_frontend_get_current_transition();
|
||||||
connectTransitionSignals(current_transition);
|
connectTransitionSignals(current_transition);
|
||||||
|
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* 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(current_transition));
|
||||||
|
|
||||||
broadcastUpdate("SwitchTransition", data);
|
broadcastUpdate("SwitchTransition", data);
|
||||||
|
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
obs_source_release(current_transition);
|
obs_source_release(current_transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnTransitionListChange() {
|
void WSEvents::OnTransitionListChange() {
|
||||||
broadcastUpdate("TransitionListChanged");
|
broadcastUpdate("TransitionListChanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnProfileChange() {
|
void WSEvents::OnProfileChange() {
|
||||||
broadcastUpdate("ProfileChanged");
|
broadcastUpdate("ProfileChanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnProfileListChange() {
|
void WSEvents::OnProfileListChange() {
|
||||||
broadcastUpdate("ProfileListChanged");
|
broadcastUpdate("ProfileListChanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStarting() {
|
void WSEvents::OnStreamStarting() {
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* 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);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStarted() {
|
void WSEvents::OnStreamStarted() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
_stream_starttime = os_gettime_ns();
|
_stream_starttime = os_gettime_ns();
|
||||||
_lastBytesSent = 0;
|
_lastBytesSent = 0;
|
||||||
broadcastUpdate("StreamStarted");
|
broadcastUpdate("StreamStarted");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStopping() {
|
void WSEvents::OnStreamStopping() {
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* 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);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStopped() {
|
void WSEvents::OnStreamStopped() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
_stream_starttime = 0;
|
_stream_starttime = 0;
|
||||||
broadcastUpdate("StreamStopped");
|
broadcastUpdate("StreamStopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStarting() {
|
void WSEvents::OnRecordingStarting() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
broadcastUpdate("RecordingStarting");
|
broadcastUpdate("RecordingStarting");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStarted() {
|
void WSEvents::OnRecordingStarted() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
_rec_starttime = os_gettime_ns();
|
_rec_starttime = os_gettime_ns();
|
||||||
broadcastUpdate("RecordingStarted");
|
broadcastUpdate("RecordingStarted");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStopping() {
|
void WSEvents::OnRecordingStopping() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
broadcastUpdate("RecordingStopping");
|
broadcastUpdate("RecordingStopping");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStopped() {
|
void WSEvents::OnRecordingStopped() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
_rec_starttime = 0;
|
_rec_starttime = 0;
|
||||||
broadcastUpdate("RecordingStopped");
|
broadcastUpdate("RecordingStopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnExit() {
|
void WSEvents::OnExit() {
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
broadcastUpdate("Exiting");
|
broadcastUpdate("Exiting");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::StreamStatus() {
|
void WSEvents::StreamStatus() {
|
||||||
bool streaming_active = obs_frontend_streaming_active();
|
bool streaming_active = obs_frontend_streaming_active();
|
||||||
bool recording_active = obs_frontend_recording_active();
|
bool recording_active = obs_frontend_recording_active();
|
||||||
|
|
||||||
obs_output_t* stream_output = obs_frontend_get_streaming_output();
|
obs_output_t* stream_output = obs_frontend_get_streaming_output();
|
||||||
|
|
||||||
if (!stream_output || !streaming_active) {
|
if (!stream_output || !streaming_active) {
|
||||||
if (stream_output) {
|
if (stream_output) {
|
||||||
obs_output_release(stream_output);
|
obs_output_release(stream_output);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t bytes_sent = obs_output_get_total_bytes(stream_output);
|
uint64_t bytes_sent = obs_output_get_total_bytes(stream_output);
|
||||||
uint64_t bytes_sent_time = os_gettime_ns();
|
uint64_t bytes_sent_time = os_gettime_ns();
|
||||||
|
|
||||||
if (bytes_sent < _lastBytesSent)
|
if (bytes_sent < _lastBytesSent)
|
||||||
bytes_sent = 0;
|
bytes_sent = 0;
|
||||||
|
|
||||||
if (bytes_sent == 0)
|
if (bytes_sent == 0)
|
||||||
_lastBytesSent = 0;
|
_lastBytesSent = 0;
|
||||||
|
|
||||||
uint64_t bytes_between = bytes_sent - _lastBytesSent;
|
uint64_t bytes_between = bytes_sent - _lastBytesSent;
|
||||||
double time_passed =
|
double time_passed =
|
||||||
double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
|
double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
|
||||||
|
|
||||||
uint64_t bytes_per_sec = bytes_between / time_passed;
|
uint64_t bytes_per_sec = bytes_between / time_passed;
|
||||||
|
|
||||||
_lastBytesSent = bytes_sent;
|
_lastBytesSent = bytes_sent;
|
||||||
_lastBytesSentTime = bytes_sent_time;
|
_lastBytesSentTime = bytes_sent_time;
|
||||||
|
|
||||||
uint64_t totalStreamTime =
|
uint64_t totalStreamTime =
|
||||||
(os_gettime_ns() - _stream_starttime) / 1000000000;
|
(os_gettime_ns() - _stream_starttime) / 1000000000;
|
||||||
|
|
||||||
int total_frames = obs_output_get_total_frames(stream_output);
|
int total_frames = obs_output_get_total_frames(stream_output);
|
||||||
int dropped_frames = obs_output_get_frames_dropped(stream_output);
|
int dropped_frames = obs_output_get_frames_dropped(stream_output);
|
||||||
|
|
||||||
float strain = obs_output_get_congestion(stream_output);
|
float strain = obs_output_get_congestion(stream_output);
|
||||||
|
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* data = obs_data_create();
|
||||||
obs_data_set_bool(data, "streaming", streaming_active);
|
obs_data_set_bool(data, "streaming", streaming_active);
|
||||||
obs_data_set_bool(data, "recording", recording_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, "bytes-per-sec", bytes_per_sec);
|
||||||
obs_data_set_int(data, "kbits-per-sec", (bytes_per_sec * 8) / 1024);
|
obs_data_set_int(data, "kbits-per-sec", (bytes_per_sec * 8) / 1024);
|
||||||
obs_data_set_int(data, "total-stream-time", totalStreamTime);
|
obs_data_set_int(data, "total-stream-time", totalStreamTime);
|
||||||
obs_data_set_int(data, "num-total-frames", total_frames);
|
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-dropped-frames", dropped_frames);
|
||||||
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_data_release(data);
|
||||||
obs_output_release(stream_output);
|
obs_output_release(stream_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::TransitionDurationChanged(int ms) {
|
void WSEvents::TransitionDurationChanged(int ms) {
|
||||||
obs_data_t* fields = obs_data_create();
|
obs_data_t* 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);
|
obs_data_release(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnTransitionBegin(void* param, calldata_t* data) {
|
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);
|
||||||
instance->broadcastUpdate("TransitionBegin");
|
instance->broadcastUpdate("TransitionBegin");
|
||||||
|
|
||||||
blog(LOG_INFO, "transition begin");
|
blog(LOG_INFO, "transition begin");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
|
void WSEvents::OnSceneReordered(void* param, calldata_t* data) {
|
||||||
WSEvents* instance = static_cast<WSEvents*>(param);
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
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();
|
obs_data_t* 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);
|
obs_data_release(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
|
void WSEvents::OnSceneItemAdd(void* param, calldata_t* data) {
|
||||||
WSEvents* instance = static_cast<WSEvents*>(param);
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
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* scene_item = nullptr;
|
||||||
calldata_get_ptr(data, "item", &scene_item);
|
calldata_get_ptr(data, "item", &scene_item);
|
||||||
|
|
||||||
const char* scene_name =
|
const char* scene_name =
|
||||||
obs_source_get_name(obs_scene_get_source(scene));
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
const char* sceneitem_name =
|
const char* sceneitem_name =
|
||||||
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
||||||
|
|
||||||
obs_data_t* fields = obs_data_create();
|
obs_data_t* fields = obs_data_create();
|
||||||
obs_data_set_string(fields, "scene-name", scene_name);
|
obs_data_set_string(fields, "scene-name", scene_name);
|
||||||
obs_data_set_string(fields, "item-name", sceneitem_name);
|
obs_data_set_string(fields, "item-name", sceneitem_name);
|
||||||
|
|
||||||
instance->broadcastUpdate("SceneItemAdded", fields);
|
instance->broadcastUpdate("SceneItemAdded", fields);
|
||||||
|
|
||||||
obs_data_release(fields);
|
obs_data_release(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
|
void WSEvents::OnSceneItemDelete(void* param, calldata_t* data) {
|
||||||
WSEvents* instance = static_cast<WSEvents*>(param);
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
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* scene_item = nullptr;
|
||||||
calldata_get_ptr(data, "item", &scene_item);
|
calldata_get_ptr(data, "item", &scene_item);
|
||||||
|
|
||||||
const char* scene_name =
|
const char* scene_name =
|
||||||
obs_source_get_name(obs_scene_get_source(scene));
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
const char* sceneitem_name =
|
const char* sceneitem_name =
|
||||||
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
||||||
|
|
||||||
obs_data_t* fields = obs_data_create();
|
obs_data_t* fields = obs_data_create();
|
||||||
obs_data_set_string(fields, "scene-name", scene_name);
|
obs_data_set_string(fields, "scene-name", scene_name);
|
||||||
obs_data_set_string(fields, "item-name", sceneitem_name);
|
obs_data_set_string(fields, "item-name", sceneitem_name);
|
||||||
|
|
||||||
instance->broadcastUpdate("SceneItemRemoved", fields);
|
instance->broadcastUpdate("SceneItemRemoved", fields);
|
||||||
|
|
||||||
obs_data_release(fields);
|
obs_data_release(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
|
void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data) {
|
||||||
WSEvents* instance = static_cast<WSEvents*>(param);
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
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* scene_item = nullptr;
|
||||||
calldata_get_ptr(data, "item", &scene_item);
|
calldata_get_ptr(data, "item", &scene_item);
|
||||||
|
|
||||||
bool visible = false;
|
bool visible = false;
|
||||||
calldata_get_bool(data, "visible", &visible);
|
calldata_get_bool(data, "visible", &visible);
|
||||||
|
|
||||||
const char* scene_name =
|
const char* scene_name =
|
||||||
obs_source_get_name(obs_scene_get_source(scene));
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
const char* sceneitem_name =
|
const char* sceneitem_name =
|
||||||
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
||||||
|
|
||||||
obs_data_t* fields = obs_data_create();
|
obs_data_t* fields = obs_data_create();
|
||||||
obs_data_set_string(fields, "scene-name", scene_name);
|
obs_data_set_string(fields, "scene-name", scene_name);
|
||||||
obs_data_set_string(fields, "item-name", sceneitem_name);
|
obs_data_set_string(fields, "item-name", sceneitem_name);
|
||||||
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);
|
obs_data_release(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::SelectedSceneChanged(QListWidgetItem* current, QListWidgetItem* prev) {
|
void WSEvents::SelectedSceneChanged(QListWidgetItem* current, QListWidgetItem* prev) {
|
||||||
if (Utils::IsPreviewModeActive()) {
|
if (Utils::IsPreviewModeActive()) {
|
||||||
obs_scene_t* scene = Utils::SceneListItemToScene(current);
|
obs_scene_t* scene = Utils::SceneListItemToScene(current);
|
||||||
if (!scene) return;
|
if (!scene) return;
|
||||||
|
|
||||||
obs_source_t* scene_source = obs_scene_get_source(scene);
|
obs_source_t* scene_source = obs_scene_get_source(scene);
|
||||||
obs_data_array_t* scene_items = Utils::GetSceneItems(scene_source);
|
obs_data_array_t* scene_items = Utils::GetSceneItems(scene_source);
|
||||||
|
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* 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(scene_source));
|
||||||
obs_data_set_array(data, "sources", scene_items);
|
obs_data_set_array(data, "sources", scene_items);
|
||||||
|
|
||||||
broadcastUpdate("PreviewSceneChanged", data);
|
broadcastUpdate("PreviewSceneChanged", data);
|
||||||
|
|
||||||
obs_data_array_release(scene_items);
|
obs_data_array_release(scene_items);
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::ModeSwitchClicked(bool checked) {
|
void WSEvents::ModeSwitchClicked(bool checked) {
|
||||||
obs_data_t* data = obs_data_create();
|
obs_data_t* 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);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
2392
WSRequestHandler.cpp
2392
WSRequestHandler.cpp
File diff suppressed because it is too large
Load Diff
156
WSServer.cpp
156
WSServer.cpp
@ -31,68 +31,68 @@ QT_USE_NAMESPACE
|
|||||||
WSServer* WSServer::Instance = nullptr;
|
WSServer* WSServer::Instance = nullptr;
|
||||||
|
|
||||||
WSServer::WSServer(QObject* parent) :
|
WSServer::WSServer(QObject* parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
_wsServer(Q_NULLPTR),
|
_wsServer(Q_NULLPTR),
|
||||||
_clients(),
|
_clients(),
|
||||||
_clMutex(QMutex::Recursive) {
|
_clMutex(QMutex::Recursive) {
|
||||||
_serverThread = new QThread();
|
_serverThread = new QThread();
|
||||||
_wsServer = new QWebSocketServer(
|
_wsServer = new QWebSocketServer(
|
||||||
QStringLiteral("obs-websocket"),
|
QStringLiteral("obs-websocket"),
|
||||||
QWebSocketServer::NonSecureMode,
|
QWebSocketServer::NonSecureMode,
|
||||||
_serverThread);
|
_serverThread);
|
||||||
_serverThread->start();
|
_serverThread->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
WSServer::~WSServer() {
|
WSServer::~WSServer() {
|
||||||
Stop();
|
Stop();
|
||||||
delete _serverThread;
|
delete _serverThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::Start(quint16 port) {
|
void WSServer::Start(quint16 port) {
|
||||||
if (port == _wsServer->serverPort())
|
if (port == _wsServer->serverPort())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(_wsServer->isListening())
|
if(_wsServer->isListening())
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
|
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
|
||||||
if (serverStarted) {
|
if (serverStarted) {
|
||||||
connect(_wsServer, SIGNAL(newConnection()),
|
connect(_wsServer, SIGNAL(newConnection()),
|
||||||
this, SLOT(onNewConnection()));
|
this, SLOT(onNewConnection()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::Stop() {
|
void WSServer::Stop() {
|
||||||
_clMutex.lock();
|
_clMutex.lock();
|
||||||
for(QWebSocket* pClient : _clients) {
|
for(QWebSocket* pClient : _clients) {
|
||||||
pClient->close();
|
pClient->close();
|
||||||
}
|
}
|
||||||
_clMutex.unlock();
|
_clMutex.unlock();
|
||||||
|
|
||||||
_wsServer->close();
|
_wsServer->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::broadcast(QString message) {
|
void WSServer::broadcast(QString message) {
|
||||||
_clMutex.lock();
|
_clMutex.lock();
|
||||||
for(QWebSocket* pClient : _clients) {
|
for(QWebSocket* pClient : _clients) {
|
||||||
if (Config::Current()->AuthRequired
|
if (Config::Current()->AuthRequired
|
||||||
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) {
|
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false)) {
|
||||||
// Skip this client if unauthenticated
|
// Skip this client if unauthenticated
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
pClient->sendTextMessage(message);
|
pClient->sendTextMessage(message);
|
||||||
}
|
}
|
||||||
_clMutex.unlock();
|
_clMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onNewConnection() {
|
void WSServer::onNewConnection() {
|
||||||
QWebSocket* pSocket = _wsServer->nextPendingConnection();
|
QWebSocket* pSocket = _wsServer->nextPendingConnection();
|
||||||
if (pSocket) {
|
if (pSocket) {
|
||||||
connect(pSocket, &QWebSocket::textMessageReceived,
|
connect(pSocket, &QWebSocket::textMessageReceived,
|
||||||
this, &WSServer::onTextMessageReceived);
|
this, &WSServer::onTextMessageReceived);
|
||||||
connect(pSocket, &QWebSocket::disconnected,
|
connect(pSocket, &QWebSocket::disconnected,
|
||||||
this, &WSServer::onSocketDisconnected);
|
this, &WSServer::onSocketDisconnected);
|
||||||
|
|
||||||
connect(pSocket, SIGNAL(textMessageReceived(const QString&)),
|
connect(pSocket, SIGNAL(textMessageReceived(const QString&)),
|
||||||
this, SLOT(onTextMessageReceived(QString)));
|
this, SLOT(onTextMessageReceived(QString)));
|
||||||
@ -101,57 +101,57 @@ void WSServer::onNewConnection() {
|
|||||||
|
|
||||||
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
||||||
|
|
||||||
_clMutex.lock();
|
_clMutex.lock();
|
||||||
_clients << pSocket;
|
_clients << pSocket;
|
||||||
_clMutex.unlock();
|
_clMutex.unlock();
|
||||||
|
|
||||||
QHostAddress clientAddr = pSocket->peerAddress();
|
QHostAddress clientAddr = pSocket->peerAddress();
|
||||||
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
||||||
|
|
||||||
blog(LOG_INFO, "new client connection from %s:%d",
|
blog(LOG_INFO, "new client connection from %s:%d",
|
||||||
clientIp.toUtf8().constData(), pSocket->peerPort());
|
clientIp.toUtf8().constData(), pSocket->peerPort());
|
||||||
|
|
||||||
QString msg = QString(obs_module_text("OBSWebsocket.ConnectNotify.ClientIP"))
|
QString msg = QString(obs_module_text("OBSWebsocket.ConnectNotify.ClientIP"))
|
||||||
+ QString(" ")
|
+ QString(" ")
|
||||||
+ clientAddr.toString();
|
+ clientAddr.toString();
|
||||||
|
|
||||||
Utils::SysTrayNotify(msg,
|
Utils::SysTrayNotify(msg,
|
||||||
QSystemTrayIcon::Information,
|
QSystemTrayIcon::Information,
|
||||||
QString(obs_module_text("OBSWebsocket.ConnectNotify.Connected")));
|
QString(obs_module_text("OBSWebsocket.ConnectNotify.Connected")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onTextMessageReceived(QString message) {
|
void WSServer::onTextMessageReceived(QString message) {
|
||||||
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
||||||
if (pSocket) {
|
if (pSocket) {
|
||||||
WSRequestHandler handler(pSocket);
|
WSRequestHandler handler(pSocket);
|
||||||
handler.processIncomingMessage(message);
|
handler.processIncomingMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onSocketDisconnected() {
|
void WSServer::onSocketDisconnected() {
|
||||||
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
||||||
if (pSocket) {
|
if (pSocket) {
|
||||||
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
||||||
|
|
||||||
_clMutex.lock();
|
_clMutex.lock();
|
||||||
_clients.removeAll(pSocket);
|
_clients.removeAll(pSocket);
|
||||||
_clMutex.unlock();
|
_clMutex.unlock();
|
||||||
|
|
||||||
pSocket->deleteLater();
|
pSocket->deleteLater();
|
||||||
|
|
||||||
QHostAddress clientAddr = pSocket->peerAddress();
|
QHostAddress clientAddr = pSocket->peerAddress();
|
||||||
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
||||||
|
|
||||||
blog(LOG_INFO, "client %s:%d disconnected",
|
blog(LOG_INFO, "client %s:%d disconnected",
|
||||||
clientIp.toUtf8().constData(), pSocket->peerPort());
|
clientIp.toUtf8().constData(), pSocket->peerPort());
|
||||||
|
|
||||||
QString msg = QString(obs_module_text("OBSWebsocket.ConnectNotify.ClientIP"))
|
QString msg = QString(obs_module_text("OBSWebsocket.ConnectNotify.ClientIP"))
|
||||||
+ QString(" ")
|
+ QString(" ")
|
||||||
+ clientAddr.toString();
|
+ clientAddr.toString();
|
||||||
|
|
||||||
Utils::SysTrayNotify(msg,
|
Utils::SysTrayNotify(msg,
|
||||||
QSystemTrayIcon::Information,
|
QSystemTrayIcon::Information,
|
||||||
QString(obs_module_text("OBSWebsocket.ConnectNotify.Disconnected")));
|
QString(obs_module_text("OBSWebsocket.ConnectNotify.Disconnected")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class WSServer : public QObject {
|
|||||||
private slots:
|
private slots:
|
||||||
void onNewConnection();
|
void onNewConnection();
|
||||||
void onTextMessageReceived(QString message);
|
void onTextMessageReceived(QString message);
|
||||||
void onSocketDisconnected();
|
void onSocketDisconnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWebSocketServer* _wsServer;
|
QWebSocketServer* _wsServer;
|
||||||
|
@ -27,87 +27,87 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#define CHANGE_ME "changeme"
|
#define CHANGE_ME "changeme"
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget* parent) :
|
SettingsDialog::SettingsDialog(QWidget* parent) :
|
||||||
QDialog(parent, Qt::Dialog),
|
QDialog(parent, Qt::Dialog),
|
||||||
ui(new Ui::SettingsDialog)
|
ui(new Ui::SettingsDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->authRequired, &QCheckBox::stateChanged,
|
connect(ui->authRequired, &QCheckBox::stateChanged,
|
||||||
this, &SettingsDialog::AuthCheckboxChanged);
|
this, &SettingsDialog::AuthCheckboxChanged);
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
||||||
this, &SettingsDialog::FormAccepted);
|
this, &SettingsDialog::FormAccepted);
|
||||||
|
|
||||||
|
|
||||||
AuthCheckboxChanged();
|
AuthCheckboxChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::showEvent(QShowEvent* event)
|
void SettingsDialog::showEvent(QShowEvent* event)
|
||||||
{
|
{
|
||||||
Config* conf = Config::Current();
|
Config* conf = Config::Current();
|
||||||
|
|
||||||
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
||||||
ui->serverPort->setValue(conf->ServerPort);
|
ui->serverPort->setValue(conf->ServerPort);
|
||||||
|
|
||||||
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
||||||
|
|
||||||
ui->authRequired->setChecked(conf->AuthRequired);
|
ui->authRequired->setChecked(conf->AuthRequired);
|
||||||
ui->password->setText(CHANGE_ME);
|
ui->password->setText(CHANGE_ME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::ToggleShowHide()
|
void SettingsDialog::ToggleShowHide()
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
else
|
else
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::AuthCheckboxChanged()
|
void SettingsDialog::AuthCheckboxChanged()
|
||||||
{
|
{
|
||||||
if (ui->authRequired->isChecked())
|
if (ui->authRequired->isChecked())
|
||||||
ui->password->setEnabled(true);
|
ui->password->setEnabled(true);
|
||||||
else
|
else
|
||||||
ui->password->setEnabled(false);
|
ui->password->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::FormAccepted()
|
void SettingsDialog::FormAccepted()
|
||||||
{
|
{
|
||||||
Config* conf = Config::Current();
|
Config* conf = Config::Current();
|
||||||
|
|
||||||
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
||||||
conf->ServerPort = ui->serverPort->value();
|
conf->ServerPort = ui->serverPort->value();
|
||||||
|
|
||||||
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
||||||
|
|
||||||
if (ui->authRequired->isChecked())
|
if (ui->authRequired->isChecked())
|
||||||
{
|
{
|
||||||
if (ui->password->text() != CHANGE_ME)
|
if (ui->password->text() != CHANGE_ME)
|
||||||
{
|
{
|
||||||
QByteArray pwd = ui->password->text().toUtf8();
|
QByteArray pwd = ui->password->text().toUtf8();
|
||||||
const char *new_password = pwd;
|
const char *new_password = pwd;
|
||||||
|
|
||||||
conf->SetPassword(new_password);
|
conf->SetPassword(new_password);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(Config::Current()->Secret, "") != 0)
|
if (strcmp(Config::Current()->Secret, "") != 0)
|
||||||
conf->AuthRequired = true;
|
conf->AuthRequired = true;
|
||||||
else
|
else
|
||||||
conf->AuthRequired = false;
|
conf->AuthRequired = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
conf->AuthRequired = false;
|
conf->AuthRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf->Save();
|
conf->Save();
|
||||||
|
|
||||||
if (conf->ServerEnabled)
|
if (conf->ServerEnabled)
|
||||||
WSServer::Instance->Start(conf->ServerPort);
|
WSServer::Instance->Start(conf->ServerPort);
|
||||||
else
|
else
|
||||||
WSServer::Instance->Stop();
|
WSServer::Instance->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
@ -27,20 +27,20 @@ class SettingsDialog;
|
|||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SettingsDialog(QWidget* parent = 0);
|
explicit SettingsDialog(QWidget* parent = 0);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
void showEvent(QShowEvent* event);
|
void showEvent(QShowEvent* event);
|
||||||
void ToggleShowHide();
|
void ToggleShowHide();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void AuthCheckboxChanged();
|
void AuthCheckboxChanged();
|
||||||
void FormAccepted();
|
void FormAccepted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::SettingsDialog* ui;
|
Ui::SettingsDialog* ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGSDIALOG_H
|
#endif // SETTINGSDIALOG_H
|
||||||
|
@ -34,39 +34,39 @@ OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
|||||||
SettingsDialog* settings_dialog;
|
SettingsDialog* settings_dialog;
|
||||||
|
|
||||||
bool obs_module_load(void) {
|
bool obs_module_load(void) {
|
||||||
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
|
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
|
||||||
|
|
||||||
// Core setup
|
// Core setup
|
||||||
Config* config = Config::Current();
|
Config* config = Config::Current();
|
||||||
config->Load();
|
config->Load();
|
||||||
|
|
||||||
WSServer::Instance = new WSServer();
|
WSServer::Instance = new WSServer();
|
||||||
WSEvents::Instance = new WSEvents(WSServer::Instance);
|
WSEvents::Instance = new WSEvents(WSServer::Instance);
|
||||||
|
|
||||||
if (config->ServerEnabled)
|
if (config->ServerEnabled)
|
||||||
WSServer::Instance->Start(config->ServerPort);
|
WSServer::Instance->Start(config->ServerPort);
|
||||||
|
|
||||||
// UI setup
|
// UI setup
|
||||||
QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
QAction* menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
||||||
obs_module_text("OBSWebsocket.Menu.SettingsItem"));
|
obs_module_text("OBSWebsocket.Menu.SettingsItem"));
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
|
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
settings_dialog = new SettingsDialog(main_window);
|
settings_dialog = new SettingsDialog(main_window);
|
||||||
obs_frontend_pop_ui_translation();
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
auto menu_cb = [] {
|
auto menu_cb = [] {
|
||||||
settings_dialog->ToggleShowHide();
|
settings_dialog->ToggleShowHide();
|
||||||
};
|
};
|
||||||
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
||||||
|
|
||||||
// Loading finished
|
// Loading finished
|
||||||
blog(LOG_INFO, "module loaded!");
|
blog(LOG_INFO, "module loaded!");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void obs_module_unload() {
|
void obs_module_unload() {
|
||||||
blog(LOG_INFO, "goodbye!");
|
blog(LOG_INFO, "goodbye!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user