window-basic-settings.cpp 47 KB

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