AutoConfig.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #include "AutoConfig.hpp"
  2. #include "AutoConfigStartPage.hpp"
  3. #include "AutoConfigStreamPage.hpp"
  4. #include "AutoConfigTestPage.hpp"
  5. #include "AutoConfigVideoPage.hpp"
  6. #include "ui_AutoConfigStartPage.h"
  7. #include "ui_AutoConfigStreamPage.h"
  8. #include "ui_AutoConfigVideoPage.h"
  9. #ifdef YOUTUBE_ENABLED
  10. #include <docks/YouTubeAppDock.hpp>
  11. #include <utility/YoutubeApiWrappers.hpp>
  12. #endif
  13. #include <widgets/OBSBasic.hpp>
  14. #include <qt-wrappers.hpp>
  15. #include "moc_AutoConfig.cpp"
  16. constexpr std::string_view OBSServiceFileName = "service.json";
  17. enum class ListOpt : int {
  18. ShowAll = 1,
  19. Custom,
  20. };
  21. static OBSData OpenServiceSettings(std::string &type)
  22. {
  23. const OBSBasic *basic = OBSBasic::Get();
  24. const OBSProfile &currentProfile = basic->GetCurrentProfile();
  25. const std::filesystem::path jsonFilePath = currentProfile.path / std::filesystem::u8path(OBSServiceFileName);
  26. if (!std::filesystem::exists(jsonFilePath)) {
  27. return OBSData();
  28. }
  29. OBSDataAutoRelease data = obs_data_create_from_json_file_safe(jsonFilePath.u8string().c_str(), "bak");
  30. obs_data_set_default_string(data, "type", "rtmp_common");
  31. type = obs_data_get_string(data, "type");
  32. OBSDataAutoRelease settings = obs_data_get_obj(data, "settings");
  33. return settings.Get();
  34. }
  35. static void GetServiceInfo(std::string &type, std::string &service, std::string &server, std::string &key)
  36. {
  37. OBSData settings = OpenServiceSettings(type);
  38. service = obs_data_get_string(settings, "service");
  39. server = obs_data_get_string(settings, "server");
  40. key = obs_data_get_string(settings, "key");
  41. }
  42. AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent)
  43. {
  44. EnableThreadedMessageBoxes(true);
  45. calldata_t cd = {0};
  46. calldata_set_int(&cd, "seconds", 5);
  47. proc_handler_t *ph = obs_get_proc_handler();
  48. proc_handler_call(ph, "twitch_ingests_refresh", &cd);
  49. proc_handler_call(ph, "amazon_ivs_ingests_refresh", &cd);
  50. calldata_free(&cd);
  51. OBSBasic *main = OBSBasic::Get();
  52. main->EnableOutputs(false);
  53. installEventFilter(CreateShortcutFilter());
  54. std::string serviceType;
  55. GetServiceInfo(serviceType, serviceName, server, key);
  56. #if defined(_WIN32) || defined(__APPLE__)
  57. setWizardStyle(QWizard::ModernStyle);
  58. #endif
  59. streamPage = new AutoConfigStreamPage();
  60. setPage(StartPage, new AutoConfigStartPage());
  61. setPage(VideoPage, new AutoConfigVideoPage());
  62. setPage(StreamPage, streamPage);
  63. setPage(TestPage, new AutoConfigTestPage());
  64. setWindowTitle(QTStr("Basic.AutoConfig"));
  65. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  66. obs_video_info ovi;
  67. obs_get_video_info(&ovi);
  68. baseResolutionCX = ovi.base_width;
  69. baseResolutionCY = ovi.base_height;
  70. /* ----------------------------------------- */
  71. /* check to see if Twitch's "auto" available */
  72. OBSDataAutoRelease twitchSettings = obs_data_create();
  73. obs_data_set_string(twitchSettings, "service", "Twitch");
  74. obs_properties_t *props = obs_get_service_properties("rtmp_common");
  75. obs_properties_apply_settings(props, twitchSettings);
  76. obs_property_t *p = obs_properties_get(props, "server");
  77. const char *first = obs_property_list_item_string(p, 0);
  78. twitchAuto = strcmp(first, "auto") == 0;
  79. obs_properties_destroy(props);
  80. /* ----------------------------------------- */
  81. /* check to see if Amazon IVS "auto" entries are available */
  82. OBSDataAutoRelease amazonIVSSettings = obs_data_create();
  83. obs_data_set_string(amazonIVSSettings, "service", "Amazon IVS");
  84. props = obs_get_service_properties("rtmp_common");
  85. obs_properties_apply_settings(props, amazonIVSSettings);
  86. p = obs_properties_get(props, "server");
  87. first = obs_property_list_item_string(p, 0);
  88. amazonIVSAuto = strncmp(first, "auto", 4) == 0;
  89. obs_properties_destroy(props);
  90. /* ----------------------------------------- */
  91. /* load service/servers */
  92. customServer = serviceType == "rtmp_custom";
  93. QComboBox *serviceList = streamPage->ui->service;
  94. if (!serviceName.empty()) {
  95. serviceList->blockSignals(true);
  96. int count = serviceList->count();
  97. bool found = false;
  98. for (int i = 0; i < count; i++) {
  99. QString name = serviceList->itemText(i);
  100. if (name == serviceName.c_str()) {
  101. serviceList->setCurrentIndex(i);
  102. found = true;
  103. break;
  104. }
  105. }
  106. if (!found) {
  107. serviceList->insertItem(0, serviceName.c_str());
  108. serviceList->setCurrentIndex(0);
  109. }
  110. serviceList->blockSignals(false);
  111. }
  112. streamPage->UpdateServerList();
  113. streamPage->UpdateKeyLink();
  114. streamPage->UpdateMoreInfoLink();
  115. streamPage->lastService.clear();
  116. if (!customServer) {
  117. QComboBox *serverList = streamPage->ui->server;
  118. int idx = serverList->findData(QString(server.c_str()));
  119. if (idx == -1)
  120. idx = 0;
  121. serverList->setCurrentIndex(idx);
  122. } else {
  123. streamPage->ui->customServer->setText(server.c_str());
  124. int idx = streamPage->ui->service->findData(QVariant((int)ListOpt::Custom));
  125. streamPage->ui->service->setCurrentIndex(idx);
  126. }
  127. if (!key.empty())
  128. streamPage->ui->key->setText(key.c_str());
  129. TestHardwareEncoding();
  130. int bitrate = config_get_int(main->Config(), "SimpleOutput", "VBitrate");
  131. bool multitrackVideoEnabled = config_has_user_value(main->Config(), "Stream1", "EnableMultitrackVideo")
  132. ? config_get_bool(main->Config(), "Stream1", "EnableMultitrackVideo")
  133. : true;
  134. streamPage->ui->bitrate->setValue(bitrate);
  135. streamPage->ui->useMultitrackVideo->setChecked(hardwareEncodingAvailable && multitrackVideoEnabled);
  136. streamPage->ServiceChanged();
  137. if (!hardwareEncodingAvailable) {
  138. delete streamPage->ui->preferHardware;
  139. streamPage->ui->preferHardware = nullptr;
  140. } else {
  141. /* Newer generations of NVENC have a high enough quality to
  142. * bitrate ratio that if NVENC is available, it makes sense to
  143. * just always prefer hardware encoding by default */
  144. bool preferHardware = nvencAvailable || appleAvailable || os_get_physical_cores() <= 4;
  145. streamPage->ui->preferHardware->setChecked(preferHardware);
  146. }
  147. setOptions(QWizard::WizardOptions());
  148. setButtonText(QWizard::FinishButton, QTStr("Basic.AutoConfig.ApplySettings"));
  149. setButtonText(QWizard::BackButton, QTStr("Back"));
  150. setButtonText(QWizard::NextButton, QTStr("Next"));
  151. setButtonText(QWizard::CancelButton, QTStr("Cancel"));
  152. }
  153. AutoConfig::~AutoConfig()
  154. {
  155. OBSBasic *main = OBSBasic::Get();
  156. main->EnableOutputs(true);
  157. EnableThreadedMessageBoxes(false);
  158. }
  159. void AutoConfig::TestHardwareEncoding()
  160. {
  161. size_t idx = 0;
  162. const char *id;
  163. while (obs_enum_encoder_types(idx++, &id)) {
  164. if (strcmp(id, "ffmpeg_nvenc") == 0)
  165. hardwareEncodingAvailable = nvencAvailable = true;
  166. else if (strcmp(id, "obs_qsv11") == 0)
  167. hardwareEncodingAvailable = qsvAvailable = true;
  168. else if (strcmp(id, "h264_texture_amf") == 0)
  169. hardwareEncodingAvailable = vceAvailable = true;
  170. #ifdef __APPLE__
  171. else if (strcmp(id, "com.apple.videotoolbox.videoencoder.ave.avc") == 0
  172. #ifndef __aarch64__
  173. && os_get_emulation_status() == true
  174. #endif
  175. )
  176. if (__builtin_available(macOS 13.0, *))
  177. hardwareEncodingAvailable = appleAvailable = true;
  178. #endif
  179. }
  180. }
  181. bool AutoConfig::CanTestServer(const char *server)
  182. {
  183. if (!testRegions || (regionUS && regionEU && regionAsia && regionOther))
  184. return true;
  185. if (service == Service::Twitch) {
  186. if (astrcmp_n(server, "US West:", 8) == 0 || astrcmp_n(server, "US East:", 8) == 0 ||
  187. astrcmp_n(server, "US Central:", 11) == 0) {
  188. return regionUS;
  189. } else if (astrcmp_n(server, "EU:", 3) == 0) {
  190. return regionEU;
  191. } else if (astrcmp_n(server, "Asia:", 5) == 0) {
  192. return regionAsia;
  193. } else if (regionOther) {
  194. return true;
  195. }
  196. } else {
  197. return true;
  198. }
  199. return false;
  200. }
  201. void AutoConfig::done(int result)
  202. {
  203. QWizard::done(result);
  204. if (result == QDialog::Accepted) {
  205. if (type == Type::Streaming)
  206. SaveStreamSettings();
  207. SaveSettings();
  208. #ifdef YOUTUBE_ENABLED
  209. if (YouTubeAppDock::IsYTServiceSelected()) {
  210. OBSBasic *main = OBSBasic::Get();
  211. main->NewYouTubeAppDock();
  212. }
  213. #endif
  214. }
  215. }
  216. inline const char *AutoConfig::GetEncoderId(Encoder enc)
  217. {
  218. switch (enc) {
  219. case Encoder::NVENC:
  220. return SIMPLE_ENCODER_NVENC;
  221. case Encoder::QSV:
  222. return SIMPLE_ENCODER_QSV;
  223. case Encoder::AMD:
  224. return SIMPLE_ENCODER_AMD;
  225. case Encoder::Apple:
  226. return SIMPLE_ENCODER_APPLE_H264;
  227. default:
  228. return SIMPLE_ENCODER_X264;
  229. }
  230. };
  231. void AutoConfig::SaveStreamSettings()
  232. {
  233. OBSBasic *main = OBSBasic::Get();
  234. /* ---------------------------------- */
  235. /* save service */
  236. const char *service_id = customServer ? "rtmp_custom" : "rtmp_common";
  237. obs_service_t *oldService = main->GetService();
  238. OBSDataAutoRelease hotkeyData = obs_hotkeys_save_service(oldService);
  239. OBSDataAutoRelease settings = obs_data_create();
  240. if (!customServer)
  241. obs_data_set_string(settings, "service", serviceName.c_str());
  242. obs_data_set_string(settings, "server", server.c_str());
  243. #ifdef YOUTUBE_ENABLED
  244. if (!streamPage->auth || !IsYouTubeService(serviceName))
  245. obs_data_set_string(settings, "key", key.c_str());
  246. #else
  247. obs_data_set_string(settings, "key", key.c_str());
  248. #endif
  249. OBSServiceAutoRelease newService = obs_service_create(service_id, "default_service", settings, hotkeyData);
  250. if (!newService)
  251. return;
  252. main->SetService(newService);
  253. main->SaveService();
  254. main->auth = streamPage->auth;
  255. if (!!main->auth) {
  256. main->auth->LoadUI();
  257. main->SetBroadcastFlowEnabled(main->auth->broadcastFlow());
  258. } else {
  259. main->SetBroadcastFlowEnabled(false);
  260. }
  261. /* ---------------------------------- */
  262. /* save stream settings */
  263. config_set_int(main->Config(), "SimpleOutput", "VBitrate", idealBitrate);
  264. config_set_string(main->Config(), "SimpleOutput", "StreamEncoder", GetEncoderId(streamingEncoder));
  265. config_remove_value(main->Config(), "SimpleOutput", "UseAdvanced");
  266. config_set_bool(main->Config(), "Stream1", "EnableMultitrackVideo", multitrackVideo.testSuccessful);
  267. if (multitrackVideo.targetBitrate.has_value())
  268. config_set_int(main->Config(), "Stream1", "MultitrackVideoTargetBitrate",
  269. *multitrackVideo.targetBitrate);
  270. else
  271. config_remove_value(main->Config(), "Stream1", "MultitrackVideoTargetBitrate");
  272. if (multitrackVideo.bitrate.has_value() && multitrackVideo.targetBitrate.has_value() &&
  273. (static_cast<double>(*multitrackVideo.bitrate) / *multitrackVideo.targetBitrate) >= 0.90) {
  274. config_set_bool(main->Config(), "Stream1", "MultitrackVideoMaximumAggregateBitrateAuto", true);
  275. } else if (multitrackVideo.bitrate.has_value()) {
  276. config_set_bool(main->Config(), "Stream1", "MultitrackVideoMaximumAggregateBitrateAuto", false);
  277. config_set_int(main->Config(), "Stream1", "MultitrackVideoMaximumAggregateBitrate",
  278. *multitrackVideo.bitrate);
  279. }
  280. }
  281. void AutoConfig::SaveSettings()
  282. {
  283. OBSBasic *main = OBSBasic::Get();
  284. if (recordingEncoder != Encoder::Stream)
  285. config_set_string(main->Config(), "SimpleOutput", "RecEncoder", GetEncoderId(recordingEncoder));
  286. const char *quality = recordingQuality == Quality::High ? "Small" : "Stream";
  287. config_set_string(main->Config(), "Output", "Mode", "Simple");
  288. config_set_string(main->Config(), "SimpleOutput", "RecQuality", quality);
  289. config_set_int(main->Config(), "Video", "BaseCX", baseResolutionCX);
  290. config_set_int(main->Config(), "Video", "BaseCY", baseResolutionCY);
  291. config_set_int(main->Config(), "Video", "OutputCX", idealResolutionCX);
  292. config_set_int(main->Config(), "Video", "OutputCY", idealResolutionCY);
  293. if (fpsType != FPSType::UseCurrent) {
  294. config_set_uint(main->Config(), "Video", "FPSType", 0);
  295. config_set_string(main->Config(), "Video", "FPSCommon", std::to_string(idealFPSNum).c_str());
  296. }
  297. main->ResetVideo();
  298. main->ResetOutputs();
  299. config_save_safe(main->Config(), "tmp", nullptr);
  300. }