window-basic-main.cpp 22.5 KB
Newer Older
1
/******************************************************************************
2
    Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
3
    Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>
4 5 6

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 2 of the License, or
8 9 10
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 13 14 15 16 17 18
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

J
jp9000 已提交
19
#include <obs.hpp>
J
jp9000 已提交
20
#include <QMessageBox>
21
#include <QShowEvent>
J
jp9000 已提交
22
#include <QFileDialog>
23

24 25 26
#include <util/util.hpp>
#include <util/platform.h>

27
#include "obs-app.hpp"
28
#include "platform.hpp"
29
#include "window-basic-settings.hpp"
30
#include "window-namedialog.hpp"
J
jp9000 已提交
31
#include "window-basic-main.hpp"
32
#include "window-basic-properties.hpp"
J
jp9000 已提交
33
#include "qt-wrappers.hpp"
34
#include "display-helpers.hpp"
35

J
jp9000 已提交
36
#include "ui_OBSBasic.h"
37

38
using namespace std;
J
jp9000 已提交
39

J
jp9000 已提交
40 41 42
Q_DECLARE_METATYPE(OBSScene);
Q_DECLARE_METATYPE(OBSSceneItem);

43 44
OBSBasic::OBSBasic(QWidget *parent)
	: OBSMainWindow (parent),
J
jp9000 已提交
45
	  outputTest    (NULL),
46
	  sceneChanging (false),
47
	  resizeTimer   (0),
48
	  properties    (nullptr),
J
jp9000 已提交
49
	  ui            (new Ui::OBSBasic)
50 51 52 53
{
	ui->setupUi(this);
}

54 55 56
static inline bool HasAudioDevices(const char *source_id)
{
	const char *output_id = source_id;
57
	obs_properties_t props = obs_get_source_properties(
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
			OBS_SOURCE_TYPE_INPUT, output_id, App()->GetLocale());
	size_t count = 0;

	if (!props)
		return false;

	obs_property_t devices = obs_properties_get(props, "device_id");
	if (devices)
		count = obs_property_list_item_count(devices);

	obs_properties_destroy(props);

	return count != 0;
}

73 74
bool OBSBasic::InitBasicConfigDefaults()
{
75 76 77
	bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource());
	bool hasInputAudio   = HasAudioDevices(App()->InputAudioSource());

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
	config_set_default_int(basicConfig, "Window", "PosX",  -1);
	config_set_default_int(basicConfig, "Window", "PosY",  -1);
	config_set_default_int(basicConfig, "Window", "SizeX", -1);
	config_set_default_int(basicConfig, "Window", "SizeY", -1);

	vector<MonitorInfo> monitors;
	GetMonitors(monitors);

	if (!monitors.size()) {
		OBSErrorBox(NULL, "There appears to be no monitors.  Er, this "
		                  "technically shouldn't be possible.");
		return false;
	}

	uint32_t cx = monitors[0].cx;
	uint32_t cy = monitors[0].cy;

95 96 97 98 99 100
	/* TODO: temporary */
	config_set_default_string(basicConfig, "OutputTemp", "URL", "");
	config_set_default_string(basicConfig, "OutputTemp", "Key", "");
	config_set_default_uint  (basicConfig, "OutputTemp", "VBitrate", 2500);
	config_set_default_uint  (basicConfig, "OutputTemp", "ABitrate", 128);

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	config_set_default_uint  (basicConfig, "Video", "BaseCX",   cx);
	config_set_default_uint  (basicConfig, "Video", "BaseCY",   cy);

	cx = cx * 10 / 15;
	cy = cy * 10 / 15;
	config_set_default_uint  (basicConfig, "Video", "OutputCX", cx);
	config_set_default_uint  (basicConfig, "Video", "OutputCY", cy);

	config_set_default_uint  (basicConfig, "Video", "FPSType", 0);
	config_set_default_string(basicConfig, "Video", "FPSCommon", "30");
	config_set_default_uint  (basicConfig, "Video", "FPSInt", 30);
	config_set_default_uint  (basicConfig, "Video", "FPSNum", 30);
	config_set_default_uint  (basicConfig, "Video", "FPSDen", 1);

	config_set_default_uint  (basicConfig, "Audio", "SampleRate", 44100);
	config_set_default_string(basicConfig, "Audio", "ChannelSetup",
			"Stereo");
	config_set_default_uint  (basicConfig, "Audio", "BufferingTime", 1000);

J
jp9000 已提交
120
	config_set_default_string(basicConfig, "Audio", "DesktopDevice1",
121
			hasDesktopAudio ? "default" : "disabled");
J
jp9000 已提交
122 123 124
	config_set_default_string(basicConfig, "Audio", "DesktopDevice2",
			"disabled");
	config_set_default_string(basicConfig, "Audio", "AuxDevice1",
125
			hasInputAudio ? "default" : "disabled");
J
jp9000 已提交
126 127 128 129 130
	config_set_default_string(basicConfig, "Audio", "AuxDevice2",
			"disabled");
	config_set_default_string(basicConfig, "Audio", "AuxDevice3",
			"disabled");

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	return true;
}

bool OBSBasic::InitBasicConfig()
{
	BPtr<char> configPath(os_get_config_path("obs-studio/basic/basic.ini"));

	int errorcode = basicConfig.Open(configPath, CONFIG_OPEN_ALWAYS);
	if (errorcode != CONFIG_SUCCESS) {
		OBSErrorBox(NULL, "Failed to open basic.ini: %d", errorcode);
		return false;
	}

	return InitBasicConfigDefaults();
}

147 148 149 150 151 152 153 154
void OBSBasic::OBSInit()
{
	/* make sure it's fully displayed before doing any initialization */
	show();
	App()->processEvents();

	if (!obs_startup())
		throw "Failed to initialize libobs";
155 156
	if (!InitBasicConfig())
		throw "Failed to load basic.ini";
157 158 159
	if (!ResetVideo())
		throw "Failed to initialize video";
	if (!ResetAudio())
160 161
		throw "Failed to initialize audio";

162
	signal_handler_connect(obs_signalhandler(), "source_add",
163
			OBSBasic::SourceAdded, this);
164
	signal_handler_connect(obs_signalhandler(), "source_remove",
165
			OBSBasic::SourceRemoved, this);
166
	signal_handler_connect(obs_signalhandler(), "channel_change",
167 168
			OBSBasic::ChannelChanged, this);

J
jp9000 已提交
169 170
	/* TODO: this is a test, all modules will be searched for and loaded
	 * automatically later */
171
	obs_load_module("test-input");
172
	obs_load_module("obs-ffmpeg");
J
jp9000 已提交
173 174
#ifdef __APPLE__
	obs_load_module("mac-capture");
J
jp9000 已提交
175 176
#elif _WIN32
	obs_load_module("win-wasapi");
J
jp9000 已提交
177
	obs_load_module("win-capture");
178 179 180
#else
	obs_load_module("linux-xshm");
	obs_load_module("linux-pulseaudio");
J
jp9000 已提交
181
#endif
J
jp9000 已提交
182 183

	ResetAudioDevices();
184 185 186 187
}

OBSBasic::~OBSBasic()
{
188 189 190
	if (properties)
		delete properties;

191 192 193 194 195 196 197
	/* free the lists before shutting down to remove the scene/item
	 * references */
	ui->sources->clear();
	ui->scenes->clear();
	obs_shutdown();
}

J
jp9000 已提交
198
OBSScene OBSBasic::GetCurrentScene()
199
{
J
jp9000 已提交
200
	QListWidgetItem *item = ui->scenes->currentItem();
J
jp9000 已提交
201
	return item ? item->data(Qt::UserRole).value<OBSScene>() : nullptr;
202 203
}

J
jp9000 已提交
204
OBSSceneItem OBSBasic::GetCurrentSceneItem()
J
jp9000 已提交
205 206
{
	QListWidgetItem *item = ui->sources->currentItem();
J
jp9000 已提交
207
	return item ? item->data(Qt::UserRole).value<OBSSceneItem>() : nullptr;
J
jp9000 已提交
208 209
}

210 211 212 213 214 215 216 217
void OBSBasic::UpdateSources(OBSScene scene)
{
	ui->sources->clear();

	obs_scene_enum_items(scene,
			[] (obs_scene_t scene, obs_sceneitem_t item, void *p)
			{
				OBSBasic *window = static_cast<OBSBasic*>(p);
218
				window->InsertSceneItem(item);
J
jp9000 已提交
219 220

				UNUSED_PARAMETER(scene);
221 222 223 224
				return true;
			}, this);
}

225 226 227 228 229 230 231 232 233 234 235 236
void OBSBasic::InsertSceneItem(obs_sceneitem_t item)
{
	obs_source_t source = obs_sceneitem_getsource(item);
	const char   *name  = obs_source_getname(source);

	QListWidgetItem *listItem = new QListWidgetItem(QT_UTF8(name));
	listItem->setData(Qt::UserRole,
			QVariant::fromValue(OBSSceneItem(item)));

	ui->sources->insertItem(0, listItem);
}

237 238 239
/* Qt callbacks for invokeMethod */

void OBSBasic::AddScene(OBSSource source)
240 241 242
{
	const char *name  = obs_source_getname(source);
	obs_scene_t scene = obs_scene_fromsource(source);
J
jp9000 已提交
243 244

	QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
J
jp9000 已提交
245
	item->setData(Qt::UserRole, QVariant::fromValue(OBSScene(scene)));
J
jp9000 已提交
246
	ui->scenes->addItem(item);
247 248

	signal_handler_t handler = obs_source_signalhandler(source);
249
	signal_handler_connect(handler, "item_add",
J
jp9000 已提交
250
			OBSBasic::SceneItemAdded, this);
251
	signal_handler_connect(handler, "item_remove",
J
jp9000 已提交
252
			OBSBasic::SceneItemRemoved, this);
253 254
}

255
void OBSBasic::RemoveScene(OBSSource source)
J
jp9000 已提交
256 257 258
{
	const char *name = obs_source_getname(source);

J
jp9000 已提交
259 260 261
	QListWidgetItem *sel = ui->scenes->currentItem();
	QList<QListWidgetItem*> items = ui->scenes->findItems(QT_UTF8(name),
			Qt::MatchExactly);
J
jp9000 已提交
262

J
jp9000 已提交
263 264 265 266
	if (sel != nullptr) {
		if (items.contains(sel))
			ui->sources->clear();
		delete sel;
J
jp9000 已提交
267
	}
268 269
}

270
void OBSBasic::AddSceneItem(OBSSceneItem item)
271
{
J
jp9000 已提交
272
	obs_scene_t  scene  = obs_sceneitem_getscene(item);
273
	obs_source_t source = obs_sceneitem_getsource(item);
J
jp9000 已提交
274

275 276
	if (GetCurrentScene() == scene)
		InsertSceneItem(item);
J
jp9000 已提交
277 278

	sourceSceneRefs[source] = sourceSceneRefs[source] + 1;
279 280
}

281
void OBSBasic::RemoveSceneItem(OBSSceneItem item)
282
{
J
jp9000 已提交
283
	obs_scene_t scene = obs_sceneitem_getscene(item);
284

J
jp9000 已提交
285
	if (GetCurrentScene() == scene) {
B
BtbN 已提交
286
		for (int i = 0; i < ui->sources->count(); i++) {
J
jp9000 已提交
287 288
			QListWidgetItem *listItem = ui->sources->item(i);
			QVariant userData = listItem->data(Qt::UserRole);
J
jp9000 已提交
289

J
jp9000 已提交
290
			if (userData.value<OBSSceneItem>() == item) {
J
jp9000 已提交
291
				delete listItem;
J
jp9000 已提交
292 293
				break;
			}
294 295
		}
	}
J
jp9000 已提交
296 297 298 299 300 301 302 303

	obs_source_t source = obs_sceneitem_getsource(item);

	int scenes = sourceSceneRefs[source] - 1;
	if (scenes == 0) {
		obs_source_remove(source);
		sourceSceneRefs.erase(source);
	}
304 305
}

306
void OBSBasic::UpdateSceneSelection(OBSSource source)
307 308 309 310 311
{
	if (source) {
		obs_source_type type;
		obs_source_gettype(source, &type, NULL);

J
jp9000 已提交
312
		if (type != OBS_SOURCE_TYPE_SCENE)
J
jp9000 已提交
313
			return;
314

J
jp9000 已提交
315 316 317 318 319 320
		obs_scene_t scene = obs_scene_fromsource(source);
		const char *name = obs_source_getname(source);

		QList<QListWidgetItem*> items =
			ui->scenes->findItems(QT_UTF8(name), Qt::MatchExactly);

321 322 323 324 325
		if (items.count()) {
			sceneChanging = true;
			ui->scenes->setCurrentItem(items.first());
			sceneChanging = false;

J
jp9000 已提交
326
			UpdateSources(scene);
327
		}
J
jp9000 已提交
328
	}
329 330 331 332 333 334 335 336 337
}

/* OBS Callbacks */

void OBSBasic::SceneItemAdded(void *data, calldata_t params)
{
	OBSBasic *window = static_cast<OBSBasic*>(data);

	obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
J
jp9000 已提交
338

339 340
	QMetaObject::invokeMethod(window, "AddSceneItem",
			Q_ARG(OBSSceneItem, OBSSceneItem(item)));
J
jp9000 已提交
341 342
}

343
void OBSBasic::SceneItemRemoved(void *data, calldata_t params)
344
{
345
	OBSBasic *window = static_cast<OBSBasic*>(data);
346

347 348
	obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");

349 350
	QMetaObject::invokeMethod(window, "RemoveSceneItem",
			Q_ARG(OBSSceneItem, OBSSceneItem(item)));
351 352 353 354 355
}

void OBSBasic::SourceAdded(void *data, calldata_t params)
{
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
356 357 358 359

	obs_source_type type;
	obs_source_gettype(source, &type, NULL);

J
jp9000 已提交
360
	if (type == OBS_SOURCE_TYPE_SCENE)
361 362 363
		QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
				"AddScene",
				Q_ARG(OBSSource, OBSSource(source)));
364 365
}

J
jp9000 已提交
366
void OBSBasic::SourceRemoved(void *data, calldata_t params)
367
{
368
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
369

J
jp9000 已提交
370 371 372
	obs_source_type type;
	obs_source_gettype(source, &type, NULL);

J
jp9000 已提交
373
	if (type == OBS_SOURCE_TYPE_SCENE)
374 375 376
		QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
				"RemoveScene",
				Q_ARG(OBSSource, OBSSource(source)));
377 378
}

379 380 381
void OBSBasic::ChannelChanged(void *data, calldata_t params)
{
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
382
	uint32_t channel = (uint32_t)calldata_int(params, "channel");
383 384

	if (channel == 0)
385 386 387
		QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
				"UpdateSceneSelection",
				Q_ARG(OBSSource, OBSSource(source)));
388 389
}

390 391
void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
{
J
jp9000 已提交
392 393 394 395
	OBSBasic *window = static_cast<OBSBasic*>(data);
	gs_matrix_push();
	gs_matrix_scale3f(window->previewScale, window->previewScale, 1.0f);
	gs_matrix_translate3f(-window->previewX, -window->previewY, 0.0f);
J
jp9000 已提交
396
	obs_render_main_view();
J
jp9000 已提交
397
	gs_matrix_pop();
J
jp9000 已提交
398 399 400

	UNUSED_PARAMETER(cx);
	UNUSED_PARAMETER(cy);
401 402
}

403 404
/* Main class functions */

405
bool OBSBasic::ResetVideo()
J
jp9000 已提交
406 407
{
	struct obs_video_info ovi;
J
jp9000 已提交
408

409
	GetConfigFPS(ovi.fps_num, ovi.fps_den);
J
jp9000 已提交
410 411

	ovi.graphics_module = App()->GetRenderModule();
412
	ovi.base_width     = (uint32_t)config_get_uint(basicConfig,
J
jp9000 已提交
413
			"Video", "BaseCX");
414
	ovi.base_height    = (uint32_t)config_get_uint(basicConfig,
J
jp9000 已提交
415
			"Video", "BaseCY");
416
	ovi.output_width   = (uint32_t)config_get_uint(basicConfig,
J
jp9000 已提交
417
			"Video", "OutputCX");
418
	ovi.output_height  = (uint32_t)config_get_uint(basicConfig,
J
jp9000 已提交
419
			"Video", "OutputCY");
420 421
	//ovi.output_format  = VIDEO_FORMAT_I420;
	ovi.output_format  = VIDEO_FORMAT_NV12;
J
jp9000 已提交
422 423
	ovi.adapter        = 0;
	ovi.gpu_conversion = true;
424

J
jp9000 已提交
425
	QTToGSWindow(ui->preview->winId(), ovi.window);
J
jp9000 已提交
426 427

	//required to make opengl display stuff on osx(?)
J
jp9000 已提交
428
	ResizePreview(ovi.base_width, ovi.base_height);
J
jp9000 已提交
429

J
jp9000 已提交
430 431 432
	QSize size = ui->preview->size();
	ovi.window_width  = size.width();
	ovi.window_height = size.height();
J
jp9000 已提交
433

434 435 436 437 438
	if (!obs_reset_video(&ovi))
		return false;

	obs_add_draw_callback(OBSBasic::RenderMain, this);
	return true;
J
jp9000 已提交
439
}
J
jp9000 已提交
440

441
bool OBSBasic::ResetAudio()
J
jp9000 已提交
442
{
J
jp9000 已提交
443
	struct audio_output_info ai;
444 445 446
	ai.name = "Main Audio Track";
	ai.format = AUDIO_FORMAT_FLOAT;

447
	ai.samples_per_sec = config_get_uint(basicConfig, "Audio",
448 449
			"SampleRate");

450
	const char *channelSetupStr = config_get_string(basicConfig,
451 452 453 454 455 456 457
			"Audio", "ChannelSetup");

	if (strcmp(channelSetupStr, "Mono") == 0)
		ai.speakers = SPEAKERS_MONO;
	else
		ai.speakers = SPEAKERS_STEREO;

458
	ai.buffer_ms = config_get_uint(basicConfig, "Audio", "BufferingTime");
J
jp9000 已提交
459 460

	return obs_reset_audio(&ai);
J
jp9000 已提交
461 462
}

J
jp9000 已提交
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceName,
		int channel)
{
	const char *deviceId = config_get_string(basicConfig, "Audio",
			deviceName);
	obs_source_t source;
	obs_data_t settings;
	bool same = false;

	source = obs_get_output_source(channel);
	if (source) {
		settings = obs_source_getsettings(source);
		const char *curId = obs_data_getstring(settings, "device_id");

		same = (strcmp(curId, deviceId) == 0);

		obs_data_release(settings);
		obs_source_release(source);
	}

	if (!same)
		obs_set_output_source(channel, nullptr);

	if (!same && strcmp(deviceId, "disabled") != 0) {
		obs_data_t settings = obs_data_create();
		obs_data_setstring(settings, "device_id", deviceId);
		source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
				sourceId, deviceName, settings);
		obs_data_release(settings);

		obs_set_output_source(channel, source);
		obs_source_release(source);
	}
}

void OBSBasic::ResetAudioDevices()
J
jp9000 已提交
499
{
J
jp9000 已提交
500 501 502 503 504
	ResetAudioDevice(App()->OutputAudioSource(), "DesktopDevice1", 1);
	ResetAudioDevice(App()->OutputAudioSource(), "DesktopDevice2", 2);
	ResetAudioDevice(App()->InputAudioSource(),  "AuxDevice1", 3);
	ResetAudioDevice(App()->InputAudioSource(),  "AuxDevice2", 4);
	ResetAudioDevice(App()->InputAudioSource(),  "AuxDevice3", 5);
J
jp9000 已提交
505 506
}

J
jp9000 已提交
507
void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
508
{
509
	QSize  targetSize;
J
jp9000 已提交
510

511
	/* resize preview panel to fix to the top section of the window */
512 513 514 515
	targetSize = ui->preview->size();
	GetScaleAndCenterPos(int(cx), int(cy),
			targetSize.width(), targetSize.height(),
			previewX, previewY, previewScale);
J
jp9000 已提交
516

J
jp9000 已提交
517 518 519 520 521
	if (isVisible()) {
		if (resizeTimer)
			killTimer(resizeTimer);
		resizeTimer = startTimer(100);
	}
J
jp9000 已提交
522 523
}

J
jp9000 已提交
524
void OBSBasic::closeEvent(QCloseEvent *event)
J
jp9000 已提交
525
{
J
jp9000 已提交
526 527
	/* TODO */
	UNUSED_PARAMETER(event);
528 529
}

J
jp9000 已提交
530
void OBSBasic::changeEvent(QEvent *event)
531
{
J
jp9000 已提交
532 533
	/* TODO */
	UNUSED_PARAMETER(event);
534 535
}

J
jp9000 已提交
536
void OBSBasic::resizeEvent(QResizeEvent *event)
537
{
J
jp9000 已提交
538 539 540 541
	struct obs_video_info ovi;

	if (obs_get_video_info(&ovi))
		ResizePreview(ovi.base_width, ovi.base_height);
J
jp9000 已提交
542 543

	UNUSED_PARAMETER(event);
544 545
}

J
jp9000 已提交
546 547 548 549 550 551 552 553 554 555 556
void OBSBasic::timerEvent(QTimerEvent *event)
{
	if (event->timerId() == resizeTimer) {
		killTimer(resizeTimer);
		resizeTimer = 0;

		QSize size = ui->preview->size();
		obs_resize(size.width(), size.height());
	}
}

J
jp9000 已提交
557
void OBSBasic::on_action_New_triggered()
558
{
J
jp9000 已提交
559
	/* TODO */
560 561
}

J
jp9000 已提交
562
void OBSBasic::on_action_Open_triggered()
563
{
J
jp9000 已提交
564
	/* TODO */
565 566
}

J
jp9000 已提交
567
void OBSBasic::on_action_Save_triggered()
568
{
J
jp9000 已提交
569
	/* TODO */
570 571
}

572 573
void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current,
		QListWidgetItem *prev)
574 575
{
	obs_source_t source = NULL;
J
jp9000 已提交
576

577 578 579 580
	if (sceneChanging)
		return;

	if (current) {
J
jp9000 已提交
581 582
		obs_scene_t scene;

583
		scene = current->data(Qt::UserRole).value<OBSScene>();
584 585 586
		source = obs_scene_getsource(scene);
	}

587
	/* TODO: allow transitions */
588
	obs_set_output_source(0, source);
589 590

	UNUSED_PARAMETER(prev);
591 592
}

J
jp9000 已提交
593
void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
594
{
J
jp9000 已提交
595 596
	/* TODO */
	UNUSED_PARAMETER(pos);
597 598
}

J
jp9000 已提交
599
void OBSBasic::on_actionAddScene_triggered()
600
{
601
	string name;
J
jp9000 已提交
602 603 604
	bool accepted = NameDialog::AskForName(this,
			QTStr("MainWindow.AddSceneDlg.Title"),
			QTStr("MainWindow.AddSceneDlg.Text"),
605 606
			name);

J
jp9000 已提交
607
	if (accepted) {
J
jp9000 已提交
608 609 610 611 612 613 614 615
		if (name.empty()) {
			QMessageBox::information(this,
					QTStr("MainWindow.NoNameEntered"),
					QTStr("MainWindow.NoNameEntered"));
			on_actionAddScene_triggered();
			return;
		}

616 617
		obs_source_t source = obs_get_source_by_name(name.c_str());
		if (source) {
J
jp9000 已提交
618 619 620
			QMessageBox::information(this,
					QTStr("MainWindow.NameExists.Title"),
					QTStr("MainWindow.NameExists.Text"));
621 622

			obs_source_release(source);
J
jp9000 已提交
623
			on_actionAddScene_triggered();
624 625 626
			return;
		}

627
		obs_scene_t scene = obs_scene_create(name.c_str());
628 629
		source = obs_scene_getsource(scene);
		obs_add_source(source);
630
		obs_scene_release(scene);
631 632

		obs_set_output_source(0, source);
633
	}
634 635
}

J
jp9000 已提交
636
void OBSBasic::on_actionRemoveScene_triggered()
637
{
J
jp9000 已提交
638 639
	QListWidgetItem *item = ui->scenes->currentItem();
	if (!item)
J
jp9000 已提交
640 641
		return;

J
jp9000 已提交
642
	QVariant userData = item->data(Qt::UserRole);
J
jp9000 已提交
643
	obs_scene_t scene = userData.value<OBSScene>();
J
jp9000 已提交
644 645
	obs_source_t source = obs_scene_getsource(scene);
	obs_source_remove(source);
646 647
}

J
jp9000 已提交
648
void OBSBasic::on_actionSceneProperties_triggered()
649
{
J
jp9000 已提交
650
	/* TODO */
651 652
}

J
jp9000 已提交
653
void OBSBasic::on_actionSceneUp_triggered()
654
{
J
jp9000 已提交
655
	/* TODO */
656 657
}

J
jp9000 已提交
658
void OBSBasic::on_actionSceneDown_triggered()
659
{
J
jp9000 已提交
660
	/* TODO */
661 662
}

663 664
void OBSBasic::on_sources_currentItemChanged(QListWidgetItem *current,
		QListWidgetItem *prev)
665
{
J
jp9000 已提交
666
	/* TODO */
667 668
	UNUSED_PARAMETER(current);
	UNUSED_PARAMETER(prev);
669 670
}

J
jp9000 已提交
671
void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
672
{
J
jp9000 已提交
673 674
	/* TODO */
	UNUSED_PARAMETER(pos);
675 676
}

677 678 679 680 681 682
void OBSBasic::AddSource(obs_scene_t scene, const char *id)
{
	string name;

	bool success = false;
	while (!success) {
J
jp9000 已提交
683
		bool accepted = NameDialog::AskForName(this,
684 685 686 687
				Str("MainWindow.AddSourceDlg.Title"),
				Str("MainWindow.AddSourceDlg.Text"),
				name);

J
jp9000 已提交
688
		if (!accepted)
689 690
			break;

J
jp9000 已提交
691 692 693 694 695 696 697
		if (name.empty()) {
			QMessageBox::information(this,
					QTStr("MainWindow.NoNameEntered"),
					QTStr("MainWindow.NoNameEntered"));
			continue;
		}

J
jp9000 已提交
698
		obs_source_t source = obs_get_source_by_name(name.c_str());
699 700
		if (!source) {
			success = true;
J
jp9000 已提交
701
			break;
702
		} else {
J
jp9000 已提交
703 704 705
			QMessageBox::information(this,
					QTStr("MainWindow.NameExists.Title"),
					QTStr("MainWindow.NameExists.Text"));
706 707 708 709 710
			obs_source_release(source);
		}
	}

	if (success) {
J
jp9000 已提交
711 712
		obs_source_t source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
				id, name.c_str(), NULL);
J
jp9000 已提交
713 714 715

		sourceSceneRefs[source] = 0;

716
		obs_add_source(source);
J
jp9000 已提交
717
		obs_scene_add(scene, source);
718 719 720 721
		obs_source_release(source);
	}
}

J
jp9000 已提交
722
void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
723
{
724
	OBSScene scene = GetCurrentScene();
725
	const char *type;
J
jp9000 已提交
726 727
	bool foundValues = false;
	size_t idx = 0;
728

J
jp9000 已提交
729
	if (!scene)
730 731
		return;

J
jp9000 已提交
732 733
	QMenu popup;
	while (obs_enum_input_types(idx++, &type)) {
J
jp9000 已提交
734 735
		const char *name = obs_source_getdisplayname(
				OBS_SOURCE_TYPE_INPUT,
J
jp9000 已提交
736
				type, App()->GetLocale());
737

J
jp9000 已提交
738 739 740
		QAction *popupItem = new QAction(QT_UTF8(name), this);
		popupItem->setData(QT_UTF8(type));
		popup.addAction(popupItem);
741

J
jp9000 已提交
742
		foundValues = true;
743 744
	}

J
jp9000 已提交
745 746 747 748
	if (foundValues) {
		QAction *ret = popup.exec(pos);
		if (ret)
			AddSource(scene, ret->data().toString().toUtf8());
749 750 751
	}
}

J
jp9000 已提交
752
void OBSBasic::on_actionAddSource_triggered()
753
{
J
jp9000 已提交
754
	AddSourcePopupMenu(QCursor::pos());
755 756
}

J
jp9000 已提交
757
void OBSBasic::on_actionRemoveSource_triggered()
758
{
759
	OBSSceneItem item = GetCurrentSceneItem();
J
jp9000 已提交
760 761
	if (item)
		obs_sceneitem_remove(item);
762 763
}

J
jp9000 已提交
764
void OBSBasic::on_actionSourceProperties_triggered()
765
{
766 767 768 769 770 771 772 773
	OBSSceneItem item = GetCurrentSceneItem();
	OBSSource source = obs_sceneitem_getsource(item);

	if (source) {
		delete properties;
		properties = new OBSBasicProperties(this, source);
		properties->Init();
	}
774 775
}

J
jp9000 已提交
776
void OBSBasic::on_actionSourceUp_triggered()
777 778
{
}
J
jp9000 已提交
779

J
jp9000 已提交
780
void OBSBasic::on_actionSourceDown_triggered()
781 782 783
{
}

784
void OBSBasic::OutputStart(int errorcode)
J
jp9000 已提交
785
{
786
	if (errorcode != OBS_OUTPUT_SUCCESS) {
J
jp9000 已提交
787 788 789
		obs_output_destroy(outputTest);
		outputTest = NULL;
	} else {
790 791
		ui->streamButton->setText("Stop Streaming");
	}
J
jp9000 已提交
792

793 794 795
	ui->streamButton->setEnabled(true);
}

796
static void OBSOutputStart(void *data, calldata_t params)
797
{
798
	int code = calldata_bool(params, "errorcode");
799 800

	QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
801
			"OutputStart", Q_ARG(int, code));
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
}

/* TODO: lots of temporary code */
void OBSBasic::on_streamButton_clicked()
{
	if (outputTest) {
		obs_output_destroy(outputTest);
		outputTest = NULL;
		ui->streamButton->setText("Start Streaming");
	} else {
		const char *url = config_get_string(basicConfig, "OutputTemp",
				"URL");
		const char *key = config_get_string(basicConfig, "OutputTemp",
				"Key");
		int vBitrate = config_get_uint(basicConfig, "OutputTemp",
				"VBitrate");
		int aBitrate = config_get_uint(basicConfig, "OutputTemp",
				"ABitrate");

821
		if (!url)
822 823
			return;

824
		string fullURL = url;
825 826
		if (key && *key)
			fullURL = fullURL + "/" + key;
J
jp9000 已提交
827 828

		obs_data_t data = obs_data_create();
829 830 831
		obs_data_setstring(data, "filename", fullURL.c_str());
		obs_data_setint(data, "audio_bitrate", aBitrate);
		obs_data_setint(data, "video_bitrate", vBitrate);
J
jp9000 已提交
832 833 834 835

		outputTest = obs_output_create("ffmpeg_output", "test", data);
		obs_data_release(data);

836
		if (!outputTest)
J
jp9000 已提交
837 838
			return;

839
		signal_handler_connect(obs_output_signalhandler(outputTest),
840
				"start", OBSOutputStart, this);
841 842 843

		obs_output_start(outputTest);
		ui->streamButton->setEnabled(false);
J
jp9000 已提交
844 845 846
	}
}

J
jp9000 已提交
847
void OBSBasic::on_settingsButton_clicked()
J
jp9000 已提交
848
{
849 850
	OBSBasicSettings settings(this);
	settings.exec();
J
jp9000 已提交
851
}
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919

void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const
{
	const char *val = config_get_string(basicConfig, "Video", "FPSCommon");

	if (strcmp(val, "10") == 0) {
		num = 10;
		den = 1;
	} else if (strcmp(val, "20") == 0) {
		num = 20;
		den = 1;
	} else if (strcmp(val, "25") == 0) {
		num = 25;
		den = 1;
	} else if (strcmp(val, "29.97") == 0) {
		num = 30000;
		den = 1001;
	} else if (strcmp(val, "48") == 0) {
		num = 48;
		den = 1;
	} else if (strcmp(val, "59.94") == 0) {
		num = 60000;
		den = 1001;
	} else if (strcmp(val, "60") == 0) {
		num = 60;
		den = 1;
	} else {
		num = 30;
		den = 1;
	}
}

void OBSBasic::GetFPSInteger(uint32_t &num, uint32_t &den) const
{
	num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSInt");
	den = 1;
}

void OBSBasic::GetFPSFraction(uint32_t &num, uint32_t &den) const
{
	num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNum");
	den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSDen");
}

void OBSBasic::GetFPSNanoseconds(uint32_t &num, uint32_t &den) const
{
	num = 1000000000;
	den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNS");
}

void OBSBasic::GetConfigFPS(uint32_t &num, uint32_t &den) const
{
	uint32_t type = config_get_uint(basicConfig, "Video", "FPSType");

	if (type == 1) //"Integer"
		GetFPSInteger(num, den);
	else if (type == 2) //"Fraction"
		GetFPSFraction(num, den);
	else if (false) //"Nanoseconds", currently not implemented
		GetFPSNanoseconds(num, den);
	else
		GetFPSCommon(num, den);
}

config_t OBSBasic::Config() const
{
	return basicConfig;
}
920 921 922 923 924

void OBSBasic::UnloadProperties()
{
	properties = nullptr;
}