window-basic-settings.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432
  1. /******************************************************************************
  2. Copyright (C) 2013-2014 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include <obs.hpp>
  15. #include <util/util.hpp>
  16. #include <util/lexer.h>
  17. #include <sstream>
  18. #include <QLineEdit>
  19. #include <QMessageBox>
  20. #include <QCloseEvent>
  21. #include <QFileDialog>
  22. #include "obs-app.hpp"
  23. #include "platform.hpp"
  24. #include "properties-view.hpp"
  25. #include "qt-wrappers.hpp"
  26. #include "window-basic-main.hpp"
  27. #include "window-basic-settings.hpp"
  28. #include <util/platform.h>
  29. using namespace std;
  30. /* parses "[width]x[height]", string, i.e. 1024x768 */
  31. static bool ConvertResText(const char *res, uint32_t &cx, uint32_t &cy)
  32. {
  33. BaseLexer lex;
  34. base_token token;
  35. lexer_start(lex, res);
  36. /* parse width */
  37. if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))
  38. return false;
  39. if (token.type != BASETOKEN_DIGIT)
  40. return false;
  41. cx = std::stoul(token.text.array);
  42. /* parse 'x' */
  43. if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))
  44. return false;
  45. if (strref_cmpi(&token.text, "x") != 0)
  46. return false;
  47. /* parse height */
  48. if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))
  49. return false;
  50. if (token.type != BASETOKEN_DIGIT)
  51. return false;
  52. cy = std::stoul(token.text.array);
  53. /* shouldn't be any more tokens after this */
  54. if (lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))
  55. return false;
  56. return true;
  57. }
  58. static inline bool WidgetChanged(QWidget *widget)
  59. {
  60. return widget->property("changed").toBool();
  61. }
  62. static inline void SetComboByName(QComboBox *combo, const char *name)
  63. {
  64. int idx = combo->findText(QT_UTF8(name));
  65. if (idx != -1)
  66. combo->setCurrentIndex(idx);
  67. }
  68. static inline void SetComboByValue(QComboBox *combo, const char *name)
  69. {
  70. int idx = combo->findData(QT_UTF8(name));
  71. if (idx != -1)
  72. combo->setCurrentIndex(idx);
  73. }
  74. static inline QString GetComboData(QComboBox *combo)
  75. {
  76. int idx = combo->currentIndex();
  77. if (idx == -1)
  78. return QString();
  79. return combo->itemData(idx).toString();
  80. }
  81. void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
  82. const char *slot)
  83. {
  84. QObject::connect(widget, signal, this, slot);
  85. widget->setProperty("changed", QVariant(false));
  86. }
  87. #define COMBO_CHANGED SIGNAL(currentIndexChanged(int))
  88. #define EDIT_CHANGED SIGNAL(textChanged(const QString &))
  89. #define CBEDIT_CHANGED SIGNAL(editTextChanged(const QString &))
  90. #define CHECK_CHANGED SIGNAL(clicked(bool))
  91. #define SCROLL_CHANGED SIGNAL(valueChanged(int))
  92. #define GENERAL_CHANGED SLOT(GeneralChanged())
  93. #define STREAM1_CHANGED SLOT(Stream1Changed())
  94. #define OUTPUTS_CHANGED SLOT(OutputsChanged())
  95. #define AUDIO_RESTART SLOT(AudioChangedRestart())
  96. #define AUDIO_CHANGED SLOT(AudioChanged())
  97. #define VIDEO_RESTART SLOT(VideoChangedRestart())
  98. #define VIDEO_RES SLOT(VideoChangedResolution())
  99. #define VIDEO_CHANGED SLOT(VideoChanged())
  100. OBSBasicSettings::OBSBasicSettings(QWidget *parent)
  101. : QDialog (parent),
  102. main (qobject_cast<OBSBasic*>(parent)),
  103. ui (new Ui::OBSBasicSettings)
  104. {
  105. string path;
  106. ui->setupUi(this);
  107. HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED);
  108. HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED);
  109. HookWidget(ui->streamType, COMBO_CHANGED, STREAM1_CHANGED);
  110. HookWidget(ui->simpleOutputPath, EDIT_CHANGED, OUTPUTS_CHANGED);
  111. HookWidget(ui->simpleOutputVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
  112. HookWidget(ui->simpleOutputABitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
  113. HookWidget(ui->simpleOutReconnect, CHECK_CHANGED, OUTPUTS_CHANGED);
  114. HookWidget(ui->simpleOutRetryDelay, SCROLL_CHANGED, OUTPUTS_CHANGED);
  115. HookWidget(ui->simpleOutMaxRetries, SCROLL_CHANGED, OUTPUTS_CHANGED);
  116. HookWidget(ui->simpleOutAdvanced, CHECK_CHANGED, OUTPUTS_CHANGED);
  117. HookWidget(ui->simpleOutUseCBR, CHECK_CHANGED, OUTPUTS_CHANGED);
  118. HookWidget(ui->simpleOutPreset, COMBO_CHANGED, OUTPUTS_CHANGED);
  119. HookWidget(ui->simpleOutUseBufsize, CHECK_CHANGED, OUTPUTS_CHANGED);
  120. HookWidget(ui->simpleOutPreset, COMBO_CHANGED, OUTPUTS_CHANGED);
  121. HookWidget(ui->simpleOutVBufsize, SCROLL_CHANGED, OUTPUTS_CHANGED);
  122. HookWidget(ui->simpleOutCustom, EDIT_CHANGED, OUTPUTS_CHANGED);
  123. HookWidget(ui->advOutReconnect, CHECK_CHANGED, OUTPUTS_CHANGED);
  124. HookWidget(ui->advOutRetryDelay, SCROLL_CHANGED, OUTPUTS_CHANGED);
  125. HookWidget(ui->advOutMaxRetries, SCROLL_CHANGED, OUTPUTS_CHANGED);
  126. HookWidget(ui->advOutEncoder, COMBO_CHANGED, OUTPUTS_CHANGED);
  127. HookWidget(ui->advOutUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
  128. HookWidget(ui->advOutRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
  129. HookWidget(ui->advOutTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
  130. HookWidget(ui->advOutTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
  131. HookWidget(ui->advOutTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
  132. HookWidget(ui->advOutTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
  133. HookWidget(ui->advOutApplyService, CHECK_CHANGED, OUTPUTS_CHANGED);
  134. HookWidget(ui->advOutRecType, COMBO_CHANGED, OUTPUTS_CHANGED);
  135. HookWidget(ui->advOutRecPath, EDIT_CHANGED, OUTPUTS_CHANGED);
  136. HookWidget(ui->advOutRecEncoder, COMBO_CHANGED, OUTPUTS_CHANGED);
  137. HookWidget(ui->advOutRecUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
  138. HookWidget(ui->advOutRecRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
  139. HookWidget(ui->advOutRecTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
  140. HookWidget(ui->advOutRecTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
  141. HookWidget(ui->advOutRecTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
  142. HookWidget(ui->advOutRecTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
  143. HookWidget(ui->advOutFFURL, EDIT_CHANGED, OUTPUTS_CHANGED);
  144. HookWidget(ui->advOutFFVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
  145. HookWidget(ui->advOutFFUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
  146. HookWidget(ui->advOutFFRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
  147. HookWidget(ui->advOutFFVEncoder, EDIT_CHANGED, OUTPUTS_CHANGED);
  148. HookWidget(ui->advOutFFVCfg, EDIT_CHANGED, OUTPUTS_CHANGED);
  149. HookWidget(ui->advOutFFABitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
  150. HookWidget(ui->advOutFFTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
  151. HookWidget(ui->advOutFFTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
  152. HookWidget(ui->advOutFFTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
  153. HookWidget(ui->advOutFFTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
  154. HookWidget(ui->advOutFFAEncoder, EDIT_CHANGED, OUTPUTS_CHANGED);
  155. HookWidget(ui->advOutFFACfg, EDIT_CHANGED, OUTPUTS_CHANGED);
  156. HookWidget(ui->advOutTrack1Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
  157. HookWidget(ui->advOutTrack1Name, EDIT_CHANGED, OUTPUTS_CHANGED);
  158. HookWidget(ui->advOutTrack2Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
  159. HookWidget(ui->advOutTrack2Name, EDIT_CHANGED, OUTPUTS_CHANGED);
  160. HookWidget(ui->advOutTrack3Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
  161. HookWidget(ui->advOutTrack3Name, EDIT_CHANGED, OUTPUTS_CHANGED);
  162. HookWidget(ui->advOutTrack4Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
  163. HookWidget(ui->advOutTrack4Name, EDIT_CHANGED, OUTPUTS_CHANGED);
  164. HookWidget(ui->channelSetup, COMBO_CHANGED, AUDIO_RESTART);
  165. HookWidget(ui->sampleRate, COMBO_CHANGED, AUDIO_RESTART);
  166. HookWidget(ui->desktopAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
  167. HookWidget(ui->desktopAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED);
  168. HookWidget(ui->auxAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
  169. HookWidget(ui->auxAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED);
  170. HookWidget(ui->auxAudioDevice3, COMBO_CHANGED, AUDIO_CHANGED);
  171. HookWidget(ui->renderer, COMBO_CHANGED, VIDEO_RESTART);
  172. HookWidget(ui->adapter, COMBO_CHANGED, VIDEO_RESTART);
  173. HookWidget(ui->baseResolution, CBEDIT_CHANGED, VIDEO_RES);
  174. HookWidget(ui->outputResolution, CBEDIT_CHANGED, VIDEO_RES);
  175. HookWidget(ui->downscaleFilter, COMBO_CHANGED, VIDEO_CHANGED);
  176. HookWidget(ui->fpsType, COMBO_CHANGED, VIDEO_CHANGED);
  177. HookWidget(ui->fpsCommon, COMBO_CHANGED, VIDEO_CHANGED);
  178. HookWidget(ui->fpsInteger, SCROLL_CHANGED, VIDEO_CHANGED);
  179. HookWidget(ui->fpsInteger, SCROLL_CHANGED, VIDEO_CHANGED);
  180. HookWidget(ui->fpsNumerator, SCROLL_CHANGED, VIDEO_CHANGED);
  181. HookWidget(ui->fpsDenominator, SCROLL_CHANGED, VIDEO_CHANGED);
  182. //Apply button disabled until change.
  183. EnableApplyButton(false);
  184. LoadServiceTypes();
  185. LoadEncoderTypes();
  186. LoadSettings(false);
  187. }
  188. void OBSBasicSettings::SaveCombo(QComboBox *widget, const char *section,
  189. const char *value)
  190. {
  191. if (WidgetChanged(widget))
  192. config_set_string(main->Config(), section, value,
  193. QT_TO_UTF8(widget->currentText()));
  194. }
  195. void OBSBasicSettings::SaveComboData(QComboBox *widget, const char *section,
  196. const char *value)
  197. {
  198. if (WidgetChanged(widget)) {
  199. QString str = GetComboData(widget);
  200. config_set_string(main->Config(), section, value,
  201. QT_TO_UTF8(str));
  202. }
  203. }
  204. void OBSBasicSettings::SaveCheckBox(QAbstractButton *widget,
  205. const char *section, const char *value, bool invert)
  206. {
  207. if (WidgetChanged(widget)) {
  208. bool checked = widget->isChecked();
  209. if (invert) checked = !checked;
  210. config_set_bool(main->Config(), section, value, checked);
  211. }
  212. }
  213. void OBSBasicSettings::SaveEdit(QLineEdit *widget, const char *section,
  214. const char *value)
  215. {
  216. if (WidgetChanged(widget))
  217. config_set_string(main->Config(), section, value,
  218. QT_TO_UTF8(widget->text()));
  219. }
  220. void OBSBasicSettings::SaveSpinBox(QSpinBox *widget, const char *section,
  221. const char *value)
  222. {
  223. if (WidgetChanged(widget))
  224. config_set_int(main->Config(), section, value, widget->value());
  225. }
  226. void OBSBasicSettings::LoadServiceTypes()
  227. {
  228. const char *type;
  229. size_t idx = 0;
  230. while (obs_enum_service_types(idx++, &type)) {
  231. const char *name = obs_service_get_display_name(type);
  232. QString qName = QT_UTF8(name);
  233. QString qType = QT_UTF8(type);
  234. ui->streamType->addItem(qName, qType);
  235. }
  236. type = obs_service_get_type(main->GetService());
  237. SetComboByValue(ui->streamType, type);
  238. }
  239. #define TEXT_USE_STREAM_ENC \
  240. QTStr("Basic.Settings.Output.Adv.Recording.UseStreamEncoder")
  241. void OBSBasicSettings::LoadEncoderTypes()
  242. {
  243. const char *type;
  244. size_t idx = 0;
  245. ui->advOutRecEncoder->addItem(TEXT_USE_STREAM_ENC, "none");
  246. while (obs_enum_encoder_types(idx++, &type)) {
  247. const char *name = obs_encoder_get_display_name(type);
  248. const char *codec = obs_get_encoder_codec(type);
  249. if (strcmp(codec, "h264") != 0)
  250. continue;
  251. QString qName = QT_UTF8(name);
  252. QString qType = QT_UTF8(type);
  253. ui->advOutEncoder->addItem(qName, qType);
  254. ui->advOutRecEncoder->addItem(qName, qType);
  255. }
  256. }
  257. void OBSBasicSettings::LoadLanguageList()
  258. {
  259. const char *currentLang = App()->GetLocale();
  260. ui->language->clear();
  261. for (const auto &locale : GetLocaleNames()) {
  262. int idx = ui->language->count();
  263. ui->language->addItem(QT_UTF8(locale.second.c_str()),
  264. QT_UTF8(locale.first.c_str()));
  265. if (locale.first == currentLang)
  266. ui->language->setCurrentIndex(idx);
  267. }
  268. ui->language->model()->sort(0);
  269. }
  270. void OBSBasicSettings::LoadGeneralSettings()
  271. {
  272. loading = true;
  273. LoadLanguageList();
  274. loading = false;
  275. }
  276. void OBSBasicSettings::LoadStream1Settings()
  277. {
  278. QLayout *layout = ui->streamContainer->layout();
  279. obs_service_t *service = main->GetService();
  280. const char *type = obs_service_get_type(service);
  281. loading = true;
  282. obs_data_t *settings = obs_service_get_settings(service);
  283. delete streamProperties;
  284. streamProperties = new OBSPropertiesView(settings, type,
  285. (PropertiesReloadCallback)obs_get_service_properties,
  286. 170);
  287. streamProperties->setProperty("changed", QVariant(false));
  288. layout->addWidget(streamProperties);
  289. QObject::connect(streamProperties, SIGNAL(Changed()),
  290. this, STREAM1_CHANGED);
  291. obs_data_release(settings);
  292. loading = false;
  293. if (main->StreamingActive()) {
  294. ui->streamType->setEnabled(false);
  295. ui->streamContainer->setEnabled(false);
  296. }
  297. }
  298. void OBSBasicSettings::LoadRendererList()
  299. {
  300. const char *renderer = config_get_string(GetGlobalConfig(), "Video",
  301. "Renderer");
  302. #ifdef _WIN32
  303. ui->renderer->addItem(QT_UTF8("Direct3D 11"));
  304. #endif
  305. ui->renderer->addItem(QT_UTF8("OpenGL"));
  306. int idx = ui->renderer->findText(QT_UTF8(renderer));
  307. if (idx == -1)
  308. idx = 0;
  309. ui->renderer->setCurrentIndex(idx);
  310. }
  311. Q_DECLARE_METATYPE(MonitorInfo);
  312. static string ResString(uint32_t cx, uint32_t cy)
  313. {
  314. stringstream res;
  315. res << cx << "x" << cy;
  316. return res.str();
  317. }
  318. /* some nice default output resolution vals */
  319. static const double vals[] =
  320. {
  321. 1.0,
  322. 1.25,
  323. (1.0/0.75),
  324. 1.5,
  325. (1.0/0.6),
  326. 1.75,
  327. 2.0,
  328. 2.25,
  329. 2.5,
  330. 2.75,
  331. 3.0
  332. };
  333. static const size_t numVals = sizeof(vals)/sizeof(double);
  334. void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy,
  335. uint32_t out_cx, uint32_t out_cy)
  336. {
  337. QString advRescale;
  338. QString advRecRescale;
  339. QString advFFRescale;
  340. advRescale = ui->advOutRescale->lineEdit()->text();
  341. advRecRescale = ui->advOutRecRescale->lineEdit()->text();
  342. advFFRescale = ui->advOutFFRescale->lineEdit()->text();
  343. ui->outputResolution->clear();
  344. ui->advOutRescale->clear();
  345. ui->advOutRecRescale->clear();
  346. ui->advOutFFRescale->clear();
  347. for (size_t idx = 0; idx < numVals; idx++) {
  348. uint32_t downscaleCX = uint32_t(double(cx) / vals[idx]);
  349. uint32_t downscaleCY = uint32_t(double(cy) / vals[idx]);
  350. uint32_t outDownscaleCX = uint32_t(double(out_cx) / vals[idx]);
  351. uint32_t outDownscaleCY = uint32_t(double(out_cy) / vals[idx]);
  352. downscaleCX &= 0xFFFFFFFC;
  353. downscaleCY &= 0xFFFFFFFE;
  354. outDownscaleCX &= 0xFFFFFFFE;
  355. outDownscaleCY &= 0xFFFFFFFE;
  356. string res = ResString(downscaleCX, downscaleCY);
  357. string outRes = ResString(outDownscaleCX, outDownscaleCY);
  358. ui->outputResolution->addItem(res.c_str());
  359. ui->advOutRescale->addItem(outRes.c_str());
  360. ui->advOutRecRescale->addItem(outRes.c_str());
  361. ui->advOutFFRescale->addItem(outRes.c_str());
  362. }
  363. string res = ResString(cx, cy);
  364. ui->outputResolution->lineEdit()->setText(res.c_str());
  365. if (advRescale.isEmpty())
  366. advRescale = res.c_str();
  367. if (advRecRescale.isEmpty())
  368. advRecRescale = res.c_str();
  369. if (advFFRescale.isEmpty())
  370. advFFRescale = res.c_str();
  371. ui->advOutRescale->lineEdit()->setText(advRescale);
  372. ui->advOutRecRescale->lineEdit()->setText(advRecRescale);
  373. ui->advOutFFRescale->lineEdit()->setText(advFFRescale);
  374. }
  375. void OBSBasicSettings::LoadDownscaleFilters()
  376. {
  377. ui->downscaleFilter->addItem(
  378. QTStr("Basic.Settings.Video.DownscaleFilter.Bilinear"),
  379. QT_UTF8("bilinear"));
  380. ui->downscaleFilter->addItem(
  381. QTStr("Basic.Settings.Video.DownscaleFilter.Bicubic"),
  382. QT_UTF8("bicubic"));
  383. ui->downscaleFilter->addItem(
  384. QTStr("Basic.Settings.Video.DownscaleFilter.Lanczos"),
  385. QT_UTF8("lanczos"));
  386. const char *scaleType = config_get_string(main->Config(),
  387. "Video", "ScaleType");
  388. if (astrcmpi(scaleType, "bilinear") == 0)
  389. ui->downscaleFilter->setCurrentIndex(0);
  390. else if (astrcmpi(scaleType, "lanczos") == 0)
  391. ui->downscaleFilter->setCurrentIndex(2);
  392. else
  393. ui->downscaleFilter->setCurrentIndex(1);
  394. }
  395. void OBSBasicSettings::LoadResolutionLists()
  396. {
  397. uint32_t cx = config_get_uint(main->Config(), "Video", "BaseCX");
  398. uint32_t cy = config_get_uint(main->Config(), "Video", "BaseCY");
  399. uint32_t out_cx = config_get_uint(main->Config(), "Video", "OutputCX");
  400. uint32_t out_cy = config_get_uint(main->Config(), "Video", "OutputCY");
  401. vector<MonitorInfo> monitors;
  402. ui->baseResolution->clear();
  403. GetMonitors(monitors);
  404. for (MonitorInfo &monitor : monitors) {
  405. string res = ResString(monitor.cx, monitor.cy);
  406. ui->baseResolution->addItem(res.c_str());
  407. }
  408. ResetDownscales(cx, cy, out_cx, out_cy);
  409. ui->baseResolution->lineEdit()->setText(ResString(cx, cy).c_str());
  410. ui->outputResolution->lineEdit()->setText(
  411. ResString(out_cx, out_cy).c_str());
  412. }
  413. static inline void LoadFPSCommon(OBSBasic *main, Ui::OBSBasicSettings *ui)
  414. {
  415. const char *val = config_get_string(main->Config(), "Video",
  416. "FPSCommon");
  417. int idx = ui->fpsCommon->findText(val);
  418. if (idx == -1) idx = 3;
  419. ui->fpsCommon->setCurrentIndex(idx);
  420. }
  421. static inline void LoadFPSInteger(OBSBasic *main, Ui::OBSBasicSettings *ui)
  422. {
  423. uint32_t val = config_get_uint(main->Config(), "Video", "FPSInt");
  424. ui->fpsInteger->setValue(val);
  425. }
  426. static inline void LoadFPSFraction(OBSBasic *main, Ui::OBSBasicSettings *ui)
  427. {
  428. uint32_t num = config_get_uint(main->Config(), "Video", "FPSNum");
  429. uint32_t den = config_get_uint(main->Config(), "Video", "FPSDen");
  430. ui->fpsNumerator->setValue(num);
  431. ui->fpsDenominator->setValue(den);
  432. }
  433. void OBSBasicSettings::LoadFPSData()
  434. {
  435. LoadFPSCommon(main, ui.get());
  436. LoadFPSInteger(main, ui.get());
  437. LoadFPSFraction(main, ui.get());
  438. uint32_t fpsType = config_get_uint(main->Config(), "Video",
  439. "FPSType");
  440. if (fpsType > 2) fpsType = 0;
  441. ui->fpsType->setCurrentIndex(fpsType);
  442. ui->fpsTypes->setCurrentIndex(fpsType);
  443. }
  444. void OBSBasicSettings::LoadVideoSettings()
  445. {
  446. loading = true;
  447. if (video_output_active(obs_get_video())) {
  448. ui->videoPage->setEnabled(false);
  449. ui->videoMsg->setText(
  450. QTStr("Basic.Settings.Video.CurrentlyActive"));
  451. }
  452. LoadRendererList();
  453. LoadResolutionLists();
  454. LoadFPSData();
  455. LoadDownscaleFilters();
  456. loading = false;
  457. }
  458. void OBSBasicSettings::LoadSimpleOutputSettings()
  459. {
  460. const char *path = config_get_string(main->Config(), "SimpleOutput",
  461. "FilePath");
  462. int videoBitrate = config_get_uint(main->Config(), "SimpleOutput",
  463. "VBitrate");
  464. int videoBufsize = config_get_uint(main->Config(), "SimpleOutput",
  465. "Bufsize");
  466. int audioBitrate = config_get_uint(main->Config(), "SimpleOutput",
  467. "ABitrate");
  468. bool reconnect = config_get_bool(main->Config(), "SimpleOutput",
  469. "Reconnect");
  470. int retryDelay = config_get_uint(main->Config(), "SimpleOutput",
  471. "RetryDelay");
  472. int maxRetries = config_get_uint(main->Config(), "SimpleOutput",
  473. "MaxRetries");
  474. bool advanced = config_get_bool(main->Config(), "SimpleOutput",
  475. "UseAdvanced");
  476. bool useCBR = config_get_bool(main->Config(), "SimpleOutput",
  477. "UseCBR");
  478. bool useBufsize = config_get_bool(main->Config(), "SimpleOutput",
  479. "UseBufsize");
  480. const char *preset = config_get_string(main->Config(), "SimpleOutput",
  481. "Preset");
  482. const char *custom = config_get_string(main->Config(), "SimpleOutput",
  483. "x264Settings");
  484. ui->simpleOutputPath->setText(path);
  485. ui->simpleOutputVBitrate->setValue(videoBitrate);
  486. ui->simpleOutUseBufsize->setChecked(useBufsize);
  487. ui->simpleOutVBufsize->setValue(
  488. useBufsize ? videoBufsize : videoBitrate);
  489. SetComboByName(ui->simpleOutputABitrate,
  490. std::to_string(audioBitrate).c_str());
  491. ui->simpleOutReconnect->setChecked(reconnect);
  492. ui->simpleOutRetryDelay->setValue(retryDelay);
  493. ui->simpleOutMaxRetries->setValue(maxRetries);
  494. ui->simpleOutAdvanced->setChecked(advanced);
  495. ui->simpleOutUseCBR->setChecked(useCBR);
  496. ui->simpleOutPreset->setCurrentText(preset);
  497. ui->simpleOutCustom->setText(custom);
  498. }
  499. void OBSBasicSettings::LoadAdvOutputStreamingSettings()
  500. {
  501. bool reconnect = config_get_bool(main->Config(), "AdvOut",
  502. "Reconnect");
  503. int retryDelay = config_get_int(main->Config(), "AdvOut",
  504. "RetryDelay");
  505. int maxRetries = config_get_int(main->Config(), "AdvOut",
  506. "MaxRetries");
  507. bool rescale = config_get_bool(main->Config(), "AdvOut",
  508. "Rescale");
  509. const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
  510. "RescaleRes");
  511. int trackIndex = config_get_int(main->Config(), "AdvOut",
  512. "TrackIndex");
  513. bool applyServiceSettings = config_get_bool(main->Config(), "AdvOut",
  514. "ApplyServiceSettings");
  515. ui->advOutReconnect->setChecked(reconnect);
  516. ui->advOutRetryDelay->setValue(retryDelay);
  517. ui->advOutMaxRetries->setValue(maxRetries);
  518. ui->advOutApplyService->setChecked(applyServiceSettings);
  519. ui->advOutUseRescale->setChecked(rescale);
  520. ui->advOutRescale->setCurrentText(rescaleRes);
  521. switch (trackIndex) {
  522. case 1: ui->advOutTrack1->setChecked(true); break;
  523. case 2: ui->advOutTrack2->setChecked(true); break;
  524. case 3: ui->advOutTrack3->setChecked(true); break;
  525. case 4: ui->advOutTrack4->setChecked(true); break;
  526. }
  527. }
  528. OBSPropertiesView *OBSBasicSettings::CreateEncoderPropertyView(
  529. const char *encoder, const char *path, bool changed)
  530. {
  531. obs_data_t *settings = obs_encoder_defaults(encoder);
  532. OBSPropertiesView *view;
  533. char encoderJsonPath[512];
  534. int ret = os_get_config_path(encoderJsonPath, sizeof(encoderJsonPath),
  535. path);
  536. if (ret > 0) {
  537. BPtr<char> jsonData = os_quick_read_utf8_file(encoderJsonPath);
  538. if (!!jsonData) {
  539. obs_data_t *data = obs_data_create_from_json(jsonData);
  540. obs_data_apply(settings, data);
  541. obs_data_release(data);
  542. }
  543. }
  544. view = new OBSPropertiesView(settings, encoder,
  545. (PropertiesReloadCallback)obs_get_encoder_properties,
  546. 170);
  547. view->setFrameShape(QFrame::StyledPanel);
  548. view->setProperty("changed", QVariant(changed));
  549. QObject::connect(view, SIGNAL(Changed()), this, SLOT(OutputsChanged()));
  550. obs_data_release(settings);
  551. return view;
  552. }
  553. void OBSBasicSettings::LoadAdvOutputStreamingEncoderProperties()
  554. {
  555. const char *encoder = config_get_string(main->Config(), "AdvOut",
  556. "Encoder");
  557. delete streamEncoderProps;
  558. streamEncoderProps = CreateEncoderPropertyView(encoder,
  559. "obs-studio/basic/streamEncoder.json");
  560. ui->advOutputStreamTab->layout()->addWidget(streamEncoderProps);
  561. SetComboByValue(ui->advOutEncoder, encoder);
  562. }
  563. void OBSBasicSettings::LoadAdvOutputRecordingSettings()
  564. {
  565. const char *type = config_get_string(main->Config(), "AdvOut",
  566. "RecType");
  567. const char *path = config_get_string(main->Config(), "AdvOut",
  568. "RecFilePath");
  569. bool rescale = config_get_bool(main->Config(), "AdvOut",
  570. "RecRescale");
  571. const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
  572. "RecRescaleRes");
  573. int trackIndex = config_get_int(main->Config(), "AdvOut",
  574. "RecTrackIndex");
  575. int typeIndex = (astrcmpi(type, "FFmpeg") == 0) ? 1 : 0;
  576. ui->advOutRecType->setCurrentIndex(typeIndex);
  577. ui->advOutRecPath->setText(path);
  578. ui->advOutRecUseRescale->setChecked(rescale);
  579. ui->advOutRecRescale->setCurrentText(rescaleRes);
  580. switch (trackIndex) {
  581. case 1: ui->advOutRecTrack1->setChecked(true); break;
  582. case 2: ui->advOutRecTrack2->setChecked(true); break;
  583. case 3: ui->advOutRecTrack3->setChecked(true); break;
  584. case 4: ui->advOutRecTrack4->setChecked(true); break;
  585. }
  586. }
  587. void OBSBasicSettings::LoadAdvOutputRecordingEncoderProperties()
  588. {
  589. const char *encoder = config_get_string(main->Config(), "AdvOut",
  590. "RecEncoder");
  591. delete recordEncoderProps;
  592. recordEncoderProps = nullptr;
  593. if (astrcmpi(encoder, "none") != 0) {
  594. recordEncoderProps = CreateEncoderPropertyView(encoder,
  595. "obs-studio/basic/recordEncoder.json");
  596. ui->advOutRecStandard->layout()->addWidget(recordEncoderProps);
  597. }
  598. SetComboByValue(ui->advOutRecEncoder, encoder);
  599. }
  600. void OBSBasicSettings::LoadAdvOutputFFmpegSettings()
  601. {
  602. const char *url = config_get_string(main->Config(), "AdvOut", "FFURL");
  603. int videoBitrate = config_get_int(main->Config(), "AdvOut",
  604. "FFVBitrate");
  605. bool rescale = config_get_bool(main->Config(), "AdvOut",
  606. "FFRescale");
  607. const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
  608. "FFRescaleRes");
  609. const char *vEncoder = config_get_string(main->Config(), "AdvOut",
  610. "FFVEncoder");
  611. const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
  612. "FFVCustom");
  613. int audioBitrate = config_get_int(main->Config(), "AdvOut",
  614. "FFABitrate");
  615. int audioTrack = config_get_int(main->Config(), "AdvOut",
  616. "FFAudioTrack");
  617. const char *aEncoder = config_get_string(main->Config(), "AdvOut",
  618. "FFAEncoder");
  619. const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
  620. "FFACustom");
  621. ui->advOutFFURL->setText(url);
  622. ui->advOutFFVBitrate->setValue(videoBitrate);
  623. ui->advOutFFUseRescale->setChecked(rescale);
  624. ui->advOutFFRescale->setCurrentText(rescaleRes);
  625. ui->advOutFFVEncoder->setText(vEncoder);
  626. ui->advOutFFVCfg->setText(vEncCustom);
  627. ui->advOutFFABitrate->setValue(audioBitrate);
  628. ui->advOutFFAEncoder->setText(aEncoder);
  629. ui->advOutFFACfg->setText(aEncCustom);
  630. switch (audioTrack) {
  631. case 1: ui->advOutFFTrack1->setChecked(true); break;
  632. case 2: ui->advOutFFTrack2->setChecked(true); break;
  633. case 3: ui->advOutFFTrack3->setChecked(true); break;
  634. case 4: ui->advOutFFTrack4->setChecked(true); break;
  635. }
  636. }
  637. void OBSBasicSettings::LoadAdvOutputAudioSettings()
  638. {
  639. int track1Bitrate = config_get_uint(main->Config(), "AdvOut",
  640. "Track1Bitrate");
  641. int track2Bitrate = config_get_uint(main->Config(), "AdvOut",
  642. "Track2Bitrate");
  643. int track3Bitrate = config_get_uint(main->Config(), "AdvOut",
  644. "Track3Bitrate");
  645. int track4Bitrate = config_get_uint(main->Config(), "AdvOut",
  646. "Track4Bitrate");
  647. const char *name1 = config_get_string(main->Config(), "AdvOut",
  648. "Track1Name");
  649. const char *name2 = config_get_string(main->Config(), "AdvOut",
  650. "Track2Name");
  651. const char *name3 = config_get_string(main->Config(), "AdvOut",
  652. "Track3Name");
  653. const char *name4 = config_get_string(main->Config(), "AdvOut",
  654. "Track4Name");
  655. SetComboByName(ui->advOutTrack1Bitrate,
  656. std::to_string(track1Bitrate).c_str());
  657. SetComboByName(ui->advOutTrack2Bitrate,
  658. std::to_string(track2Bitrate).c_str());
  659. SetComboByName(ui->advOutTrack3Bitrate,
  660. std::to_string(track3Bitrate).c_str());
  661. SetComboByName(ui->advOutTrack4Bitrate,
  662. std::to_string(track4Bitrate).c_str());
  663. ui->advOutTrack1Name->setText(name1);
  664. ui->advOutTrack2Name->setText(name2);
  665. ui->advOutTrack3Name->setText(name3);
  666. ui->advOutTrack4Name->setText(name4);
  667. }
  668. void OBSBasicSettings::LoadOutputSettings()
  669. {
  670. loading = true;
  671. const char *mode = config_get_string(main->Config(), "Output", "Mode");
  672. int modeIdx = astrcmpi(mode, "Advanced") == 0 ? 1 : 0;
  673. ui->outputMode->setCurrentIndex(modeIdx);
  674. LoadSimpleOutputSettings();
  675. LoadAdvOutputStreamingSettings();
  676. LoadAdvOutputStreamingEncoderProperties();
  677. LoadAdvOutputRecordingSettings();
  678. LoadAdvOutputRecordingEncoderProperties();
  679. LoadAdvOutputFFmpegSettings();
  680. LoadAdvOutputAudioSettings();
  681. if (video_output_active(obs_get_video())) {
  682. ui->outputMode->setEnabled(false);
  683. ui->advOutTopContainer->setEnabled(false);
  684. ui->advOutRecTopContainer->setEnabled(false);
  685. ui->advOutRecTypeContainer->setEnabled(false);
  686. ui->advOutputAudioTracksTab->setEnabled(false);
  687. }
  688. loading = false;
  689. }
  690. static inline void LoadListValue(QComboBox *widget, const char *text,
  691. const char *val)
  692. {
  693. widget->addItem(QT_UTF8(text), QT_UTF8(val));
  694. }
  695. void OBSBasicSettings::LoadListValues(QComboBox *widget, obs_property_t *prop,
  696. const char *configName)
  697. {
  698. size_t count = obs_property_list_item_count(prop);
  699. const char *deviceId = config_get_string(main->Config(), "Audio",
  700. configName);
  701. widget->addItem(QTStr("Disabled"), "disabled");
  702. for (size_t i = 0; i < count; i++) {
  703. const char *name = obs_property_list_item_name(prop, i);
  704. const char *val = obs_property_list_item_string(prop, i);
  705. LoadListValue(widget, name, val);
  706. }
  707. int idx = widget->findData(QVariant(QT_UTF8(deviceId)));
  708. if (idx == -1) {
  709. deviceId = config_get_default_string(main->Config(), "Audio",
  710. configName);
  711. idx = widget->findData(QVariant(QT_UTF8(deviceId)));
  712. }
  713. if (idx != -1)
  714. widget->setCurrentIndex(idx);
  715. }
  716. void OBSBasicSettings::LoadAudioDevices()
  717. {
  718. const char *input_id = App()->InputAudioSource();
  719. const char *output_id = App()->OutputAudioSource();
  720. obs_properties_t *input_props = obs_get_source_properties(
  721. OBS_SOURCE_TYPE_INPUT, input_id);
  722. obs_properties_t *output_props = obs_get_source_properties(
  723. OBS_SOURCE_TYPE_INPUT, output_id);
  724. if (input_props) {
  725. obs_property_t *inputs = obs_properties_get(input_props,
  726. "device_id");
  727. LoadListValues(ui->auxAudioDevice1, inputs, "AuxDevice1");
  728. LoadListValues(ui->auxAudioDevice2, inputs, "AuxDevice2");
  729. LoadListValues(ui->auxAudioDevice3, inputs, "AuxDevice3");
  730. obs_properties_destroy(input_props);
  731. }
  732. if (output_props) {
  733. obs_property_t *outputs = obs_properties_get(output_props,
  734. "device_id");
  735. LoadListValues(ui->desktopAudioDevice1, outputs,
  736. "DesktopDevice1");
  737. LoadListValues(ui->desktopAudioDevice2, outputs,
  738. "DesktopDevice2");
  739. obs_properties_destroy(output_props);
  740. }
  741. }
  742. void OBSBasicSettings::LoadAudioSettings()
  743. {
  744. uint32_t sampleRate = config_get_uint(main->Config(), "Audio",
  745. "SampleRate");
  746. const char *speakers = config_get_string(main->Config(), "Audio",
  747. "ChannelSetup");
  748. loading = true;
  749. const char *str;
  750. if (sampleRate == 22050)
  751. str = "22.05khz";
  752. else if (sampleRate == 48000)
  753. str = "48khz";
  754. else
  755. str = "44.1khz";
  756. int sampleRateIdx = ui->sampleRate->findText(str);
  757. if (sampleRateIdx != -1)
  758. ui->sampleRate->setCurrentIndex(sampleRateIdx);
  759. if (strcmp(speakers, "Mono") == 0)
  760. ui->channelSetup->setCurrentIndex(0);
  761. else
  762. ui->channelSetup->setCurrentIndex(1);
  763. LoadAudioDevices();
  764. loading = false;
  765. }
  766. void OBSBasicSettings::LoadSettings(bool changedOnly)
  767. {
  768. if (!changedOnly || generalChanged)
  769. LoadGeneralSettings();
  770. if (!changedOnly || stream1Changed)
  771. LoadStream1Settings();
  772. if (!changedOnly || outputsChanged)
  773. LoadOutputSettings();
  774. if (!changedOnly || audioChanged)
  775. LoadAudioSettings();
  776. if (!changedOnly || videoChanged)
  777. LoadVideoSettings();
  778. }
  779. void OBSBasicSettings::SaveGeneralSettings()
  780. {
  781. int languageIndex = ui->language->currentIndex();
  782. QVariant langData = ui->language->itemData(languageIndex);
  783. string language = langData.toString().toStdString();
  784. if (WidgetChanged(ui->language))
  785. config_set_string(GetGlobalConfig(), "General", "Language",
  786. language.c_str());
  787. }
  788. void OBSBasicSettings::SaveStream1Settings()
  789. {
  790. QString streamType = GetComboData(ui->streamType);
  791. obs_service_t *newService = obs_service_create(QT_TO_UTF8(streamType),
  792. "default_service", streamProperties->GetSettings());
  793. if (!newService)
  794. return;
  795. main->SetService(newService);
  796. main->SaveService();
  797. }
  798. void OBSBasicSettings::SaveVideoSettings()
  799. {
  800. QString baseResolution = ui->baseResolution->currentText();
  801. QString outputResolution = ui->outputResolution->currentText();
  802. int fpsType = ui->fpsType->currentIndex();
  803. uint32_t cx = 0, cy = 0;
  804. /* ------------------- */
  805. if (WidgetChanged(ui->renderer))
  806. config_set_string(App()->GlobalConfig(), "Video", "Renderer",
  807. QT_TO_UTF8(ui->renderer->currentText()));
  808. if (WidgetChanged(ui->baseResolution) &&
  809. ConvertResText(QT_TO_UTF8(baseResolution), cx, cy)) {
  810. config_set_uint(main->Config(), "Video", "BaseCX", cx);
  811. config_set_uint(main->Config(), "Video", "BaseCY", cy);
  812. }
  813. if (WidgetChanged(ui->outputResolution) &&
  814. ConvertResText(QT_TO_UTF8(outputResolution), cx, cy)) {
  815. config_set_uint(main->Config(), "Video", "OutputCX", cx);
  816. config_set_uint(main->Config(), "Video", "OutputCY", cy);
  817. }
  818. if (WidgetChanged(ui->fpsType))
  819. config_set_uint(main->Config(), "Video", "FPSType", fpsType);
  820. SaveCombo(ui->fpsCommon, "Video", "FPSCommon");
  821. SaveSpinBox(ui->fpsInteger, "Video", "FPSInt");
  822. SaveSpinBox(ui->fpsNumerator, "Video", "FPSNum");
  823. SaveSpinBox(ui->fpsDenominator, "Video", "FPSDen");
  824. SaveComboData(ui->downscaleFilter, "Video", "ScaleType");
  825. main->ResetVideo();
  826. }
  827. static inline const char *OutputModeFromIdx(int idx)
  828. {
  829. if (idx == 1)
  830. return "Advanced";
  831. else
  832. return "Simple";
  833. }
  834. static inline const char *RecTypeFromIdx(int idx)
  835. {
  836. if (idx == 1)
  837. return "FFmpeg";
  838. else
  839. return "Standard";
  840. }
  841. static void WriteJsonData(OBSPropertiesView *view, const char *path)
  842. {
  843. char full_path[512];
  844. if (!view || !WidgetChanged(view))
  845. return;
  846. int ret = os_get_config_path(full_path, sizeof(full_path), path);
  847. if (ret > 0) {
  848. obs_data_t *settings = view->GetSettings();
  849. if (settings) {
  850. const char *json = obs_data_get_json(settings);
  851. if (json && *json) {
  852. os_quick_write_utf8_file(full_path, json,
  853. strlen(json), false);
  854. }
  855. }
  856. }
  857. }
  858. static void SaveTrackIndex(config_t *config, const char *section,
  859. const char *name,
  860. QAbstractButton *check1,
  861. QAbstractButton *check2,
  862. QAbstractButton *check3,
  863. QAbstractButton *check4)
  864. {
  865. if (check1->isChecked()) config_set_int(config, section, name, 1);
  866. else if (check2->isChecked()) config_set_int(config, section, name, 2);
  867. else if (check3->isChecked()) config_set_int(config, section, name, 3);
  868. else if (check4->isChecked()) config_set_int(config, section, name, 4);
  869. }
  870. void OBSBasicSettings::SaveOutputSettings()
  871. {
  872. config_set_string(main->Config(), "Output", "Mode",
  873. OutputModeFromIdx(ui->outputMode->currentIndex()));
  874. SaveSpinBox(ui->simpleOutputVBitrate, "SimpleOutput", "VBitrate");
  875. SaveCombo(ui->simpleOutputABitrate, "SimpleOutput", "ABitrate");
  876. SaveEdit(ui->simpleOutputPath, "SimpleOutput", "FilePath");
  877. SaveCheckBox(ui->simpleOutReconnect, "SimpleOutput", "Reconnect");
  878. SaveSpinBox(ui->simpleOutRetryDelay, "SimpleOutput", "RetryDelay");
  879. SaveSpinBox(ui->simpleOutMaxRetries, "SimpleOutput", "MaxRetries");
  880. SaveCheckBox(ui->simpleOutAdvanced, "SimpleOutput", "UseAdvanced");
  881. SaveCheckBox(ui->simpleOutUseCBR, "SimpleOutput", "UseCBR");
  882. SaveCheckBox(ui->simpleOutUseBufsize, "SimpleOutput", "UseBufsize");
  883. SaveCombo(ui->simpleOutPreset, "SimpleOutput", "Preset");
  884. SaveEdit(ui->simpleOutCustom, "SimpleOutput", "x264Settings");
  885. if (ui->simpleOutUseBufsize->isChecked())
  886. SaveSpinBox(ui->simpleOutVBufsize, "SimpleOutput", "VBufsize");
  887. SaveCheckBox(ui->advOutReconnect, "AdvOut", "Reconnect");
  888. SaveSpinBox(ui->advOutRetryDelay, "AdvOut", "RetryDelay");
  889. SaveSpinBox(ui->advOutMaxRetries, "AdvOut", "MaxRetries");
  890. SaveCheckBox(ui->advOutApplyService, "AdvOut", "ApplyServiceSettings");
  891. SaveComboData(ui->advOutEncoder, "AdvOut", "Encoder");
  892. SaveCheckBox(ui->advOutUseRescale, "AdvOut", "Rescale");
  893. SaveCombo(ui->advOutRescale, "AdvOut", "RescaleRes");
  894. SaveTrackIndex(main->Config(), "AdvOut", "TrackIndex",
  895. ui->advOutTrack1, ui->advOutTrack2,
  896. ui->advOutTrack3, ui->advOutTrack4);
  897. config_set_string(main->Config(), "AdvOut", "RecType",
  898. RecTypeFromIdx(ui->advOutRecType->currentIndex()));
  899. SaveEdit(ui->advOutRecPath, "AdvOut", "RecFilePath");
  900. SaveComboData(ui->advOutRecEncoder, "AdvOut", "RecEncoder");
  901. SaveCheckBox(ui->advOutRecUseRescale, "AdvOut", "RecRescale");
  902. SaveCombo(ui->advOutRecRescale, "AdvOut", "RecRescaleRes");
  903. SaveTrackIndex(main->Config(), "AdvOut", "RecTrackIndex",
  904. ui->advOutRecTrack1, ui->advOutRecTrack2,
  905. ui->advOutRecTrack3, ui->advOutRecTrack4);
  906. SaveEdit(ui->advOutFFURL, "AdvOut", "FFURL");
  907. SaveSpinBox(ui->advOutFFVBitrate, "AdvOut", "FFVBitrate");
  908. SaveCheckBox(ui->advOutFFUseRescale, "AdvOut", "FFRescale");
  909. SaveCombo(ui->advOutFFRescale, "AdvOut", "FFRescaleRes");
  910. SaveEdit(ui->advOutFFVEncoder, "AdvOut", "FFVEncoder");
  911. SaveEdit(ui->advOutFFVCfg, "AdvOut", "FFVCustom");
  912. SaveSpinBox(ui->advOutFFABitrate, "AdvOut", "FFABitrate");
  913. SaveEdit(ui->advOutFFAEncoder, "AdvOut", "FFAEncoder");
  914. SaveEdit(ui->advOutFFACfg, "AdvOut", "FFACustom");
  915. SaveTrackIndex(main->Config(), "AdvOut", "FFAudioTrack",
  916. ui->advOutFFTrack1, ui->advOutFFTrack2,
  917. ui->advOutFFTrack3, ui->advOutFFTrack4);
  918. SaveCombo(ui->advOutTrack1Bitrate, "AdvOut", "Track1Bitrate");
  919. SaveCombo(ui->advOutTrack2Bitrate, "AdvOut", "Track2Bitrate");
  920. SaveCombo(ui->advOutTrack3Bitrate, "AdvOut", "Track3Bitrate");
  921. SaveCombo(ui->advOutTrack4Bitrate, "AdvOut", "Track4Bitrate");
  922. SaveEdit(ui->advOutTrack1Name, "AdvOut", "Track1Name");
  923. SaveEdit(ui->advOutTrack2Name, "AdvOut", "Track2Name");
  924. SaveEdit(ui->advOutTrack3Name, "AdvOut", "Track3Name");
  925. SaveEdit(ui->advOutTrack4Name, "AdvOut", "Track4Name");
  926. WriteJsonData(streamEncoderProps,
  927. "obs-studio/basic/streamEncoder.json");
  928. WriteJsonData(recordEncoderProps,
  929. "obs-studio/basic/recordEncoder.json");
  930. main->ResetOutputs();
  931. }
  932. void OBSBasicSettings::SaveAudioSettings()
  933. {
  934. QString sampleRateStr = ui->sampleRate->currentText();
  935. int channelSetupIdx = ui->channelSetup->currentIndex();
  936. const char *channelSetup = (channelSetupIdx == 0) ? "Mono" : "Stereo";
  937. int sampleRate = 44100;
  938. if (sampleRateStr == "22.05khz")
  939. sampleRate = 22050;
  940. else if (sampleRateStr == "48khz")
  941. sampleRate = 48000;
  942. if (WidgetChanged(ui->sampleRate))
  943. config_set_uint(main->Config(), "Audio", "SampleRate",
  944. sampleRate);
  945. if (WidgetChanged(ui->channelSetup))
  946. config_set_string(main->Config(), "Audio", "ChannelSetup",
  947. channelSetup);
  948. SaveComboData(ui->desktopAudioDevice1, "Audio", "DesktopDevice1");
  949. SaveComboData(ui->desktopAudioDevice2, "Audio", "DesktopDevice2");
  950. SaveComboData(ui->auxAudioDevice1, "Audio", "AuxDevice1");
  951. SaveComboData(ui->auxAudioDevice2, "Audio", "AuxDevice2");
  952. SaveComboData(ui->auxAudioDevice3, "Audio", "AuxDevice3");
  953. main->ResetAudioDevices();
  954. }
  955. void OBSBasicSettings::SaveSettings()
  956. {
  957. if (generalChanged)
  958. SaveGeneralSettings();
  959. if (stream1Changed)
  960. SaveStream1Settings();
  961. if (outputsChanged)
  962. SaveOutputSettings();
  963. if (audioChanged)
  964. SaveAudioSettings();
  965. if (videoChanged)
  966. SaveVideoSettings();
  967. config_save(main->Config());
  968. config_save(GetGlobalConfig());
  969. }
  970. bool OBSBasicSettings::QueryChanges()
  971. {
  972. QMessageBox::StandardButton button;
  973. button = QMessageBox::question(this,
  974. QTStr("Basic.Settings.ConfirmTitle"),
  975. QTStr("Basic.Settings.Confirm"),
  976. QMessageBox::Yes | QMessageBox::No |
  977. QMessageBox::Cancel);
  978. if (button == QMessageBox::Cancel)
  979. return false;
  980. else if (button == QMessageBox::Yes)
  981. SaveSettings();
  982. else
  983. LoadSettings(true);
  984. ClearChanged();
  985. return true;
  986. }
  987. void OBSBasicSettings::closeEvent(QCloseEvent *event)
  988. {
  989. if (Changed() && !QueryChanges())
  990. event->ignore();
  991. }
  992. void OBSBasicSettings::on_simpleOutUseBufsize_toggled(bool checked)
  993. {
  994. if (!checked)
  995. ui->simpleOutVBufsize->setValue(
  996. ui->simpleOutputVBitrate->value());
  997. }
  998. void OBSBasicSettings::on_simpleOutputVBitrate_valueChanged(int val)
  999. {
  1000. if (!ui->simpleOutUseBufsize->isChecked())
  1001. ui->simpleOutVBufsize->setValue(val);
  1002. }
  1003. void OBSBasicSettings::on_listWidget_itemSelectionChanged()
  1004. {
  1005. int row = ui->listWidget->currentRow();
  1006. if (loading || row == pageIndex)
  1007. return;
  1008. pageIndex = row;
  1009. }
  1010. void OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button)
  1011. {
  1012. QDialogButtonBox::ButtonRole val = ui->buttonBox->buttonRole(button);
  1013. if (val == QDialogButtonBox::ApplyRole ||
  1014. val == QDialogButtonBox::AcceptRole) {
  1015. SaveSettings();
  1016. ClearChanged();
  1017. }
  1018. if (val == QDialogButtonBox::AcceptRole ||
  1019. val == QDialogButtonBox::RejectRole) {
  1020. ClearChanged();
  1021. close();
  1022. }
  1023. }
  1024. void OBSBasicSettings::on_streamType_currentIndexChanged(int idx)
  1025. {
  1026. if (loading)
  1027. return;
  1028. QLayout *layout = ui->streamContainer->layout();
  1029. QString streamType = ui->streamType->itemData(idx).toString();
  1030. obs_data_t *settings = obs_service_defaults(QT_TO_UTF8(streamType));
  1031. delete streamProperties;
  1032. streamProperties = new OBSPropertiesView(settings,
  1033. QT_TO_UTF8(streamType),
  1034. (PropertiesReloadCallback)obs_get_service_properties,
  1035. 170);
  1036. streamProperties->setProperty("changed", QVariant(true));
  1037. layout->addWidget(streamProperties);
  1038. QObject::connect(streamProperties, SIGNAL(Changed()),
  1039. this, STREAM1_CHANGED);
  1040. obs_data_release(settings);
  1041. }
  1042. void OBSBasicSettings::on_simpleOutputBrowse_clicked()
  1043. {
  1044. QString dir = QFileDialog::getExistingDirectory(this,
  1045. QTStr("Basic.Settings.Output.SelectDirectory"),
  1046. ui->simpleOutputPath->text(),
  1047. QFileDialog::ShowDirsOnly |
  1048. QFileDialog::DontResolveSymlinks);
  1049. if (dir.isEmpty())
  1050. return;
  1051. ui->simpleOutputPath->setText(dir);
  1052. }
  1053. void OBSBasicSettings::on_advOutRecPathBrowse_clicked()
  1054. {
  1055. QString dir = QFileDialog::getExistingDirectory(this,
  1056. QTStr("Basic.Settings.Output.SelectDirectory"),
  1057. ui->advOutRecPath->text(),
  1058. QFileDialog::ShowDirsOnly |
  1059. QFileDialog::DontResolveSymlinks);
  1060. if (dir.isEmpty())
  1061. return;
  1062. ui->advOutRecPath->setText(dir);
  1063. }
  1064. void OBSBasicSettings::on_advOutFFPathBrowse_clicked()
  1065. {
  1066. QString filter;
  1067. filter += QTStr("Basic.Settings.Output.Adv.FFmpeg.SaveFilter.Common");
  1068. filter += " (*.avi *.mp4 *.flv *.ts *.mkv *.wav *.aac);;";
  1069. filter += QTStr("Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All");
  1070. filter += " (*.*)";
  1071. QString file = QFileDialog::getSaveFileName(this,
  1072. QTStr("Basic.Settings.Output.SelectFile"),
  1073. ui->simpleOutputPath->text(), filter);
  1074. if (file.isEmpty())
  1075. return;
  1076. ui->advOutFFURL->setText(file);
  1077. }
  1078. void OBSBasicSettings::on_advOutEncoder_currentIndexChanged(int idx)
  1079. {
  1080. QString encoder = GetComboData(ui->advOutEncoder);
  1081. delete streamEncoderProps;
  1082. streamEncoderProps = CreateEncoderPropertyView(QT_TO_UTF8(encoder),
  1083. "obs-studio/basic/streamEncoder.json", true);
  1084. ui->advOutputStreamTab->layout()->addWidget(streamEncoderProps);
  1085. UNUSED_PARAMETER(idx);
  1086. }
  1087. void OBSBasicSettings::on_advOutRecEncoder_currentIndexChanged(int idx)
  1088. {
  1089. ui->advOutRecUseRescale->setEnabled(idx > 0);
  1090. ui->advOutRecRescaleContainer->setEnabled(idx > 0);
  1091. delete recordEncoderProps;
  1092. recordEncoderProps = nullptr;
  1093. if (idx > 0) {
  1094. QString encoder = GetComboData(ui->advOutRecEncoder);
  1095. recordEncoderProps = CreateEncoderPropertyView(
  1096. QT_TO_UTF8(encoder),
  1097. "obs-studio/basic/recordEncoder.json", true);
  1098. ui->advOutRecStandard->layout()->addWidget(recordEncoderProps);
  1099. }
  1100. }
  1101. #define INVALID_RES_STR "Basic.Settings.Video.InvalidResolution"
  1102. static bool ValidResolutions(Ui::OBSBasicSettings *ui)
  1103. {
  1104. QString baseRes = ui->baseResolution->lineEdit()->text();
  1105. QString outputRes = ui->outputResolution->lineEdit()->text();
  1106. uint32_t cx, cy;
  1107. if (!ConvertResText(QT_TO_UTF8(baseRes), cx, cy) ||
  1108. !ConvertResText(QT_TO_UTF8(outputRes), cx, cy)) {
  1109. ui->videoMsg->setText(QTStr(INVALID_RES_STR));
  1110. return false;
  1111. }
  1112. ui->videoMsg->setText("");
  1113. return true;
  1114. }
  1115. void OBSBasicSettings::on_baseResolution_editTextChanged(const QString &text)
  1116. {
  1117. if (!loading && ValidResolutions(ui.get())) {
  1118. QString baseResolution = text;
  1119. uint32_t cx, cy, out_cx, out_cy;
  1120. ConvertResText(QT_TO_UTF8(baseResolution), cx, cy);
  1121. QString outRes = ui->outputResolution->lineEdit()->text();
  1122. ConvertResText(QT_TO_UTF8(outRes), out_cx, out_cy);
  1123. ResetDownscales(cx, cy, out_cx, out_cy);
  1124. }
  1125. }
  1126. void OBSBasicSettings::GeneralChanged()
  1127. {
  1128. if (!loading) {
  1129. generalChanged = true;
  1130. sender()->setProperty("changed", QVariant(true));
  1131. EnableApplyButton(true);
  1132. }
  1133. }
  1134. void OBSBasicSettings::Stream1Changed()
  1135. {
  1136. if (!loading) {
  1137. stream1Changed = true;
  1138. sender()->setProperty("changed", QVariant(true));
  1139. EnableApplyButton(true);
  1140. }
  1141. }
  1142. void OBSBasicSettings::OutputsChanged()
  1143. {
  1144. if (!loading) {
  1145. outputsChanged = true;
  1146. sender()->setProperty("changed", QVariant(true));
  1147. EnableApplyButton(true);
  1148. }
  1149. }
  1150. void OBSBasicSettings::AudioChanged()
  1151. {
  1152. if (!loading) {
  1153. audioChanged = true;
  1154. sender()->setProperty("changed", QVariant(true));
  1155. EnableApplyButton(true);
  1156. }
  1157. }
  1158. void OBSBasicSettings::AudioChangedRestart()
  1159. {
  1160. if (!loading) {
  1161. audioChanged = true;
  1162. ui->audioMsg->setText(QTStr("Basic.Settings.ProgramRestart"));
  1163. sender()->setProperty("changed", QVariant(true));
  1164. EnableApplyButton(true);
  1165. }
  1166. }
  1167. void OBSBasicSettings::VideoChangedRestart()
  1168. {
  1169. if (!loading) {
  1170. videoChanged = true;
  1171. ui->videoMsg->setText(QTStr("Basic.Settings.ProgramRestart"));
  1172. sender()->setProperty("changed", QVariant(true));
  1173. EnableApplyButton(true);
  1174. }
  1175. }
  1176. void OBSBasicSettings::VideoChangedResolution()
  1177. {
  1178. if (!loading && ValidResolutions(ui.get())) {
  1179. videoChanged = true;
  1180. sender()->setProperty("changed", QVariant(true));
  1181. EnableApplyButton(true);
  1182. }
  1183. }
  1184. void OBSBasicSettings::VideoChanged()
  1185. {
  1186. if (!loading) {
  1187. videoChanged = true;
  1188. sender()->setProperty("changed", QVariant(true));
  1189. EnableApplyButton(true);
  1190. }
  1191. }