General: refactor continued

This commit is contained in:
Stéphane L 2017-08-05 03:21:28 +02:00
parent 586f9076f0
commit dba599c127
12 changed files with 2113 additions and 2113 deletions

View File

@ -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
View File

@ -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;
} }

View File

@ -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();

View File

@ -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);
} }

File diff suppressed because it is too large Load Diff

View File

@ -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")));
} }
} }

View File

@ -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;

View File

@ -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;
} }

View File

@ -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

View File

@ -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!");
} }