window-basic-main.cpp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565
  1. /******************************************************************************
  2. Copyright (C) 2013-2014 by Hugh Bailey <[email protected]>
  3. Zachary Lund <[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 <QMessageBox>
  17. #include <QShowEvent>
  18. #include <QFileDialog>
  19. #include <QNetworkRequest>
  20. #include <QNetworkReply>
  21. #include <util/dstr.h>
  22. #include <util/util.hpp>
  23. #include <util/platform.h>
  24. #include "obs-app.hpp"
  25. #include "platform.hpp"
  26. #include "window-basic-settings.hpp"
  27. #include "window-namedialog.hpp"
  28. #include "window-basic-source-select.hpp"
  29. #include "window-basic-main.hpp"
  30. #include "window-basic-properties.hpp"
  31. #include "window-log-reply.hpp"
  32. #include "qt-wrappers.hpp"
  33. #include "display-helpers.hpp"
  34. #include "volume-control.hpp"
  35. #include "ui_OBSBasic.h"
  36. #include <fstream>
  37. #include <sstream>
  38. #include <QScreen>
  39. #include <QWindow>
  40. using namespace std;
  41. Q_DECLARE_METATYPE(OBSScene);
  42. Q_DECLARE_METATYPE(OBSSceneItem);
  43. Q_DECLARE_METATYPE(order_movement);
  44. OBSBasic::OBSBasic(QWidget *parent)
  45. : OBSMainWindow (parent),
  46. properties (nullptr),
  47. fileOutput (nullptr),
  48. streamOutput (nullptr),
  49. service (nullptr),
  50. aac (nullptr),
  51. x264 (nullptr),
  52. sceneChanging (false),
  53. resizeTimer (0),
  54. activeRefs (0),
  55. ui (new Ui::OBSBasic)
  56. {
  57. ui->setupUi(this);
  58. connect(windowHandle(), &QWindow::screenChanged, [this]() {
  59. struct obs_video_info ovi;
  60. if (obs_get_video_info(&ovi))
  61. ResizePreview(ovi.base_width, ovi.base_height);
  62. });
  63. stringstream name;
  64. name << "OBS " << App()->GetVersionString();
  65. blog(LOG_INFO, "%s", name.str().c_str());
  66. setWindowTitle(QT_UTF8(name.str().c_str()));
  67. }
  68. static void SaveAudioDevice(const char *name, int channel, obs_data_t parent)
  69. {
  70. obs_source_t source = obs_get_output_source(channel);
  71. if (!source)
  72. return;
  73. obs_data_t data = obs_save_source(source);
  74. obs_data_setobj(parent, name, data);
  75. obs_data_release(data);
  76. obs_source_release(source);
  77. }
  78. static obs_data_t GenerateSaveData()
  79. {
  80. obs_data_t saveData = obs_data_create();
  81. obs_data_array_t sourcesArray = obs_save_sources();
  82. obs_source_t currentScene = obs_get_output_source(0);
  83. const char *sceneName = obs_source_getname(currentScene);
  84. SaveAudioDevice(DESKTOP_AUDIO_1, 1, saveData);
  85. SaveAudioDevice(DESKTOP_AUDIO_2, 2, saveData);
  86. SaveAudioDevice(AUX_AUDIO_1, 3, saveData);
  87. SaveAudioDevice(AUX_AUDIO_2, 4, saveData);
  88. SaveAudioDevice(AUX_AUDIO_3, 5, saveData);
  89. obs_data_setstring(saveData, "current_scene", sceneName);
  90. obs_data_setarray(saveData, "sources", sourcesArray);
  91. obs_data_array_release(sourcesArray);
  92. obs_source_release(currentScene);
  93. return saveData;
  94. }
  95. void OBSBasic::ClearVolumeControls()
  96. {
  97. VolControl *control;
  98. for (size_t i = 0; i < volumes.size(); i++) {
  99. control = volumes[i];
  100. delete control;
  101. }
  102. volumes.clear();
  103. }
  104. void OBSBasic::Save(const char *file)
  105. {
  106. obs_data_t saveData = GenerateSaveData();
  107. const char *jsonData = obs_data_getjson(saveData);
  108. /* TODO maybe a message box here? */
  109. if (!os_quick_write_utf8_file(file, jsonData, strlen(jsonData), false))
  110. blog(LOG_ERROR, "Could not save scene data to %s", file);
  111. obs_data_release(saveData);
  112. }
  113. static void LoadAudioDevice(const char *name, int channel, obs_data_t parent)
  114. {
  115. obs_data_t data = obs_data_getobj(parent, name);
  116. if (!data)
  117. return;
  118. obs_source_t source = obs_load_source(data);
  119. if (source) {
  120. obs_set_output_source(channel, source);
  121. obs_source_release(source);
  122. }
  123. obs_data_release(data);
  124. }
  125. void OBSBasic::CreateDefaultScene()
  126. {
  127. obs_scene_t scene = obs_scene_create(Str("Basic.Scene"));
  128. obs_source_t source = obs_scene_getsource(scene);
  129. obs_add_source(source);
  130. #ifdef __APPLE__
  131. source = obs_source_create(OBS_SOURCE_TYPE_INPUT, "display_capture",
  132. Str("Basic.DisplayCapture"), NULL);
  133. if (source) {
  134. obs_scene_add(scene, source);
  135. obs_add_source(source);
  136. obs_source_release(source);
  137. }
  138. #endif
  139. obs_set_output_source(0, obs_scene_getsource(scene));
  140. obs_scene_release(scene);
  141. }
  142. void OBSBasic::Load(const char *file)
  143. {
  144. if (!file) {
  145. blog(LOG_ERROR, "Could not find file %s", file);
  146. return;
  147. }
  148. BPtr<char> jsonData = os_quick_read_utf8_file(file);
  149. if (!jsonData) {
  150. CreateDefaultScene();
  151. return;
  152. }
  153. obs_data_t data = obs_data_create_from_json(jsonData);
  154. obs_data_array_t sources = obs_data_getarray(data, "sources");
  155. const char *sceneName = obs_data_getstring(data, "current_scene");
  156. obs_source_t curScene;
  157. LoadAudioDevice(DESKTOP_AUDIO_1, 1, data);
  158. LoadAudioDevice(DESKTOP_AUDIO_2, 2, data);
  159. LoadAudioDevice(AUX_AUDIO_1, 3, data);
  160. LoadAudioDevice(AUX_AUDIO_2, 4, data);
  161. LoadAudioDevice(AUX_AUDIO_3, 5, data);
  162. obs_load_sources(sources);
  163. curScene = obs_get_source_by_name(sceneName);
  164. obs_set_output_source(0, curScene);
  165. obs_source_release(curScene);
  166. obs_data_array_release(sources);
  167. obs_data_release(data);
  168. }
  169. static inline bool HasAudioDevices(const char *source_id)
  170. {
  171. const char *output_id = source_id;
  172. obs_properties_t props = obs_get_source_properties(
  173. OBS_SOURCE_TYPE_INPUT, output_id, App()->GetLocale());
  174. size_t count = 0;
  175. if (!props)
  176. return false;
  177. obs_property_t devices = obs_properties_get(props, "device_id");
  178. if (devices)
  179. count = obs_property_list_item_count(devices);
  180. obs_properties_destroy(props);
  181. return count != 0;
  182. }
  183. static void OBSStartStreaming(void *data, calldata_t params)
  184. {
  185. UNUSED_PARAMETER(params);
  186. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  187. "StreamingStart");
  188. }
  189. static void OBSStopStreaming(void *data, calldata_t params)
  190. {
  191. int code = (int)calldata_int(params, "code");
  192. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  193. "StreamingStop", Q_ARG(int, code));
  194. }
  195. static void OBSStopRecording(void *data, calldata_t params)
  196. {
  197. UNUSED_PARAMETER(params);
  198. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  199. "RecordingStop");
  200. }
  201. #define SERVICE_PATH "obs-studio/basic/service.json"
  202. void OBSBasic::SaveService()
  203. {
  204. if (!service)
  205. return;
  206. BPtr<char> serviceJsonPath(os_get_config_path(SERVICE_PATH));
  207. if (!serviceJsonPath)
  208. return;
  209. obs_data_t data = obs_data_create();
  210. obs_data_t settings = obs_service_get_settings(service);
  211. obs_data_setstring(data, "type", obs_service_gettype(service));
  212. obs_data_setobj(data, "settings", settings);
  213. const char *json = obs_data_getjson(data);
  214. os_quick_write_utf8_file(serviceJsonPath, json, strlen(json), false);
  215. obs_data_release(settings);
  216. obs_data_release(data);
  217. }
  218. bool OBSBasic::LoadService()
  219. {
  220. const char *type;
  221. BPtr<char> serviceJsonPath(os_get_config_path(SERVICE_PATH));
  222. if (!serviceJsonPath)
  223. return false;
  224. BPtr<char> jsonText = os_quick_read_utf8_file(serviceJsonPath);
  225. if (!jsonText)
  226. return false;
  227. obs_data_t data = obs_data_create_from_json(jsonText);
  228. obs_data_set_default_string(data, "type", "rtmp_common");
  229. type = obs_data_getstring(data, "type");
  230. obs_data_t settings = obs_data_getobj(data, "settings");
  231. service = obs_service_create(type, "default", settings);
  232. obs_data_release(settings);
  233. obs_data_release(data);
  234. return !!service;
  235. }
  236. bool OBSBasic::InitOutputs()
  237. {
  238. fileOutput = obs_output_create("flv_output", "default", nullptr);
  239. if (!fileOutput)
  240. return false;
  241. streamOutput = obs_output_create("rtmp_output", "default", nullptr);
  242. if (!streamOutput)
  243. return false;
  244. signal_handler_connect(obs_output_signalhandler(streamOutput),
  245. "start", OBSStartStreaming, this);
  246. signal_handler_connect(obs_output_signalhandler(streamOutput),
  247. "stop", OBSStopStreaming, this);
  248. signal_handler_connect(obs_output_signalhandler(fileOutput),
  249. "stop", OBSStopRecording, this);
  250. return true;
  251. }
  252. bool OBSBasic::InitEncoders()
  253. {
  254. aac = obs_audio_encoder_create("ffmpeg_aac", "aac", nullptr);
  255. if (!aac)
  256. return false;
  257. x264 = obs_video_encoder_create("obs_x264", "h264", nullptr);
  258. if (!x264)
  259. return false;
  260. return true;
  261. }
  262. bool OBSBasic::InitService()
  263. {
  264. if (LoadService())
  265. return true;
  266. service = obs_service_create("rtmp_common", nullptr, nullptr);
  267. if (!service)
  268. return false;
  269. return true;
  270. }
  271. bool OBSBasic::InitBasicConfigDefaults()
  272. {
  273. bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource());
  274. bool hasInputAudio = HasAudioDevices(App()->InputAudioSource());
  275. config_set_default_int(basicConfig, "Window", "PosX", -1);
  276. config_set_default_int(basicConfig, "Window", "PosY", -1);
  277. config_set_default_int(basicConfig, "Window", "SizeX", -1);
  278. config_set_default_int(basicConfig, "Window", "SizeY", -1);
  279. vector<MonitorInfo> monitors;
  280. GetMonitors(monitors);
  281. if (!monitors.size()) {
  282. OBSErrorBox(NULL, "There appears to be no monitors. Er, this "
  283. "technically shouldn't be possible.");
  284. return false;
  285. }
  286. uint32_t cx = monitors[0].cx;
  287. uint32_t cy = monitors[0].cy;
  288. /* TODO: temporary */
  289. config_set_default_string(basicConfig, "SimpleOutput", "FilePath",
  290. GetDefaultVideoSavePath().c_str());
  291. config_set_default_uint (basicConfig, "SimpleOutput", "VBitrate",
  292. 2500);
  293. config_set_default_uint (basicConfig, "SimpleOutput", "ABitrate", 128);
  294. config_set_default_uint (basicConfig, "Video", "BaseCX", cx);
  295. config_set_default_uint (basicConfig, "Video", "BaseCY", cy);
  296. cx = cx * 10 / 15;
  297. cy = cy * 10 / 15;
  298. config_set_default_uint (basicConfig, "Video", "OutputCX", cx);
  299. config_set_default_uint (basicConfig, "Video", "OutputCY", cy);
  300. config_set_default_uint (basicConfig, "Video", "FPSType", 0);
  301. config_set_default_string(basicConfig, "Video", "FPSCommon", "30");
  302. config_set_default_uint (basicConfig, "Video", "FPSInt", 30);
  303. config_set_default_uint (basicConfig, "Video", "FPSNum", 30);
  304. config_set_default_uint (basicConfig, "Video", "FPSDen", 1);
  305. config_set_default_uint (basicConfig, "Audio", "SampleRate", 44100);
  306. config_set_default_string(basicConfig, "Audio", "ChannelSetup",
  307. "Stereo");
  308. config_set_default_uint (basicConfig, "Audio", "BufferingTime", 1000);
  309. config_set_default_string(basicConfig, "Audio", "DesktopDevice1",
  310. hasDesktopAudio ? "default" : "disabled");
  311. config_set_default_string(basicConfig, "Audio", "DesktopDevice2",
  312. "disabled");
  313. config_set_default_string(basicConfig, "Audio", "AuxDevice1",
  314. hasInputAudio ? "default" : "disabled");
  315. config_set_default_string(basicConfig, "Audio", "AuxDevice2",
  316. "disabled");
  317. config_set_default_string(basicConfig, "Audio", "AuxDevice3",
  318. "disabled");
  319. return true;
  320. }
  321. bool OBSBasic::InitBasicConfig()
  322. {
  323. BPtr<char> configPath(os_get_config_path("obs-studio/basic/basic.ini"));
  324. int code = basicConfig.Open(configPath, CONFIG_OPEN_ALWAYS);
  325. if (code != CONFIG_SUCCESS) {
  326. OBSErrorBox(NULL, "Failed to open basic.ini: %d", code);
  327. return false;
  328. }
  329. return InitBasicConfigDefaults();
  330. }
  331. void OBSBasic::InitOBSCallbacks()
  332. {
  333. signal_handler_connect(obs_signalhandler(), "source_add",
  334. OBSBasic::SourceAdded, this);
  335. signal_handler_connect(obs_signalhandler(), "source_remove",
  336. OBSBasic::SourceRemoved, this);
  337. signal_handler_connect(obs_signalhandler(), "channel_change",
  338. OBSBasic::ChannelChanged, this);
  339. signal_handler_connect(obs_signalhandler(), "source_activate",
  340. OBSBasic::SourceActivated, this);
  341. signal_handler_connect(obs_signalhandler(), "source_deactivate",
  342. OBSBasic::SourceDeactivated, this);
  343. }
  344. void OBSBasic::OBSInit()
  345. {
  346. BPtr<char> savePath(os_get_config_path("obs-studio/basic/scenes.json"));
  347. /* make sure it's fully displayed before doing any initialization */
  348. show();
  349. App()->processEvents();
  350. if (!obs_startup())
  351. throw "Failed to initialize libobs";
  352. if (!InitBasicConfig())
  353. throw "Failed to load basic.ini";
  354. if (!ResetVideo())
  355. throw "Failed to initialize video";
  356. if (!ResetAudio())
  357. throw "Failed to initialize audio";
  358. InitOBSCallbacks();
  359. /* TODO: this is a test, all modules will be searched for and loaded
  360. * automatically later */
  361. obs_load_module("test-input");
  362. obs_load_module("obs-ffmpeg");
  363. obs_load_module("obs-x264");
  364. obs_load_module("obs-outputs");
  365. obs_load_module("rtmp-services");
  366. #ifdef __APPLE__
  367. obs_load_module("mac-avcapture");
  368. obs_load_module("mac-capture");
  369. #elif _WIN32
  370. obs_load_module("win-wasapi");
  371. obs_load_module("win-capture");
  372. #else
  373. obs_load_module("linux-xshm");
  374. obs_load_module("linux-xcomposite");
  375. obs_load_module("linux-pulseaudio");
  376. #endif
  377. if (!InitOutputs())
  378. throw "Failed to initialize outputs";
  379. if (!InitEncoders())
  380. throw "Failed to initialize encoders";
  381. if (!InitService())
  382. throw "Failed to initialize service";
  383. Load(savePath);
  384. ResetAudioDevices();
  385. }
  386. OBSBasic::~OBSBasic()
  387. {
  388. BPtr<char> savePath(os_get_config_path("obs-studio/basic/scenes.json"));
  389. SaveService();
  390. Save(savePath);
  391. if (properties)
  392. delete properties;
  393. /* free the lists before shutting down to remove the scene/item
  394. * references */
  395. ClearVolumeControls();
  396. ui->sources->clear();
  397. ui->scenes->clear();
  398. obs_shutdown();
  399. }
  400. OBSScene OBSBasic::GetCurrentScene()
  401. {
  402. QListWidgetItem *item = ui->scenes->currentItem();
  403. return item ? item->data(Qt::UserRole).value<OBSScene>() : nullptr;
  404. }
  405. OBSSceneItem OBSBasic::GetCurrentSceneItem()
  406. {
  407. QListWidgetItem *item = ui->sources->currentItem();
  408. return item ? item->data(Qt::UserRole).value<OBSSceneItem>() : nullptr;
  409. }
  410. void OBSBasic::UpdateSources(OBSScene scene)
  411. {
  412. ui->sources->clear();
  413. obs_scene_enum_items(scene,
  414. [] (obs_scene_t scene, obs_sceneitem_t item, void *p)
  415. {
  416. OBSBasic *window = static_cast<OBSBasic*>(p);
  417. window->InsertSceneItem(item);
  418. UNUSED_PARAMETER(scene);
  419. return true;
  420. }, this);
  421. }
  422. void OBSBasic::InsertSceneItem(obs_sceneitem_t item)
  423. {
  424. obs_source_t source = obs_sceneitem_getsource(item);
  425. const char *name = obs_source_getname(source);
  426. QListWidgetItem *listItem = new QListWidgetItem(QT_UTF8(name));
  427. listItem->setData(Qt::UserRole,
  428. QVariant::fromValue(OBSSceneItem(item)));
  429. ui->sources->insertItem(0, listItem);
  430. }
  431. /* Qt callbacks for invokeMethod */
  432. void OBSBasic::AddScene(OBSSource source)
  433. {
  434. const char *name = obs_source_getname(source);
  435. obs_scene_t scene = obs_scene_fromsource(source);
  436. QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
  437. item->setData(Qt::UserRole, QVariant::fromValue(OBSScene(scene)));
  438. ui->scenes->addItem(item);
  439. signal_handler_t handler = obs_source_signalhandler(source);
  440. signal_handler_connect(handler, "item_add",
  441. OBSBasic::SceneItemAdded, this);
  442. signal_handler_connect(handler, "item_remove",
  443. OBSBasic::SceneItemRemoved, this);
  444. signal_handler_connect(handler, "item_move_up",
  445. OBSBasic::SceneItemMoveUp, this);
  446. signal_handler_connect(handler, "item_move_down",
  447. OBSBasic::SceneItemMoveDown, this);
  448. signal_handler_connect(handler, "item_move_top",
  449. OBSBasic::SceneItemMoveTop, this);
  450. signal_handler_connect(handler, "item_move_bottom",
  451. OBSBasic::SceneItemMoveBottom, this);
  452. }
  453. void OBSBasic::RemoveScene(OBSSource source)
  454. {
  455. const char *name = obs_source_getname(source);
  456. QListWidgetItem *sel = ui->scenes->currentItem();
  457. QList<QListWidgetItem*> items = ui->scenes->findItems(QT_UTF8(name),
  458. Qt::MatchExactly);
  459. if (sel != nullptr) {
  460. if (items.contains(sel))
  461. ui->sources->clear();
  462. delete sel;
  463. }
  464. }
  465. void OBSBasic::AddSceneItem(OBSSceneItem item)
  466. {
  467. obs_scene_t scene = obs_sceneitem_getscene(item);
  468. obs_source_t source = obs_sceneitem_getsource(item);
  469. if (GetCurrentScene() == scene)
  470. InsertSceneItem(item);
  471. sourceSceneRefs[source] = sourceSceneRefs[source] + 1;
  472. }
  473. void OBSBasic::RemoveSceneItem(OBSSceneItem item)
  474. {
  475. obs_scene_t scene = obs_sceneitem_getscene(item);
  476. if (GetCurrentScene() == scene) {
  477. for (int i = 0; i < ui->sources->count(); i++) {
  478. QListWidgetItem *listItem = ui->sources->item(i);
  479. QVariant userData = listItem->data(Qt::UserRole);
  480. if (userData.value<OBSSceneItem>() == item) {
  481. delete listItem;
  482. break;
  483. }
  484. }
  485. }
  486. obs_source_t source = obs_sceneitem_getsource(item);
  487. int scenes = sourceSceneRefs[source] - 1;
  488. sourceSceneRefs[source] = scenes;
  489. if (scenes == 0) {
  490. obs_source_remove(source);
  491. sourceSceneRefs.erase(source);
  492. }
  493. }
  494. void OBSBasic::UpdateSceneSelection(OBSSource source)
  495. {
  496. if (source) {
  497. obs_source_type type;
  498. obs_source_gettype(source, &type, NULL);
  499. obs_scene_t scene = obs_scene_fromsource(source);
  500. const char *name = obs_source_getname(source);
  501. if (!scene)
  502. return;
  503. QList<QListWidgetItem*> items =
  504. ui->scenes->findItems(QT_UTF8(name), Qt::MatchExactly);
  505. if (items.count()) {
  506. sceneChanging = true;
  507. ui->scenes->setCurrentItem(items.first());
  508. sceneChanging = false;
  509. UpdateSources(scene);
  510. }
  511. }
  512. }
  513. void OBSBasic::MoveSceneItem(OBSSceneItem item, order_movement movement)
  514. {
  515. OBSScene scene = obs_sceneitem_getscene(item);
  516. if (scene != GetCurrentScene())
  517. return;
  518. int curRow = ui->sources->currentRow();
  519. if (curRow == -1)
  520. return;
  521. QListWidgetItem *listItem = ui->sources->takeItem(curRow);
  522. switch (movement) {
  523. case ORDER_MOVE_UP:
  524. if (curRow > 0)
  525. curRow--;
  526. break;
  527. case ORDER_MOVE_DOWN:
  528. if (curRow < ui->sources->count())
  529. curRow++;
  530. break;
  531. case ORDER_MOVE_TOP:
  532. curRow = 0;
  533. break;
  534. case ORDER_MOVE_BOTTOM:
  535. curRow = ui->sources->count();
  536. break;
  537. }
  538. ui->sources->insertItem(curRow, listItem);
  539. ui->sources->setCurrentRow(curRow);
  540. }
  541. void OBSBasic::ActivateAudioSource(OBSSource source)
  542. {
  543. VolControl *vol = new VolControl(source);
  544. volumes.push_back(vol);
  545. ui->volumeWidgets->layout()->addWidget(vol);
  546. }
  547. void OBSBasic::DeactivateAudioSource(OBSSource source)
  548. {
  549. for (size_t i = 0; i < volumes.size(); i++) {
  550. if (volumes[i]->GetSource() == source) {
  551. delete volumes[i];
  552. volumes.erase(volumes.begin() + i);
  553. break;
  554. }
  555. }
  556. }
  557. /* OBS Callbacks */
  558. void OBSBasic::SceneItemAdded(void *data, calldata_t params)
  559. {
  560. OBSBasic *window = static_cast<OBSBasic*>(data);
  561. obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
  562. QMetaObject::invokeMethod(window, "AddSceneItem",
  563. Q_ARG(OBSSceneItem, OBSSceneItem(item)));
  564. }
  565. void OBSBasic::SceneItemRemoved(void *data, calldata_t params)
  566. {
  567. OBSBasic *window = static_cast<OBSBasic*>(data);
  568. obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
  569. QMetaObject::invokeMethod(window, "RemoveSceneItem",
  570. Q_ARG(OBSSceneItem, OBSSceneItem(item)));
  571. }
  572. void OBSBasic::SourceAdded(void *data, calldata_t params)
  573. {
  574. OBSBasic *window = static_cast<OBSBasic*>(data);
  575. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  576. if (obs_scene_fromsource(source) != NULL)
  577. QMetaObject::invokeMethod(window,
  578. "AddScene",
  579. Q_ARG(OBSSource, OBSSource(source)));
  580. else
  581. window->sourceSceneRefs[source] = 0;
  582. }
  583. void OBSBasic::SourceRemoved(void *data, calldata_t params)
  584. {
  585. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  586. if (obs_scene_fromsource(source) != NULL)
  587. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  588. "RemoveScene",
  589. Q_ARG(OBSSource, OBSSource(source)));
  590. }
  591. void OBSBasic::SourceActivated(void *data, calldata_t params)
  592. {
  593. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  594. uint32_t flags = obs_source_get_output_flags(source);
  595. if (flags & OBS_SOURCE_AUDIO)
  596. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  597. "ActivateAudioSource",
  598. Q_ARG(OBSSource, OBSSource(source)));
  599. }
  600. void OBSBasic::SourceDeactivated(void *data, calldata_t params)
  601. {
  602. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  603. uint32_t flags = obs_source_get_output_flags(source);
  604. if (flags & OBS_SOURCE_AUDIO)
  605. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  606. "DeactivateAudioSource",
  607. Q_ARG(OBSSource, OBSSource(source)));
  608. }
  609. void OBSBasic::ChannelChanged(void *data, calldata_t params)
  610. {
  611. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  612. uint32_t channel = (uint32_t)calldata_int(params, "channel");
  613. if (channel == 0)
  614. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  615. "UpdateSceneSelection",
  616. Q_ARG(OBSSource, OBSSource(source)));
  617. }
  618. void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
  619. {
  620. OBSBasic *window = static_cast<OBSBasic*>(data);
  621. obs_video_info ovi;
  622. int newCX, newCY;
  623. obs_get_video_info(&ovi);
  624. newCX = int(window->previewScale * float(ovi.base_width));
  625. newCY = int(window->previewScale * float(ovi.base_height));
  626. gs_viewport_push();
  627. gs_projection_push();
  628. gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height),
  629. -100.0f, 100.0f);
  630. gs_setviewport(window->previewX, window->previewY, newCX, newCY);
  631. obs_render_main_view();
  632. gs_projection_pop();
  633. gs_viewport_pop();
  634. UNUSED_PARAMETER(cx);
  635. UNUSED_PARAMETER(cy);
  636. }
  637. void OBSBasic::SceneItemMoveUp(void *data, calldata_t params)
  638. {
  639. OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(params, "item");
  640. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  641. "MoveSceneItem",
  642. Q_ARG(OBSSceneItem, OBSSceneItem(item)),
  643. Q_ARG(order_movement, ORDER_MOVE_UP));
  644. }
  645. void OBSBasic::SceneItemMoveDown(void *data, calldata_t params)
  646. {
  647. OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(params, "item");
  648. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  649. "MoveSceneItem",
  650. Q_ARG(OBSSceneItem, OBSSceneItem(item)),
  651. Q_ARG(order_movement, ORDER_MOVE_DOWN));
  652. }
  653. void OBSBasic::SceneItemMoveTop(void *data, calldata_t params)
  654. {
  655. OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(params, "item");
  656. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  657. "MoveSceneItem",
  658. Q_ARG(OBSSceneItem, OBSSceneItem(item)),
  659. Q_ARG(order_movement, ORDER_MOVE_TOP));
  660. }
  661. void OBSBasic::SceneItemMoveBottom(void *data, calldata_t params)
  662. {
  663. OBSSceneItem item = (obs_sceneitem_t)calldata_ptr(params, "item");
  664. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  665. "MoveSceneItem",
  666. Q_ARG(OBSSceneItem, OBSSceneItem(item)),
  667. Q_ARG(order_movement, ORDER_MOVE_BOTTOM));
  668. }
  669. /* Main class functions */
  670. obs_service_t OBSBasic::GetService()
  671. {
  672. if (!service)
  673. service = obs_service_create("rtmp_common", NULL, NULL);
  674. return service;
  675. }
  676. void OBSBasic::SetService(obs_service_t newService)
  677. {
  678. if (newService) {
  679. if (service)
  680. obs_service_destroy(service);
  681. service = newService;
  682. }
  683. }
  684. bool OBSBasic::ResetVideo()
  685. {
  686. struct obs_video_info ovi;
  687. GetConfigFPS(ovi.fps_num, ovi.fps_den);
  688. ovi.graphics_module = App()->GetRenderModule();
  689. ovi.base_width = (uint32_t)config_get_uint(basicConfig,
  690. "Video", "BaseCX");
  691. ovi.base_height = (uint32_t)config_get_uint(basicConfig,
  692. "Video", "BaseCY");
  693. ovi.output_width = (uint32_t)config_get_uint(basicConfig,
  694. "Video", "OutputCX");
  695. ovi.output_height = (uint32_t)config_get_uint(basicConfig,
  696. "Video", "OutputCY");
  697. ovi.output_format = VIDEO_FORMAT_NV12;
  698. ovi.adapter = 0;
  699. ovi.gpu_conversion = true;
  700. QTToGSWindow(ui->preview->winId(), ovi.window);
  701. //required to make opengl display stuff on osx(?)
  702. ResizePreview(ovi.base_width, ovi.base_height);
  703. QSize size = GetPixelSize(ui->preview);
  704. ovi.window_width = size.width();
  705. ovi.window_height = size.height();
  706. if (!obs_reset_video(&ovi))
  707. return false;
  708. obs_add_draw_callback(OBSBasic::RenderMain, this);
  709. return true;
  710. }
  711. bool OBSBasic::ResetAudio()
  712. {
  713. struct audio_output_info ai;
  714. ai.name = "Main Audio Track";
  715. ai.format = AUDIO_FORMAT_FLOAT;
  716. ai.samples_per_sec = config_get_uint(basicConfig, "Audio",
  717. "SampleRate");
  718. const char *channelSetupStr = config_get_string(basicConfig,
  719. "Audio", "ChannelSetup");
  720. if (strcmp(channelSetupStr, "Mono") == 0)
  721. ai.speakers = SPEAKERS_MONO;
  722. else
  723. ai.speakers = SPEAKERS_STEREO;
  724. ai.buffer_ms = config_get_uint(basicConfig, "Audio", "BufferingTime");
  725. return obs_reset_audio(&ai);
  726. }
  727. void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceName,
  728. const char *deviceDesc, int channel)
  729. {
  730. const char *deviceId = config_get_string(basicConfig, "Audio",
  731. deviceName);
  732. obs_source_t source;
  733. obs_data_t settings;
  734. bool same = false;
  735. source = obs_get_output_source(channel);
  736. if (source) {
  737. settings = obs_source_getsettings(source);
  738. const char *curId = obs_data_getstring(settings, "device_id");
  739. same = (strcmp(curId, deviceId) == 0);
  740. obs_data_release(settings);
  741. obs_source_release(source);
  742. }
  743. if (!same)
  744. obs_set_output_source(channel, nullptr);
  745. if (!same && strcmp(deviceId, "disabled") != 0) {
  746. obs_data_t settings = obs_data_create();
  747. obs_data_setstring(settings, "device_id", deviceId);
  748. source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
  749. sourceId, deviceDesc, settings);
  750. obs_data_release(settings);
  751. obs_set_output_source(channel, source);
  752. obs_source_release(source);
  753. }
  754. }
  755. void OBSBasic::ResetAudioDevices()
  756. {
  757. ResetAudioDevice(App()->OutputAudioSource(), "DesktopDevice1",
  758. Str("Basic.DesktopDevice1"), 1);
  759. ResetAudioDevice(App()->OutputAudioSource(), "DesktopDevice2",
  760. Str("Basic.DesktopDevice2"), 2);
  761. ResetAudioDevice(App()->InputAudioSource(), "AuxDevice1",
  762. Str("Basic.AuxDevice1"), 3);
  763. ResetAudioDevice(App()->InputAudioSource(), "AuxDevice2",
  764. Str("Basic.AuxDevice2"), 4);
  765. ResetAudioDevice(App()->InputAudioSource(), "AuxDevice3",
  766. Str("Basic.AuxDevice3"), 5);
  767. }
  768. void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
  769. {
  770. QSize targetSize;
  771. /* resize preview panel to fix to the top section of the window */
  772. targetSize = GetPixelSize(ui->preview);
  773. GetScaleAndCenterPos(int(cx), int(cy),
  774. targetSize.width(), targetSize.height(),
  775. previewX, previewY, previewScale);
  776. if (isVisible()) {
  777. if (resizeTimer)
  778. killTimer(resizeTimer);
  779. resizeTimer = startTimer(100);
  780. }
  781. }
  782. void OBSBasic::closeEvent(QCloseEvent *event)
  783. {
  784. QWidget::closeEvent(event);
  785. if (!event->isAccepted())
  786. return;
  787. // remove draw callback in case our drawable surfaces go away before
  788. // the destructor gets called
  789. obs_remove_draw_callback(OBSBasic::RenderMain, this);
  790. }
  791. void OBSBasic::changeEvent(QEvent *event)
  792. {
  793. /* TODO */
  794. UNUSED_PARAMETER(event);
  795. }
  796. void OBSBasic::resizeEvent(QResizeEvent *event)
  797. {
  798. struct obs_video_info ovi;
  799. if (obs_get_video_info(&ovi))
  800. ResizePreview(ovi.base_width, ovi.base_height);
  801. UNUSED_PARAMETER(event);
  802. }
  803. void OBSBasic::timerEvent(QTimerEvent *event)
  804. {
  805. if (event->timerId() == resizeTimer) {
  806. killTimer(resizeTimer);
  807. resizeTimer = 0;
  808. QSize size = GetPixelSize(ui->preview);
  809. obs_resize(size.width(), size.height());
  810. }
  811. }
  812. void OBSBasic::on_action_New_triggered()
  813. {
  814. /* TODO */
  815. }
  816. void OBSBasic::on_action_Open_triggered()
  817. {
  818. /* TODO */
  819. }
  820. void OBSBasic::on_action_Save_triggered()
  821. {
  822. /* TODO */
  823. }
  824. void OBSBasic::on_action_Settings_triggered()
  825. {
  826. OBSBasicSettings settings(this);
  827. settings.exec();
  828. }
  829. void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current,
  830. QListWidgetItem *prev)
  831. {
  832. obs_source_t source = NULL;
  833. if (sceneChanging)
  834. return;
  835. if (current) {
  836. obs_scene_t scene;
  837. scene = current->data(Qt::UserRole).value<OBSScene>();
  838. source = obs_scene_getsource(scene);
  839. }
  840. /* TODO: allow transitions */
  841. obs_set_output_source(0, source);
  842. UNUSED_PARAMETER(prev);
  843. }
  844. void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
  845. {
  846. /* TODO */
  847. UNUSED_PARAMETER(pos);
  848. }
  849. void OBSBasic::on_actionAddScene_triggered()
  850. {
  851. string name;
  852. QString format{QTStr("Basic.Main.DefaultSceneName.Text")};
  853. int i = 1;
  854. QString placeHolderText = format.arg(i);
  855. obs_source_t source = nullptr;
  856. while ((source = obs_get_source_by_name(QT_TO_UTF8(placeHolderText)))) {
  857. obs_source_release(source);
  858. placeHolderText = format.arg(++i);
  859. }
  860. bool accepted = NameDialog::AskForName(this,
  861. QTStr("Basic.Main.AddSceneDlg.Title"),
  862. QTStr("Basic.Main.AddSceneDlg.Text"),
  863. name,
  864. placeHolderText);
  865. if (accepted) {
  866. if (name.empty()) {
  867. QMessageBox::information(this,
  868. QTStr("NoNameEntered"),
  869. QTStr("NoNameEntered"));
  870. on_actionAddScene_triggered();
  871. return;
  872. }
  873. obs_source_t source = obs_get_source_by_name(name.c_str());
  874. if (source) {
  875. QMessageBox::information(this,
  876. QTStr("NameExists.Title"),
  877. QTStr("NameExists.Text"));
  878. obs_source_release(source);
  879. on_actionAddScene_triggered();
  880. return;
  881. }
  882. obs_scene_t scene = obs_scene_create(name.c_str());
  883. source = obs_scene_getsource(scene);
  884. obs_add_source(source);
  885. obs_scene_release(scene);
  886. obs_set_output_source(0, source);
  887. }
  888. }
  889. void OBSBasic::on_actionRemoveScene_triggered()
  890. {
  891. QListWidgetItem *item = ui->scenes->currentItem();
  892. if (!item)
  893. return;
  894. QVariant userData = item->data(Qt::UserRole);
  895. obs_scene_t scene = userData.value<OBSScene>();
  896. obs_source_t source = obs_scene_getsource(scene);
  897. obs_source_remove(source);
  898. }
  899. void OBSBasic::on_actionSceneProperties_triggered()
  900. {
  901. /* TODO */
  902. }
  903. void OBSBasic::on_actionSceneUp_triggered()
  904. {
  905. /* TODO */
  906. }
  907. void OBSBasic::on_actionSceneDown_triggered()
  908. {
  909. /* TODO */
  910. }
  911. void OBSBasic::on_sources_currentItemChanged(QListWidgetItem *current,
  912. QListWidgetItem *prev)
  913. {
  914. /* TODO */
  915. UNUSED_PARAMETER(current);
  916. UNUSED_PARAMETER(prev);
  917. }
  918. void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
  919. {
  920. /* TODO */
  921. UNUSED_PARAMETER(pos);
  922. }
  923. void OBSBasic::AddSource(const char *id)
  924. {
  925. OBSBasicSourceSelect sourceSelect(this, id);
  926. sourceSelect.exec();
  927. }
  928. void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
  929. {
  930. const char *type;
  931. bool foundValues = false;
  932. size_t idx = 0;
  933. if (!GetCurrentScene()) {
  934. // Tell the user he needs a scene first (help beginners).
  935. QMessageBox::information(this,
  936. QTStr("Basic.Main.AddSourceHelp.Title"),
  937. QTStr("Basic.Main.AddSourceHelp.Text"));
  938. return;
  939. }
  940. QMenu popup;
  941. while (obs_enum_input_types(idx++, &type)) {
  942. const char *name = obs_source_getdisplayname(
  943. OBS_SOURCE_TYPE_INPUT,
  944. type, App()->GetLocale());
  945. if (strcmp(type, "scene") == 0)
  946. continue;
  947. QAction *popupItem = new QAction(QT_UTF8(name), this);
  948. popupItem->setData(QT_UTF8(type));
  949. popup.addAction(popupItem);
  950. foundValues = true;
  951. }
  952. if (foundValues) {
  953. QAction *ret = popup.exec(pos);
  954. if (ret)
  955. AddSource(ret->data().toString().toUtf8());
  956. }
  957. }
  958. void OBSBasic::on_actionAddSource_triggered()
  959. {
  960. AddSourcePopupMenu(QCursor::pos());
  961. }
  962. void OBSBasic::on_actionRemoveSource_triggered()
  963. {
  964. OBSSceneItem item = GetCurrentSceneItem();
  965. if (item)
  966. obs_sceneitem_remove(item);
  967. }
  968. void OBSBasic::on_actionSourceProperties_triggered()
  969. {
  970. OBSSceneItem item = GetCurrentSceneItem();
  971. OBSSource source = obs_sceneitem_getsource(item);
  972. if (source) {
  973. delete properties;
  974. properties = new OBSBasicProperties(this, source);
  975. properties->Init();
  976. }
  977. }
  978. void OBSBasic::on_actionSourceUp_triggered()
  979. {
  980. OBSSceneItem item = GetCurrentSceneItem();
  981. obs_sceneitem_setorder(item, ORDER_MOVE_UP);
  982. }
  983. void OBSBasic::on_actionSourceDown_triggered()
  984. {
  985. OBSSceneItem item = GetCurrentSceneItem();
  986. obs_sceneitem_setorder(item, ORDER_MOVE_DOWN);
  987. }
  988. static char *ReadLogFile(const char *log)
  989. {
  990. BPtr<char> logDir(os_get_config_path("obs-studio/logs"));
  991. string path = (char*)logDir;
  992. path += "/";
  993. path += log;
  994. char *file = os_quick_read_utf8_file(path.c_str());
  995. if (!file)
  996. blog(LOG_WARNING, "Failed to read log file %s", path.c_str());
  997. return file;
  998. }
  999. void OBSBasic::UploadLog(const char *file)
  1000. {
  1001. dstr fileString = {};
  1002. stringstream ss;
  1003. string jsonData;
  1004. dstr_move_array(&fileString, ReadLogFile(file));
  1005. if (!fileString.array)
  1006. return;
  1007. if (!*fileString.array) {
  1008. dstr_free(&fileString);
  1009. return;
  1010. }
  1011. ui->menuLogFiles->setEnabled(false);
  1012. dstr_replace(&fileString, "\\", "\\\\");
  1013. dstr_replace(&fileString, "\"", "\\\"");
  1014. dstr_replace(&fileString, "\n", "\\n");
  1015. dstr_replace(&fileString, "\r", "\\r");
  1016. dstr_replace(&fileString, "\t", "\\t");
  1017. dstr_replace(&fileString, "\t", "\\t");
  1018. dstr_replace(&fileString, "/", "\\/");
  1019. ss << "{ \"public\": false, \"description\": \"OBS " <<
  1020. App()->GetVersionString() << " log file uploaded at " <<
  1021. CurrentDateTimeString() << "\", \"files\": { \"" <<
  1022. file << "\": { \"content\": \"" <<
  1023. fileString.array << "\" } } }";
  1024. jsonData = std::move(ss.str());
  1025. logUploadPostData.setData(jsonData.c_str(), jsonData.size());
  1026. QUrl url("https://api.github.com/gists");
  1027. logUploadReply = networkManager.post(QNetworkRequest(url),
  1028. &logUploadPostData);
  1029. connect(logUploadReply, SIGNAL(finished()),
  1030. this, SLOT(logUploadFinished()));
  1031. connect(logUploadReply, SIGNAL(readyRead()),
  1032. this, SLOT(logUploadRead()));
  1033. dstr_free(&fileString);
  1034. }
  1035. void OBSBasic::on_actionUploadCurrentLog_triggered()
  1036. {
  1037. UploadLog(App()->GetCurrentLog());
  1038. }
  1039. void OBSBasic::on_actionUploadLastLog_triggered()
  1040. {
  1041. UploadLog(App()->GetLastLog());
  1042. }
  1043. void OBSBasic::logUploadRead()
  1044. {
  1045. logUploadReturnData.push_back(logUploadReply->readAll());
  1046. }
  1047. void OBSBasic::logUploadFinished()
  1048. {
  1049. ui->menuLogFiles->setEnabled(true);
  1050. if (logUploadReply->error()) {
  1051. QMessageBox::information(this,
  1052. QTStr("LogReturnDialog.ErrorUploadingLog"),
  1053. logUploadReply->errorString());
  1054. return;
  1055. }
  1056. const char *jsonReply = logUploadReturnData.constData();
  1057. if (!jsonReply || !*jsonReply)
  1058. return;
  1059. obs_data_t returnData = obs_data_create_from_json(jsonReply);
  1060. QString logURL = obs_data_getstring(returnData, "html_url");
  1061. obs_data_release(returnData);
  1062. OBSLogReply logDialog(this, logURL);
  1063. logDialog.exec();
  1064. }
  1065. void OBSBasic::StreamingStart()
  1066. {
  1067. ui->streamButton->setText("Stop Streaming");
  1068. ui->streamButton->setEnabled(true);
  1069. }
  1070. void OBSBasic::StreamingStop(int code)
  1071. {
  1072. const char *errorMessage;
  1073. switch (code) {
  1074. case OBS_OUTPUT_BAD_PATH:
  1075. errorMessage = Str("Output.ConnectFail.BadPath");
  1076. break;
  1077. case OBS_OUTPUT_CONNECT_FAILED:
  1078. errorMessage = Str("Output.ConnectFail.ConnectFailed");
  1079. break;
  1080. case OBS_OUTPUT_INVALID_STREAM:
  1081. errorMessage = Str("Output.ConnectFail.InvalidStream");
  1082. break;
  1083. case OBS_OUTPUT_ERROR:
  1084. errorMessage = Str("Output.ConnectFail.Error");
  1085. break;
  1086. case OBS_OUTPUT_DISCONNECTED:
  1087. /* doesn't happen if output is set to reconnect. note that
  1088. * reconnects are handled in the output, not in the UI */
  1089. errorMessage = Str("Output.ConnectFail.Disconnected");
  1090. }
  1091. activeRefs--;
  1092. ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
  1093. ui->streamButton->setEnabled(true);
  1094. if (code != OBS_OUTPUT_SUCCESS)
  1095. QMessageBox::information(this,
  1096. QTStr("Output.ConnectFail.Title"),
  1097. QT_UTF8(errorMessage));
  1098. }
  1099. void OBSBasic::RecordingStop()
  1100. {
  1101. activeRefs--;
  1102. ui->recordButton->setText(QTStr("Basic.Main.StartRecording"));
  1103. }
  1104. void OBSBasic::SetupEncoders()
  1105. {
  1106. if (activeRefs == 0) {
  1107. obs_data_t x264Settings = obs_data_create();
  1108. obs_data_t aacSettings = obs_data_create();
  1109. int videoBitrate = config_get_uint(basicConfig, "SimpleOutput",
  1110. "VBitrate");
  1111. int audioBitrate = config_get_uint(basicConfig, "SimpleOutput",
  1112. "ABitrate");
  1113. obs_data_setint(x264Settings, "bitrate", videoBitrate);
  1114. obs_data_setbool(x264Settings, "cbr", true);
  1115. obs_data_setint(aacSettings, "bitrate", audioBitrate);
  1116. obs_encoder_update(x264, x264Settings);
  1117. obs_encoder_update(aac, aacSettings);
  1118. obs_data_release(x264Settings);
  1119. obs_data_release(aacSettings);
  1120. obs_encoder_set_video(x264, obs_video());
  1121. obs_encoder_set_audio(aac, obs_audio());
  1122. }
  1123. }
  1124. void OBSBasic::on_streamButton_clicked()
  1125. {
  1126. if (obs_output_active(streamOutput)) {
  1127. obs_output_stop(streamOutput);
  1128. } else {
  1129. SaveService();
  1130. SetupEncoders();
  1131. obs_output_set_video_encoder(streamOutput, x264);
  1132. obs_output_set_audio_encoder(streamOutput, aac);
  1133. obs_output_set_service(streamOutput, service);
  1134. if (obs_output_start(streamOutput)) {
  1135. activeRefs++;
  1136. ui->streamButton->setEnabled(false);
  1137. ui->streamButton->setText(
  1138. QTStr("Basic.Main.Connecting"));
  1139. }
  1140. }
  1141. }
  1142. void OBSBasic::on_recordButton_clicked()
  1143. {
  1144. if (obs_output_active(fileOutput)) {
  1145. obs_output_stop(fileOutput);
  1146. } else {
  1147. const char *path = config_get_string(basicConfig,
  1148. "SimpleOutput", "FilePath");
  1149. os_dir_t dir = path ? os_opendir(path) : nullptr;
  1150. if (!dir) {
  1151. QMessageBox::information(this,
  1152. QTStr("Output.BadPath.Title"),
  1153. QTStr("Output.BadPath.Text"));
  1154. return;
  1155. }
  1156. os_closedir(dir);
  1157. string strPath;
  1158. strPath += path;
  1159. char lastChar = strPath.back();
  1160. if (lastChar != '/' && lastChar != '\\')
  1161. strPath += "/";
  1162. strPath += GenerateTimeDateFilename("flv");
  1163. SetupEncoders();
  1164. obs_output_set_video_encoder(fileOutput, x264);
  1165. obs_output_set_audio_encoder(fileOutput, aac);
  1166. obs_data_t settings = obs_data_create();
  1167. obs_data_setstring(settings, "path", strPath.c_str());
  1168. obs_output_update(fileOutput, settings);
  1169. obs_data_release(settings);
  1170. if (obs_output_start(fileOutput)) {
  1171. activeRefs++;
  1172. ui->recordButton->setText(
  1173. QTStr("Basic.Main.StopRecording"));
  1174. }
  1175. }
  1176. }
  1177. void OBSBasic::on_settingsButton_clicked()
  1178. {
  1179. OBSBasicSettings settings(this);
  1180. settings.exec();
  1181. }
  1182. void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const
  1183. {
  1184. const char *val = config_get_string(basicConfig, "Video", "FPSCommon");
  1185. if (strcmp(val, "10") == 0) {
  1186. num = 10;
  1187. den = 1;
  1188. } else if (strcmp(val, "20") == 0) {
  1189. num = 20;
  1190. den = 1;
  1191. } else if (strcmp(val, "25") == 0) {
  1192. num = 25;
  1193. den = 1;
  1194. } else if (strcmp(val, "29.97") == 0) {
  1195. num = 30000;
  1196. den = 1001;
  1197. } else if (strcmp(val, "48") == 0) {
  1198. num = 48;
  1199. den = 1;
  1200. } else if (strcmp(val, "59.94") == 0) {
  1201. num = 60000;
  1202. den = 1001;
  1203. } else if (strcmp(val, "60") == 0) {
  1204. num = 60;
  1205. den = 1;
  1206. } else {
  1207. num = 30;
  1208. den = 1;
  1209. }
  1210. }
  1211. void OBSBasic::GetFPSInteger(uint32_t &num, uint32_t &den) const
  1212. {
  1213. num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSInt");
  1214. den = 1;
  1215. }
  1216. void OBSBasic::GetFPSFraction(uint32_t &num, uint32_t &den) const
  1217. {
  1218. num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNum");
  1219. den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSDen");
  1220. }
  1221. void OBSBasic::GetFPSNanoseconds(uint32_t &num, uint32_t &den) const
  1222. {
  1223. num = 1000000000;
  1224. den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNS");
  1225. }
  1226. void OBSBasic::GetConfigFPS(uint32_t &num, uint32_t &den) const
  1227. {
  1228. uint32_t type = config_get_uint(basicConfig, "Video", "FPSType");
  1229. if (type == 1) //"Integer"
  1230. GetFPSInteger(num, den);
  1231. else if (type == 2) //"Fraction"
  1232. GetFPSFraction(num, den);
  1233. else if (false) //"Nanoseconds", currently not implemented
  1234. GetFPSNanoseconds(num, den);
  1235. else
  1236. GetFPSCommon(num, den);
  1237. }
  1238. config_t OBSBasic::Config() const
  1239. {
  1240. return basicConfig;
  1241. }