window-basic-main.cpp 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. /******************************************************************************
  2. Copyright (C) 2013-2014 by Hugh Bailey <[email protected]>
  3. Copyright (C) 2014 by 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 <util/util.hpp>
  20. #include <util/platform.h>
  21. #include "obs-app.hpp"
  22. #include "platform.hpp"
  23. #include "window-basic-settings.hpp"
  24. #include "window-namedialog.hpp"
  25. #include "window-basic-main.hpp"
  26. #include "window-basic-properties.hpp"
  27. #include "qt-wrappers.hpp"
  28. #include "display-helpers.hpp"
  29. #include "ui_OBSBasic.h"
  30. #include <sstream>
  31. #include <QScreen>
  32. #include <QWindow>
  33. using namespace std;
  34. Q_DECLARE_METATYPE(OBSScene);
  35. Q_DECLARE_METATYPE(OBSSceneItem);
  36. OBSBasic::OBSBasic(QWidget *parent)
  37. : OBSMainWindow (parent),
  38. outputTest (nullptr),
  39. aac (nullptr),
  40. x264 (nullptr),
  41. sceneChanging (false),
  42. resizeTimer (0),
  43. properties (nullptr),
  44. ui (new Ui::OBSBasic)
  45. {
  46. ui->setupUi(this);
  47. connect(windowHandle(), &QWindow::screenChanged, [this]() {
  48. struct obs_video_info ovi;
  49. if (obs_get_video_info(&ovi))
  50. ResizePreview(ovi.base_width, ovi.base_height);
  51. });
  52. }
  53. static inline bool HasAudioDevices(const char *source_id)
  54. {
  55. const char *output_id = source_id;
  56. obs_properties_t props = obs_get_source_properties(
  57. OBS_SOURCE_TYPE_INPUT, output_id, App()->GetLocale());
  58. size_t count = 0;
  59. if (!props)
  60. return false;
  61. obs_property_t devices = obs_properties_get(props, "device_id");
  62. if (devices)
  63. count = obs_property_list_item_count(devices);
  64. obs_properties_destroy(props);
  65. return count != 0;
  66. }
  67. bool OBSBasic::InitBasicConfigDefaults()
  68. {
  69. bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource());
  70. bool hasInputAudio = HasAudioDevices(App()->InputAudioSource());
  71. config_set_default_int(basicConfig, "Window", "PosX", -1);
  72. config_set_default_int(basicConfig, "Window", "PosY", -1);
  73. config_set_default_int(basicConfig, "Window", "SizeX", -1);
  74. config_set_default_int(basicConfig, "Window", "SizeY", -1);
  75. vector<MonitorInfo> monitors;
  76. GetMonitors(monitors);
  77. if (!monitors.size()) {
  78. OBSErrorBox(NULL, "There appears to be no monitors. Er, this "
  79. "technically shouldn't be possible.");
  80. return false;
  81. }
  82. uint32_t cx = monitors[0].cx;
  83. uint32_t cy = monitors[0].cy;
  84. /* TODO: temporary */
  85. config_set_default_string(basicConfig, "OutputTemp", "URL", "");
  86. config_set_default_string(basicConfig, "OutputTemp", "Key", "");
  87. config_set_default_uint (basicConfig, "OutputTemp", "VBitrate", 2500);
  88. config_set_default_uint (basicConfig, "OutputTemp", "ABitrate", 128);
  89. config_set_default_uint (basicConfig, "Video", "BaseCX", cx);
  90. config_set_default_uint (basicConfig, "Video", "BaseCY", cy);
  91. cx = cx * 10 / 15;
  92. cy = cy * 10 / 15;
  93. config_set_default_uint (basicConfig, "Video", "OutputCX", cx);
  94. config_set_default_uint (basicConfig, "Video", "OutputCY", cy);
  95. config_set_default_uint (basicConfig, "Video", "FPSType", 0);
  96. config_set_default_string(basicConfig, "Video", "FPSCommon", "30");
  97. config_set_default_uint (basicConfig, "Video", "FPSInt", 30);
  98. config_set_default_uint (basicConfig, "Video", "FPSNum", 30);
  99. config_set_default_uint (basicConfig, "Video", "FPSDen", 1);
  100. config_set_default_uint (basicConfig, "Audio", "SampleRate", 44100);
  101. config_set_default_string(basicConfig, "Audio", "ChannelSetup",
  102. "Stereo");
  103. config_set_default_uint (basicConfig, "Audio", "BufferingTime", 1000);
  104. config_set_default_string(basicConfig, "Audio", "DesktopDevice1",
  105. hasDesktopAudio ? "default" : "disabled");
  106. config_set_default_string(basicConfig, "Audio", "DesktopDevice2",
  107. "disabled");
  108. config_set_default_string(basicConfig, "Audio", "AuxDevice1",
  109. hasInputAudio ? "default" : "disabled");
  110. config_set_default_string(basicConfig, "Audio", "AuxDevice2",
  111. "disabled");
  112. config_set_default_string(basicConfig, "Audio", "AuxDevice3",
  113. "disabled");
  114. return true;
  115. }
  116. bool OBSBasic::InitBasicConfig()
  117. {
  118. BPtr<char> configPath(os_get_config_path("obs-studio/basic/basic.ini"));
  119. int errorcode = basicConfig.Open(configPath, CONFIG_OPEN_ALWAYS);
  120. if (errorcode != CONFIG_SUCCESS) {
  121. OBSErrorBox(NULL, "Failed to open basic.ini: %d", errorcode);
  122. return false;
  123. }
  124. return InitBasicConfigDefaults();
  125. }
  126. void OBSBasic::OBSInit()
  127. {
  128. /* make sure it's fully displayed before doing any initialization */
  129. show();
  130. App()->processEvents();
  131. if (!obs_startup())
  132. throw "Failed to initialize libobs";
  133. if (!InitBasicConfig())
  134. throw "Failed to load basic.ini";
  135. if (!ResetVideo())
  136. throw "Failed to initialize video";
  137. if (!ResetAudio())
  138. throw "Failed to initialize audio";
  139. signal_handler_connect(obs_signalhandler(), "source_add",
  140. OBSBasic::SourceAdded, this);
  141. signal_handler_connect(obs_signalhandler(), "source_remove",
  142. OBSBasic::SourceRemoved, this);
  143. signal_handler_connect(obs_signalhandler(), "channel_change",
  144. OBSBasic::ChannelChanged, this);
  145. /* TODO: this is a test, all modules will be searched for and loaded
  146. * automatically later */
  147. obs_load_module("test-input");
  148. obs_load_module("obs-ffmpeg");
  149. obs_load_module("obs-x264");
  150. obs_load_module("obs-outputs");
  151. #ifdef __APPLE__
  152. obs_load_module("mac-capture");
  153. #elif _WIN32
  154. obs_load_module("win-wasapi");
  155. obs_load_module("win-capture");
  156. #else
  157. obs_load_module("linux-xshm");
  158. obs_load_module("linux-pulseaudio");
  159. #endif
  160. ResetAudioDevices();
  161. }
  162. OBSBasic::~OBSBasic()
  163. {
  164. if (properties)
  165. delete properties;
  166. /* free the lists before shutting down to remove the scene/item
  167. * references */
  168. ui->sources->clear();
  169. ui->scenes->clear();
  170. obs_shutdown();
  171. }
  172. OBSScene OBSBasic::GetCurrentScene()
  173. {
  174. QListWidgetItem *item = ui->scenes->currentItem();
  175. return item ? item->data(Qt::UserRole).value<OBSScene>() : nullptr;
  176. }
  177. OBSSceneItem OBSBasic::GetCurrentSceneItem()
  178. {
  179. QListWidgetItem *item = ui->sources->currentItem();
  180. return item ? item->data(Qt::UserRole).value<OBSSceneItem>() : nullptr;
  181. }
  182. void OBSBasic::UpdateSources(OBSScene scene)
  183. {
  184. ui->sources->clear();
  185. obs_scene_enum_items(scene,
  186. [] (obs_scene_t scene, obs_sceneitem_t item, void *p)
  187. {
  188. OBSBasic *window = static_cast<OBSBasic*>(p);
  189. window->InsertSceneItem(item);
  190. UNUSED_PARAMETER(scene);
  191. return true;
  192. }, this);
  193. }
  194. void OBSBasic::InsertSceneItem(obs_sceneitem_t item)
  195. {
  196. obs_source_t source = obs_sceneitem_getsource(item);
  197. const char *name = obs_source_getname(source);
  198. QListWidgetItem *listItem = new QListWidgetItem(QT_UTF8(name));
  199. listItem->setData(Qt::UserRole,
  200. QVariant::fromValue(OBSSceneItem(item)));
  201. ui->sources->insertItem(0, listItem);
  202. }
  203. /* Qt callbacks for invokeMethod */
  204. void OBSBasic::AddScene(OBSSource source)
  205. {
  206. const char *name = obs_source_getname(source);
  207. obs_scene_t scene = obs_scene_fromsource(source);
  208. QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
  209. item->setData(Qt::UserRole, QVariant::fromValue(OBSScene(scene)));
  210. ui->scenes->addItem(item);
  211. signal_handler_t handler = obs_source_signalhandler(source);
  212. signal_handler_connect(handler, "item_add",
  213. OBSBasic::SceneItemAdded, this);
  214. signal_handler_connect(handler, "item_remove",
  215. OBSBasic::SceneItemRemoved, this);
  216. }
  217. void OBSBasic::RemoveScene(OBSSource source)
  218. {
  219. const char *name = obs_source_getname(source);
  220. QListWidgetItem *sel = ui->scenes->currentItem();
  221. QList<QListWidgetItem*> items = ui->scenes->findItems(QT_UTF8(name),
  222. Qt::MatchExactly);
  223. if (sel != nullptr) {
  224. if (items.contains(sel))
  225. ui->sources->clear();
  226. delete sel;
  227. }
  228. }
  229. void OBSBasic::AddSceneItem(OBSSceneItem item)
  230. {
  231. obs_scene_t scene = obs_sceneitem_getscene(item);
  232. obs_source_t source = obs_sceneitem_getsource(item);
  233. if (GetCurrentScene() == scene)
  234. InsertSceneItem(item);
  235. sourceSceneRefs[source] = sourceSceneRefs[source] + 1;
  236. }
  237. void OBSBasic::RemoveSceneItem(OBSSceneItem item)
  238. {
  239. obs_scene_t scene = obs_sceneitem_getscene(item);
  240. if (GetCurrentScene() == scene) {
  241. for (int i = 0; i < ui->sources->count(); i++) {
  242. QListWidgetItem *listItem = ui->sources->item(i);
  243. QVariant userData = listItem->data(Qt::UserRole);
  244. if (userData.value<OBSSceneItem>() == item) {
  245. delete listItem;
  246. break;
  247. }
  248. }
  249. }
  250. obs_source_t source = obs_sceneitem_getsource(item);
  251. int scenes = sourceSceneRefs[source] - 1;
  252. if (scenes == 0) {
  253. obs_source_remove(source);
  254. sourceSceneRefs.erase(source);
  255. }
  256. }
  257. void OBSBasic::UpdateSceneSelection(OBSSource source)
  258. {
  259. if (source) {
  260. obs_source_type type;
  261. obs_source_gettype(source, &type, NULL);
  262. if (type != OBS_SOURCE_TYPE_SCENE)
  263. return;
  264. obs_scene_t scene = obs_scene_fromsource(source);
  265. const char *name = obs_source_getname(source);
  266. QList<QListWidgetItem*> items =
  267. ui->scenes->findItems(QT_UTF8(name), Qt::MatchExactly);
  268. if (items.count()) {
  269. sceneChanging = true;
  270. ui->scenes->setCurrentItem(items.first());
  271. sceneChanging = false;
  272. UpdateSources(scene);
  273. }
  274. }
  275. }
  276. /* OBS Callbacks */
  277. void OBSBasic::SceneItemAdded(void *data, calldata_t params)
  278. {
  279. OBSBasic *window = static_cast<OBSBasic*>(data);
  280. obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
  281. QMetaObject::invokeMethod(window, "AddSceneItem",
  282. Q_ARG(OBSSceneItem, OBSSceneItem(item)));
  283. }
  284. void OBSBasic::SceneItemRemoved(void *data, calldata_t params)
  285. {
  286. OBSBasic *window = static_cast<OBSBasic*>(data);
  287. obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
  288. QMetaObject::invokeMethod(window, "RemoveSceneItem",
  289. Q_ARG(OBSSceneItem, OBSSceneItem(item)));
  290. }
  291. void OBSBasic::SourceAdded(void *data, calldata_t params)
  292. {
  293. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  294. obs_source_type type;
  295. obs_source_gettype(source, &type, NULL);
  296. if (type == OBS_SOURCE_TYPE_SCENE)
  297. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  298. "AddScene",
  299. Q_ARG(OBSSource, OBSSource(source)));
  300. }
  301. void OBSBasic::SourceRemoved(void *data, calldata_t params)
  302. {
  303. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  304. obs_source_type type;
  305. obs_source_gettype(source, &type, NULL);
  306. if (type == OBS_SOURCE_TYPE_SCENE)
  307. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  308. "RemoveScene",
  309. Q_ARG(OBSSource, OBSSource(source)));
  310. }
  311. void OBSBasic::ChannelChanged(void *data, calldata_t params)
  312. {
  313. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  314. uint32_t channel = (uint32_t)calldata_int(params, "channel");
  315. if (channel == 0)
  316. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
  317. "UpdateSceneSelection",
  318. Q_ARG(OBSSource, OBSSource(source)));
  319. }
  320. void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
  321. {
  322. OBSBasic *window = static_cast<OBSBasic*>(data);
  323. obs_video_info ovi;
  324. int newCX, newCY;
  325. obs_get_video_info(&ovi);
  326. newCX = int(window->previewScale * float(ovi.base_width));
  327. newCY = int(window->previewScale * float(ovi.base_height));
  328. gs_viewport_push();
  329. gs_projection_push();
  330. gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height),
  331. -100.0f, 100.0f);
  332. gs_setviewport(window->previewX, window->previewY, newCX, newCY);
  333. obs_render_main_view();
  334. gs_projection_pop();
  335. gs_viewport_pop();
  336. UNUSED_PARAMETER(cx);
  337. UNUSED_PARAMETER(cy);
  338. }
  339. /* Main class functions */
  340. bool OBSBasic::ResetVideo()
  341. {
  342. struct obs_video_info ovi;
  343. GetConfigFPS(ovi.fps_num, ovi.fps_den);
  344. ovi.graphics_module = App()->GetRenderModule();
  345. ovi.base_width = (uint32_t)config_get_uint(basicConfig,
  346. "Video", "BaseCX");
  347. ovi.base_height = (uint32_t)config_get_uint(basicConfig,
  348. "Video", "BaseCY");
  349. ovi.output_width = (uint32_t)config_get_uint(basicConfig,
  350. "Video", "OutputCX");
  351. ovi.output_height = (uint32_t)config_get_uint(basicConfig,
  352. "Video", "OutputCY");
  353. //ovi.output_format = VIDEO_FORMAT_I420;
  354. ovi.output_format = VIDEO_FORMAT_NV12;
  355. ovi.adapter = 0;
  356. ovi.gpu_conversion = true;
  357. QTToGSWindow(ui->preview->winId(), ovi.window);
  358. //required to make opengl display stuff on osx(?)
  359. ResizePreview(ovi.base_width, ovi.base_height);
  360. QSize size = GetPixelSize(ui->preview);
  361. ovi.window_width = size.width();
  362. ovi.window_height = size.height();
  363. if (!obs_reset_video(&ovi))
  364. return false;
  365. obs_add_draw_callback(OBSBasic::RenderMain, this);
  366. return true;
  367. }
  368. bool OBSBasic::ResetAudio()
  369. {
  370. struct audio_output_info ai;
  371. ai.name = "Main Audio Track";
  372. ai.format = AUDIO_FORMAT_FLOAT;
  373. ai.samples_per_sec = config_get_uint(basicConfig, "Audio",
  374. "SampleRate");
  375. const char *channelSetupStr = config_get_string(basicConfig,
  376. "Audio", "ChannelSetup");
  377. if (strcmp(channelSetupStr, "Mono") == 0)
  378. ai.speakers = SPEAKERS_MONO;
  379. else
  380. ai.speakers = SPEAKERS_STEREO;
  381. ai.buffer_ms = config_get_uint(basicConfig, "Audio", "BufferingTime");
  382. return obs_reset_audio(&ai);
  383. }
  384. void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceName,
  385. int channel)
  386. {
  387. const char *deviceId = config_get_string(basicConfig, "Audio",
  388. deviceName);
  389. obs_source_t source;
  390. obs_data_t settings;
  391. bool same = false;
  392. source = obs_get_output_source(channel);
  393. if (source) {
  394. settings = obs_source_getsettings(source);
  395. const char *curId = obs_data_getstring(settings, "device_id");
  396. same = (strcmp(curId, deviceId) == 0);
  397. obs_data_release(settings);
  398. obs_source_release(source);
  399. }
  400. if (!same)
  401. obs_set_output_source(channel, nullptr);
  402. if (!same && strcmp(deviceId, "disabled") != 0) {
  403. obs_data_t settings = obs_data_create();
  404. obs_data_setstring(settings, "device_id", deviceId);
  405. source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
  406. sourceId, deviceName, settings);
  407. obs_data_release(settings);
  408. obs_set_output_source(channel, source);
  409. obs_source_release(source);
  410. }
  411. }
  412. void OBSBasic::ResetAudioDevices()
  413. {
  414. ResetAudioDevice(App()->OutputAudioSource(), "DesktopDevice1", 1);
  415. ResetAudioDevice(App()->OutputAudioSource(), "DesktopDevice2", 2);
  416. ResetAudioDevice(App()->InputAudioSource(), "AuxDevice1", 3);
  417. ResetAudioDevice(App()->InputAudioSource(), "AuxDevice2", 4);
  418. ResetAudioDevice(App()->InputAudioSource(), "AuxDevice3", 5);
  419. }
  420. void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
  421. {
  422. QSize targetSize;
  423. /* resize preview panel to fix to the top section of the window */
  424. targetSize = GetPixelSize(ui->preview);
  425. GetScaleAndCenterPos(int(cx), int(cy),
  426. targetSize.width(), targetSize.height(),
  427. previewX, previewY, previewScale);
  428. if (isVisible()) {
  429. if (resizeTimer)
  430. killTimer(resizeTimer);
  431. resizeTimer = startTimer(100);
  432. }
  433. }
  434. void OBSBasic::closeEvent(QCloseEvent *event)
  435. {
  436. QWidget::closeEvent(event);
  437. if (!event->isAccepted())
  438. return;
  439. // remove draw callback in case our drawable surfaces go away before
  440. // the destructor gets called
  441. obs_remove_draw_callback(OBSBasic::RenderMain, this);
  442. }
  443. void OBSBasic::changeEvent(QEvent *event)
  444. {
  445. /* TODO */
  446. UNUSED_PARAMETER(event);
  447. }
  448. void OBSBasic::resizeEvent(QResizeEvent *event)
  449. {
  450. struct obs_video_info ovi;
  451. if (obs_get_video_info(&ovi))
  452. ResizePreview(ovi.base_width, ovi.base_height);
  453. UNUSED_PARAMETER(event);
  454. }
  455. void OBSBasic::timerEvent(QTimerEvent *event)
  456. {
  457. if (event->timerId() == resizeTimer) {
  458. killTimer(resizeTimer);
  459. resizeTimer = 0;
  460. QSize size = GetPixelSize(ui->preview);
  461. obs_resize(size.width(), size.height());
  462. }
  463. }
  464. void OBSBasic::on_action_New_triggered()
  465. {
  466. /* TODO */
  467. }
  468. void OBSBasic::on_action_Open_triggered()
  469. {
  470. /* TODO */
  471. }
  472. void OBSBasic::on_action_Save_triggered()
  473. {
  474. /* TODO */
  475. }
  476. void OBSBasic::on_action_Settings_triggered()
  477. {
  478. OBSBasicSettings settings(this);
  479. settings.exec();
  480. }
  481. void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current,
  482. QListWidgetItem *prev)
  483. {
  484. obs_source_t source = NULL;
  485. if (sceneChanging)
  486. return;
  487. if (current) {
  488. obs_scene_t scene;
  489. scene = current->data(Qt::UserRole).value<OBSScene>();
  490. source = obs_scene_getsource(scene);
  491. }
  492. /* TODO: allow transitions */
  493. obs_set_output_source(0, source);
  494. UNUSED_PARAMETER(prev);
  495. }
  496. void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
  497. {
  498. /* TODO */
  499. UNUSED_PARAMETER(pos);
  500. }
  501. void OBSBasic::on_actionAddScene_triggered()
  502. {
  503. string name;
  504. bool accepted = NameDialog::AskForName(this,
  505. QTStr("MainWindow.AddSceneDlg.Title"),
  506. QTStr("MainWindow.AddSceneDlg.Text"),
  507. name);
  508. if (accepted) {
  509. if (name.empty()) {
  510. QMessageBox::information(this,
  511. QTStr("MainWindow.NoNameEntered"),
  512. QTStr("MainWindow.NoNameEntered"));
  513. on_actionAddScene_triggered();
  514. return;
  515. }
  516. obs_source_t source = obs_get_source_by_name(name.c_str());
  517. if (source) {
  518. QMessageBox::information(this,
  519. QTStr("MainWindow.NameExists.Title"),
  520. QTStr("MainWindow.NameExists.Text"));
  521. obs_source_release(source);
  522. on_actionAddScene_triggered();
  523. return;
  524. }
  525. obs_scene_t scene = obs_scene_create(name.c_str());
  526. source = obs_scene_getsource(scene);
  527. obs_add_source(source);
  528. obs_scene_release(scene);
  529. obs_set_output_source(0, source);
  530. }
  531. }
  532. void OBSBasic::on_actionRemoveScene_triggered()
  533. {
  534. QListWidgetItem *item = ui->scenes->currentItem();
  535. if (!item)
  536. return;
  537. QVariant userData = item->data(Qt::UserRole);
  538. obs_scene_t scene = userData.value<OBSScene>();
  539. obs_source_t source = obs_scene_getsource(scene);
  540. obs_source_remove(source);
  541. }
  542. void OBSBasic::on_actionSceneProperties_triggered()
  543. {
  544. /* TODO */
  545. }
  546. void OBSBasic::on_actionSceneUp_triggered()
  547. {
  548. /* TODO */
  549. }
  550. void OBSBasic::on_actionSceneDown_triggered()
  551. {
  552. /* TODO */
  553. }
  554. void OBSBasic::on_sources_currentItemChanged(QListWidgetItem *current,
  555. QListWidgetItem *prev)
  556. {
  557. /* TODO */
  558. UNUSED_PARAMETER(current);
  559. UNUSED_PARAMETER(prev);
  560. }
  561. void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
  562. {
  563. /* TODO */
  564. UNUSED_PARAMETER(pos);
  565. }
  566. void OBSBasic::AddSource(obs_scene_t scene, const char *id)
  567. {
  568. string name;
  569. bool success = false;
  570. while (!success) {
  571. bool accepted = NameDialog::AskForName(this,
  572. Str("MainWindow.AddSourceDlg.Title"),
  573. Str("MainWindow.AddSourceDlg.Text"),
  574. name);
  575. if (!accepted)
  576. break;
  577. if (name.empty()) {
  578. QMessageBox::information(this,
  579. QTStr("MainWindow.NoNameEntered"),
  580. QTStr("MainWindow.NoNameEntered"));
  581. continue;
  582. }
  583. obs_source_t source = obs_get_source_by_name(name.c_str());
  584. if (!source) {
  585. success = true;
  586. break;
  587. } else {
  588. QMessageBox::information(this,
  589. QTStr("MainWindow.NameExists.Title"),
  590. QTStr("MainWindow.NameExists.Text"));
  591. obs_source_release(source);
  592. }
  593. }
  594. if (success) {
  595. obs_source_t source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
  596. id, name.c_str(), NULL);
  597. sourceSceneRefs[source] = 0;
  598. obs_add_source(source);
  599. obs_scene_add(scene, source);
  600. obs_source_release(source);
  601. }
  602. }
  603. void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
  604. {
  605. OBSScene scene = GetCurrentScene();
  606. const char *type;
  607. bool foundValues = false;
  608. size_t idx = 0;
  609. if (!scene)
  610. return;
  611. QMenu popup;
  612. while (obs_enum_input_types(idx++, &type)) {
  613. const char *name = obs_source_getdisplayname(
  614. OBS_SOURCE_TYPE_INPUT,
  615. type, App()->GetLocale());
  616. QAction *popupItem = new QAction(QT_UTF8(name), this);
  617. popupItem->setData(QT_UTF8(type));
  618. popup.addAction(popupItem);
  619. foundValues = true;
  620. }
  621. if (foundValues) {
  622. QAction *ret = popup.exec(pos);
  623. if (ret)
  624. AddSource(scene, ret->data().toString().toUtf8());
  625. }
  626. }
  627. void OBSBasic::on_actionAddSource_triggered()
  628. {
  629. AddSourcePopupMenu(QCursor::pos());
  630. }
  631. void OBSBasic::on_actionRemoveSource_triggered()
  632. {
  633. OBSSceneItem item = GetCurrentSceneItem();
  634. if (item)
  635. obs_sceneitem_remove(item);
  636. }
  637. void OBSBasic::on_actionSourceProperties_triggered()
  638. {
  639. OBSSceneItem item = GetCurrentSceneItem();
  640. OBSSource source = obs_sceneitem_getsource(item);
  641. if (source) {
  642. delete properties;
  643. properties = new OBSBasicProperties(this, source);
  644. properties->Init();
  645. }
  646. }
  647. void OBSBasic::on_actionSourceUp_triggered()
  648. {
  649. }
  650. void OBSBasic::on_actionSourceDown_triggered()
  651. {
  652. }
  653. void OBSBasic::OutputStop(int errorcode)
  654. {
  655. UNUSED_PARAMETER(errorcode);
  656. ui->streamButton->setText("Start Streaming");
  657. }
  658. void OBSBasic::OutputStart()
  659. {
  660. ui->streamButton->setText("Stop Streaming");
  661. }
  662. static void OBSOutputStart(void *data, calldata_t params)
  663. {
  664. UNUSED_PARAMETER(params);
  665. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data), "OutputStart");
  666. }
  667. static void OBSOutputStop(void *data, calldata_t params)
  668. {
  669. int code = (int)calldata_int(params, "errorcode");
  670. QMetaObject::invokeMethod(static_cast<OBSBasic*>(data), "OutputStop",
  671. Q_ARG(int, code));
  672. }
  673. void OBSBasic::TempFileOutput(const char *path, int vBitrate, int aBitrate)
  674. {
  675. obs_data_t data = obs_data_create();
  676. obs_data_setstring(data, "filename", path);
  677. obs_data_setint(data, "audio_bitrate", aBitrate);
  678. obs_data_setint(data, "video_bitrate", vBitrate);
  679. outputTest = obs_output_create("ffmpeg_output", "test", data);
  680. obs_data_release(data);
  681. }
  682. void OBSBasic::TempStreamOutput(const char *url, const char *key,
  683. int vBitrate, int aBitrate)
  684. {
  685. obs_data_t aac_settings = obs_data_create();
  686. obs_data_t x264_settings = obs_data_create();
  687. obs_data_t output_settings = obs_data_create();
  688. stringstream ss;
  689. ss << "filler=1:crf=0:bitrate=" << vBitrate;
  690. obs_data_setint(aac_settings, "bitrate", aBitrate);
  691. obs_data_setint(x264_settings, "bitrate", vBitrate);
  692. obs_data_setint(x264_settings, "buffer_size", vBitrate);
  693. obs_data_setint(x264_settings, "keyint_sec", 2);
  694. obs_data_setstring(x264_settings, "x264opts", ss.str().c_str());
  695. obs_data_setstring(output_settings, "path", url);
  696. obs_data_setstring(output_settings, "key", key);
  697. aac = obs_audio_encoder_create("ffmpeg_aac", "blabla1",
  698. aac_settings, obs_audio());
  699. x264 = obs_video_encoder_create("obs_x264", "blabla2",
  700. x264_settings, obs_video());
  701. outputTest = obs_output_create("rtmp_output", "test", output_settings);
  702. obs_output_set_video_encoder(outputTest, x264);
  703. obs_output_set_audio_encoder(outputTest, aac);
  704. obs_data_release(aac_settings);
  705. obs_data_release(x264_settings);
  706. obs_data_release(output_settings);
  707. }
  708. /* TODO: lots of temporary code */
  709. void OBSBasic::on_streamButton_clicked()
  710. {
  711. if (obs_output_active(outputTest)) {
  712. obs_output_stop(outputTest);
  713. } else {
  714. const char *url = config_get_string(basicConfig, "OutputTemp",
  715. "URL");
  716. const char *key = config_get_string(basicConfig, "OutputTemp",
  717. "Key");
  718. int vBitrate = config_get_uint(basicConfig, "OutputTemp",
  719. "VBitrate");
  720. int aBitrate = config_get_uint(basicConfig, "OutputTemp",
  721. "ABitrate");
  722. if (!url)
  723. return;
  724. obs_output_destroy(outputTest);
  725. obs_encoder_destroy(aac);
  726. obs_encoder_destroy(x264);
  727. outputTest = nullptr;
  728. aac = nullptr;
  729. x264 = nullptr;
  730. if (strstr(url, "rtmp://") != NULL)
  731. TempStreamOutput(url, key, vBitrate, aBitrate);
  732. else
  733. TempFileOutput(url, vBitrate, aBitrate);
  734. if (!outputTest) {
  735. OutputStop(OBS_OUTPUT_FAIL);
  736. return;
  737. }
  738. signal_handler_connect(obs_output_signalhandler(outputTest),
  739. "start", OBSOutputStart, this);
  740. signal_handler_connect(obs_output_signalhandler(outputTest),
  741. "stop", OBSOutputStop, this);
  742. obs_output_start(outputTest);
  743. }
  744. }
  745. void OBSBasic::on_settingsButton_clicked()
  746. {
  747. OBSBasicSettings settings(this);
  748. settings.exec();
  749. }
  750. void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const
  751. {
  752. const char *val = config_get_string(basicConfig, "Video", "FPSCommon");
  753. if (strcmp(val, "10") == 0) {
  754. num = 10;
  755. den = 1;
  756. } else if (strcmp(val, "20") == 0) {
  757. num = 20;
  758. den = 1;
  759. } else if (strcmp(val, "25") == 0) {
  760. num = 25;
  761. den = 1;
  762. } else if (strcmp(val, "29.97") == 0) {
  763. num = 30000;
  764. den = 1001;
  765. } else if (strcmp(val, "48") == 0) {
  766. num = 48;
  767. den = 1;
  768. } else if (strcmp(val, "59.94") == 0) {
  769. num = 60000;
  770. den = 1001;
  771. } else if (strcmp(val, "60") == 0) {
  772. num = 60;
  773. den = 1;
  774. } else {
  775. num = 30;
  776. den = 1;
  777. }
  778. }
  779. void OBSBasic::GetFPSInteger(uint32_t &num, uint32_t &den) const
  780. {
  781. num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSInt");
  782. den = 1;
  783. }
  784. void OBSBasic::GetFPSFraction(uint32_t &num, uint32_t &den) const
  785. {
  786. num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNum");
  787. den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSDen");
  788. }
  789. void OBSBasic::GetFPSNanoseconds(uint32_t &num, uint32_t &den) const
  790. {
  791. num = 1000000000;
  792. den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNS");
  793. }
  794. void OBSBasic::GetConfigFPS(uint32_t &num, uint32_t &den) const
  795. {
  796. uint32_t type = config_get_uint(basicConfig, "Video", "FPSType");
  797. if (type == 1) //"Integer"
  798. GetFPSInteger(num, den);
  799. else if (type == 2) //"Fraction"
  800. GetFPSFraction(num, den);
  801. else if (false) //"Nanoseconds", currently not implemented
  802. GetFPSNanoseconds(num, den);
  803. else
  804. GetFPSCommon(num, den);
  805. }
  806. config_t OBSBasic::Config() const
  807. {
  808. return basicConfig;
  809. }