diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index d61b44631..22900db43 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -140,6 +140,8 @@ target_sources( crash-report.cpp crash-report.hpp display-helpers.hpp + streaming-helpers.cpp + streaming-helpers.hpp platform.hpp qt-display.cpp qt-display.hpp diff --git a/UI/streaming-helpers.cpp b/UI/streaming-helpers.cpp new file mode 100644 index 000000000..aecdde2f2 --- /dev/null +++ b/UI/streaming-helpers.cpp @@ -0,0 +1,67 @@ +#include "streaming-helpers.hpp" + +#include "../plugins/rtmp-services/rtmp-format-ver.h" + +#include +#include +#include + +using namespace json11; + +static Json open_json_file(const char *path) +{ + BPtr file_data = os_quick_read_utf8_file(path); + if (!file_data) + return Json(); + + std::string err; + Json json = Json::parse(file_data, err); + if (json["format_version"].int_value() != RTMP_SERVICES_FORMAT_VERSION) + return Json(); + return json; +} + +static inline bool name_matches(const Json &service, const char *name) +{ + if (service["name"].string_value() == name) + return true; + + auto &alt_names = service["alt_names"].array_items(); + for (const Json &alt_name : alt_names) { + if (alt_name.string_value() == name) { + return true; + } + } + + return false; +} + +Json get_services_json() +{ + obs_module_t *mod = obs_get_module("rtmp-services"); + Json root; + + BPtr file = obs_module_get_config_path(mod, "services.json"); + if (file) + root = open_json_file(file); + + if (root.is_null()) { + file = obs_find_module_file(mod, "services.json"); + if (file) + root = open_json_file(file); + } + + return root; +} + +Json get_service_from_json(Json &root, const char *name) +{ + auto &services = root["services"].array_items(); + for (const Json &service : services) { + if (name_matches(service, name)) { + return service; + } + } + + return Json(); +} diff --git a/UI/streaming-helpers.hpp b/UI/streaming-helpers.hpp new file mode 100644 index 000000000..fa47ea7fc --- /dev/null +++ b/UI/streaming-helpers.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include + +extern json11::Json get_services_json(); +extern json11::Json get_service_from_json(json11::Json &root, const char *name); diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index 386d3290e..71ffc2f29 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -11,6 +11,7 @@ #include "window-basic-auto-config.hpp" #include "window-basic-main.hpp" +#include "streaming-helpers.hpp" #include "qt-wrappers.hpp" #include "obs-app.hpp" @@ -19,6 +20,7 @@ #define wiz reinterpret_cast(wizard()) using namespace std; +using namespace json11; /* ------------------------------------------------------------------------- */ @@ -119,28 +121,19 @@ void AutoConfigTestPage::StartRecordingEncoderStage() void AutoConfigTestPage::GetServers(std::vector &servers) { - OBSDataAutoRelease settings = obs_data_create(); - obs_data_set_string(settings, "service", wiz->serviceName.c_str()); + Json root = get_services_json(); + Json service = get_service_from_json(root, wiz->serviceName.c_str()); - obs_properties_t *ppts = obs_get_service_properties("rtmp_common"); - obs_property_t *p = obs_properties_get(ppts, "service"); - obs_property_modified(p, settings); + auto &json_services = service["servers"].array_items(); + for (const Json &server : json_services) { + const std::string &name = server["name"].string_value(); + const std::string &url = server["url"].string_value(); - p = obs_properties_get(ppts, "server"); - size_t count = obs_property_list_item_count(p); - servers.reserve(count); - - for (size_t i = 0; i < count; i++) { - const char *name = obs_property_list_item_name(p, i); - const char *server = obs_property_list_item_string(p, i); - - if (wiz->CanTestServer(name)) { - ServerInfo info(name, server); + if (wiz->CanTestServer(name.c_str())) { + ServerInfo info(name, url); servers.push_back(info); } } - - obs_properties_destroy(ppts); } static inline void string_depad_key(string &key) diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index eb4e72b99..4ec8c781d 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -8,6 +8,7 @@ #include "qt-wrappers.hpp" #include "obs-app.hpp" #include "url-push-button.hpp" +#include "streaming-helpers.hpp" #include "ui_AutoConfigStartPage.h" #include "ui_AutoConfigVideoPage.h" @@ -23,6 +24,8 @@ #include "youtube-api-wrappers.hpp" #endif +using namespace json11; + struct QCef; struct QCefCookieManager; @@ -672,6 +675,14 @@ void AutoConfigStreamPage::ServiceChanged() UpdateCompleted(); } +inline void AutoConfigStreamPage::GetServicesJson() +{ + if (!servicesLoaded && servicesRoot.is_null()) { + servicesRoot = get_services_json(); + servicesLoaded = true; + } +} + void AutoConfigStreamPage::UpdateMoreInfoLink() { if (IsCustomService()) { @@ -680,42 +691,32 @@ void AutoConfigStreamPage::UpdateMoreInfoLink() } QString serviceName = ui->service->currentText(); - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_property_t *services = obs_properties_get(props, "service"); - OBSDataAutoRelease settings = obs_data_create(); + GetServicesJson(); + Json service = + get_service_from_json(servicesRoot, QT_TO_UTF8(serviceName)); - obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); - obs_property_modified(services, settings); + const std::string &more_info_link = + service["more_info_link"].string_value(); - const char *more_info_link = - obs_data_get_string(settings, "more_info_link"); - - if (!more_info_link || (*more_info_link == '\0')) { + if (more_info_link.empty()) { ui->moreInfoButton->hide(); } else { - ui->moreInfoButton->setTargetUrl(QUrl(more_info_link)); + ui->moreInfoButton->setTargetUrl(QUrl(more_info_link.c_str())); ui->moreInfoButton->show(); } - obs_properties_destroy(props); } void AutoConfigStreamPage::UpdateKeyLink() { QString serviceName = ui->service->currentText(); QString customServer = ui->customServer->text().trimmed(); - QString streamKeyLink; - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_property_t *services = obs_properties_get(props, "service"); - - OBSDataAutoRelease settings = obs_data_create(); - - obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); - obs_property_modified(services, settings); - - streamKeyLink = obs_data_get_string(settings, "stream_key_link"); + GetServicesJson(); + Json service = + get_service_from_json(servicesRoot, QT_TO_UTF8(serviceName)); + std::string streamKeyLink = service["stream_key_link"].string_value(); if (customServer.contains("fbcdn.net") && IsCustomService()) { streamKeyLink = "https://www.facebook.com/live/producer?ref=OBS"; @@ -729,37 +730,28 @@ void AutoConfigStreamPage::UpdateKeyLink() QTStr("Basic.AutoConfig.StreamPage.StreamKey")); } - if (QString(streamKeyLink).isNull() || - QString(streamKeyLink).isEmpty()) { + if (streamKeyLink.empty()) { ui->streamKeyButton->hide(); } else { - ui->streamKeyButton->setTargetUrl(QUrl(streamKeyLink)); + ui->streamKeyButton->setTargetUrl(QUrl(streamKeyLink.c_str())); ui->streamKeyButton->show(); } - obs_properties_destroy(props); } void AutoConfigStreamPage::LoadServices(bool showAll) { - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - - OBSDataAutoRelease settings = obs_data_create(); - - obs_data_set_bool(settings, "show_all", showAll); - - obs_property_t *prop = obs_properties_get(props, "show_all"); - obs_property_modified(prop, settings); + GetServicesJson(); + auto &services = servicesRoot["services"].array_items(); ui->service->blockSignals(true); ui->service->clear(); QStringList names; - obs_property_t *services = obs_properties_get(props, "service"); - size_t services_count = obs_property_list_item_count(services); - for (size_t i = 0; i < services_count; i++) { - const char *name = obs_property_list_item_string(services, i); - names.push_back(name); + for (const Json &service : services) { + if (!showAll && !service["common"].bool_value()) + continue; + names.push_back(service["name"].string_value().c_str()); } if (showAll) @@ -784,8 +776,6 @@ void AutoConfigStreamPage::LoadServices(bool showAll) ui->service->setCurrentIndex(idx); } - obs_properties_destroy(props); - ui->service->blockSignals(false); } @@ -803,26 +793,17 @@ void AutoConfigStreamPage::UpdateServerList() lastService = serviceName; } - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_property_t *services = obs_properties_get(props, "service"); - - OBSDataAutoRelease settings = obs_data_create(); - - obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); - obs_property_modified(services, settings); - - obs_property_t *servers = obs_properties_get(props, "server"); + GetServicesJson(); + Json service = + get_service_from_json(servicesRoot, QT_TO_UTF8(serviceName)); ui->server->clear(); - size_t servers_count = obs_property_list_item_count(servers); - for (size_t i = 0; i < servers_count; i++) { - const char *name = obs_property_list_item_name(servers, i); - const char *server = obs_property_list_item_string(servers, i); - ui->server->addItem(name, server); + auto &servers = service["servers"].array_items(); + for (const Json &server : servers) { + ui->server->addItem(server["name"].string_value().c_str(), + server["url"].string_value().c_str()); } - - obs_properties_destroy(props); } void AutoConfigStreamPage::UpdateCompleted() @@ -886,18 +867,10 @@ AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent) /* ----------------------------------------- */ /* check to see if Twitch's "auto" available */ - OBSDataAutoRelease twitchSettings = obs_data_create(); - - obs_data_set_string(twitchSettings, "service", "Twitch"); - - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_properties_apply_settings(props, twitchSettings); - - obs_property_t *p = obs_properties_get(props, "server"); - const char *first = obs_property_list_item_string(p, 0); - twitchAuto = strcmp(first, "auto") == 0; - - obs_properties_destroy(props); + Json servicesRoot = get_services_json(); + Json serviceJson = get_service_from_json(servicesRoot, "Twitch"); + Json servers = serviceJson["servers"]; + twitchAuto = servers[0]["url"].string_value() == "auto"; /* ----------------------------------------- */ /* load service/servers */ diff --git a/UI/window-basic-auto-config.hpp b/UI/window-basic-auto-config.hpp index e88d5b502..d84777fae 100644 --- a/UI/window-basic-auto-config.hpp +++ b/UI/window-basic-auto-config.hpp @@ -13,6 +13,8 @@ #include #include +#include + class Ui_AutoConfigStartPage; class Ui_AutoConfigVideoPage; class Ui_AutoConfigStreamPage; @@ -174,6 +176,10 @@ class AutoConfigStreamPage : public QWizardPage { QString lastService; bool ready = false; + inline void GetServicesJson(); + json11::Json servicesRoot; + bool servicesLoaded = false; + void LoadServices(bool showAll); inline bool IsCustomService() const; @@ -248,7 +254,8 @@ class AutoConfigTestPage : public QWizardPage { inline ServerInfo() {} - inline ServerInfo(const char *name_, const char *address_) + inline ServerInfo(const std::string &name_, + const std::string &address_) : name(name_), address(address_) { } diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 3e4eb15c0..0bfef21e2 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -2,6 +2,7 @@ #include #include "window-basic-settings.hpp" +#include "streaming-helpers.hpp" #include "obs-frontend-api.h" #include "obs-app.hpp" #include "window-basic-main.hpp" @@ -20,6 +21,8 @@ #include "youtube-api-wrappers.hpp" #endif +using namespace json11; + struct QCef; struct QCefCookieManager; @@ -246,6 +249,14 @@ void OBSBasicSettings::SaveStream1Settings() SaveCheckBox(ui->ignoreRecommended, "Stream1", "IgnoreRecommended"); } +inline void OBSBasicSettings::GetServicesJson() +{ + if (!servicesLoaded && servicesRoot.is_null()) { + servicesRoot = get_services_json(); + servicesLoaded = true; + } +} + void OBSBasicSettings::UpdateMoreInfoLink() { if (IsCustomService()) { @@ -254,42 +265,32 @@ void OBSBasicSettings::UpdateMoreInfoLink() } QString serviceName = ui->service->currentText(); - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_property_t *services = obs_properties_get(props, "service"); - OBSDataAutoRelease settings = obs_data_create(); + GetServicesJson(); + Json service = + get_service_from_json(servicesRoot, QT_TO_UTF8(serviceName)); - obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); - obs_property_modified(services, settings); + const std::string &more_info_link = + service["more_info_link"].string_value(); - const char *more_info_link = - obs_data_get_string(settings, "more_info_link"); - - if (!more_info_link || (*more_info_link == '\0')) { + if (more_info_link.empty()) { ui->moreInfoButton->hide(); } else { - ui->moreInfoButton->setTargetUrl(QUrl(more_info_link)); + ui->moreInfoButton->setTargetUrl(QUrl(more_info_link.c_str())); ui->moreInfoButton->show(); } - obs_properties_destroy(props); } void OBSBasicSettings::UpdateKeyLink() { QString serviceName = ui->service->currentText(); QString customServer = ui->customServer->text().trimmed(); - QString streamKeyLink; - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_property_t *services = obs_properties_get(props, "service"); - - OBSDataAutoRelease settings = obs_data_create(); - - obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); - obs_property_modified(services, settings); - - streamKeyLink = obs_data_get_string(settings, "stream_key_link"); + GetServicesJson(); + Json service = + get_service_from_json(servicesRoot, QT_TO_UTF8(serviceName)); + std::string streamKeyLink = service["stream_key_link"].string_value(); if (customServer.contains("fbcdn.net") && IsCustomService()) { streamKeyLink = "https://www.facebook.com/live/producer?ref=OBS"; @@ -303,37 +304,29 @@ void OBSBasicSettings::UpdateKeyLink() QTStr("Basic.AutoConfig.StreamPage.StreamKey")); } - if (QString(streamKeyLink).isNull() || - QString(streamKeyLink).isEmpty()) { + if (streamKeyLink.empty()) { ui->getStreamKeyButton->hide(); } else { - ui->getStreamKeyButton->setTargetUrl(QUrl(streamKeyLink)); + ui->getStreamKeyButton->setTargetUrl( + QUrl(streamKeyLink.c_str())); ui->getStreamKeyButton->show(); } - obs_properties_destroy(props); } void OBSBasicSettings::LoadServices(bool showAll) { - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - - OBSDataAutoRelease settings = obs_data_create(); - - obs_data_set_bool(settings, "show_all", showAll); - - obs_property_t *prop = obs_properties_get(props, "show_all"); - obs_property_modified(prop, settings); + GetServicesJson(); + auto &services = servicesRoot["services"].array_items(); ui->service->blockSignals(true); ui->service->clear(); QStringList names; - obs_property_t *services = obs_properties_get(props, "service"); - size_t services_count = obs_property_list_item_count(services); - for (size_t i = 0; i < services_count; i++) { - const char *name = obs_property_list_item_string(services, i); - names.push_back(name); + for (const Json &service : services) { + if (!showAll && !service["common"].bool_value()) + continue; + names.push_back(service["name"].string_value().c_str()); } if (showAll) @@ -358,8 +351,6 @@ void OBSBasicSettings::LoadServices(bool showAll) ui->service->setCurrentIndex(idx); } - obs_properties_destroy(props); - ui->service->blockSignals(false); } @@ -498,26 +489,17 @@ void OBSBasicSettings::UpdateServerList() lastService = serviceName; } - obs_properties_t *props = obs_get_service_properties("rtmp_common"); - obs_property_t *services = obs_properties_get(props, "service"); - - OBSDataAutoRelease settings = obs_data_create(); - - obs_data_set_string(settings, "service", QT_TO_UTF8(serviceName)); - obs_property_modified(services, settings); - - obs_property_t *servers = obs_properties_get(props, "server"); + GetServicesJson(); + Json service = + get_service_from_json(servicesRoot, QT_TO_UTF8(serviceName)); ui->server->clear(); - size_t servers_count = obs_property_list_item_count(servers); - for (size_t i = 0; i < servers_count; i++) { - const char *name = obs_property_list_item_name(servers, i); - const char *server = obs_property_list_item_string(servers, i); - ui->server->addItem(name, server); + auto &servers = service["servers"].array_items(); + for (const Json &server : servers) { + ui->server->addItem(server["name"].string_value().c_str(), + server["url"].string_value().c_str()); } - - obs_properties_destroy(props); } void OBSBasicSettings::on_show_clicked() diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 374d12a55..54ad0156a 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -41,6 +41,8 @@ class OBSHotkeyWidget; #include "ui_OBSBasicSettings.h" +#include + #define VOLUME_METER_DECAY_FAST 23.53 #define VOLUME_METER_DECAY_MEDIUM 11.76 #define VOLUME_METER_DECAY_SLOW 8.57 @@ -107,6 +109,10 @@ private: std::shared_ptr auth; + inline void GetServicesJson(); + json11::Json servicesRoot; + bool servicesLoaded = false; + bool generalChanged = false; bool stream1Changed = false; bool outputsChanged = false;