window-basic-main.cpp 14.8 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
#include "obs-app.hpp"
25
#include "window-basic-settings.hpp"
26
#include "window-namedialog.hpp"
J
jp9000 已提交
27 28
#include "window-basic-main.hpp"
#include "qt-wrappers.hpp"
29

J
jp9000 已提交
30
#include "ui_OBSBasic.h"
31

32
using namespace std;
J
jp9000 已提交
33

J
jp9000 已提交
34 35 36
Q_DECLARE_METATYPE(OBSScene);
Q_DECLARE_METATYPE(OBSSceneItem);

37 38
OBSBasic::OBSBasic(QWidget *parent)
	: OBSMainWindow (parent),
J
jp9000 已提交
39
	  outputTest    (NULL),
40
	  sceneChanging (false),
J
jp9000 已提交
41
	  ui            (new Ui::OBSBasic)
42 43 44 45 46 47 48 49 50 51 52 53
{
	ui->setupUi(this);
}

void OBSBasic::OBSInit()
{
	/* make sure it's fully displayed before doing any initialization */
	show();
	App()->processEvents();

	if (!obs_startup())
		throw "Failed to initialize libobs";
54 55 56
	if (!ResetVideo())
		throw "Failed to initialize video";
	if (!ResetAudio())
57 58
		throw "Failed to initialize audio";

59
	signal_handler_connect(obs_signalhandler(), "source_add",
60
			OBSBasic::SourceAdded, this);
61
	signal_handler_connect(obs_signalhandler(), "source_remove",
62
			OBSBasic::SourceRemoved, this);
63
	signal_handler_connect(obs_signalhandler(), "channel_change",
64 65
			OBSBasic::ChannelChanged, this);

J
jp9000 已提交
66 67
	/* TODO: this is a test, all modules will be searched for and loaded
	 * automatically later */
68
	obs_load_module("test-input");
69
	obs_load_module("obs-ffmpeg");
J
jp9000 已提交
70 71
#ifdef __APPLE__
	obs_load_module("mac-capture");
J
jp9000 已提交
72 73
#elif _WIN32
	obs_load_module("win-wasapi");
J
jp9000 已提交
74
	obs_load_module("win-capture");
J
jp9000 已提交
75
#endif
76

77
	/* HACK: fixes a qt bug with native widgets with native repaint */
78 79 80 81 82 83 84 85 86 87 88 89
	ui->previewContainer->repaint();
}

OBSBasic::~OBSBasic()
{
	/* free the lists before shutting down to remove the scene/item
	 * references */
	ui->sources->clear();
	ui->scenes->clear();
	obs_shutdown();
}

J
jp9000 已提交
90
OBSScene OBSBasic::GetCurrentScene()
91
{
J
jp9000 已提交
92
	QListWidgetItem *item = ui->scenes->currentItem();
J
jp9000 已提交
93
	return item ? item->data(Qt::UserRole).value<OBSScene>() : nullptr;
94 95
}

J
jp9000 已提交
96
OBSSceneItem OBSBasic::GetCurrentSceneItem()
J
jp9000 已提交
97 98
{
	QListWidgetItem *item = ui->sources->currentItem();
J
jp9000 已提交
99
	return item ? item->data(Qt::UserRole).value<OBSSceneItem>() : nullptr;
J
jp9000 已提交
100 101
}

102 103 104 105 106 107 108 109
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);
110
				window->InsertSceneItem(item);
J
jp9000 已提交
111 112

				UNUSED_PARAMETER(scene);
113 114 115 116
				return true;
			}, this);
}

117 118 119 120 121 122 123 124 125 126 127 128
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);
}

129 130 131
/* Qt callbacks for invokeMethod */

void OBSBasic::AddScene(OBSSource source)
132 133 134
{
	const char *name  = obs_source_getname(source);
	obs_scene_t scene = obs_scene_fromsource(source);
J
jp9000 已提交
135 136

	QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
J
jp9000 已提交
137
	item->setData(Qt::UserRole, QVariant::fromValue(OBSScene(scene)));
J
jp9000 已提交
138
	ui->scenes->addItem(item);
139 140

	signal_handler_t handler = obs_source_signalhandler(source);
141
	signal_handler_connect(handler, "item_add",
J
jp9000 已提交
142
			OBSBasic::SceneItemAdded, this);
143
	signal_handler_connect(handler, "item_remove",
J
jp9000 已提交
144
			OBSBasic::SceneItemRemoved, this);
145 146
}

147
void OBSBasic::RemoveScene(OBSSource source)
J
jp9000 已提交
148 149 150
{
	const char *name = obs_source_getname(source);

J
jp9000 已提交
151 152 153
	QListWidgetItem *sel = ui->scenes->currentItem();
	QList<QListWidgetItem*> items = ui->scenes->findItems(QT_UTF8(name),
			Qt::MatchExactly);
J
jp9000 已提交
154

J
jp9000 已提交
155 156 157 158
	if (sel != nullptr) {
		if (items.contains(sel))
			ui->sources->clear();
		delete sel;
J
jp9000 已提交
159
	}
160 161
}

162
void OBSBasic::AddSceneItem(OBSSceneItem item)
163
{
J
jp9000 已提交
164
	obs_scene_t  scene  = obs_sceneitem_getscene(item);
165
	obs_source_t source = obs_sceneitem_getsource(item);
J
jp9000 已提交
166

167 168
	if (GetCurrentScene() == scene)
		InsertSceneItem(item);
J
jp9000 已提交
169 170

	sourceSceneRefs[source] = sourceSceneRefs[source] + 1;
171 172
}

173
void OBSBasic::RemoveSceneItem(OBSSceneItem item)
174
{
J
jp9000 已提交
175
	obs_scene_t scene = obs_sceneitem_getscene(item);
176

J
jp9000 已提交
177
	if (GetCurrentScene() == scene) {
B
BtbN 已提交
178
		for (int i = 0; i < ui->sources->count(); i++) {
J
jp9000 已提交
179 180
			QListWidgetItem *listItem = ui->sources->item(i);
			QVariant userData = listItem->data(Qt::UserRole);
J
jp9000 已提交
181

J
jp9000 已提交
182
			if (userData.value<OBSSceneItem>() == item) {
J
jp9000 已提交
183
				delete listItem;
J
jp9000 已提交
184 185
				break;
			}
186 187
		}
	}
J
jp9000 已提交
188 189 190 191 192 193 194 195

	obs_source_t source = obs_sceneitem_getsource(item);

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

198
void OBSBasic::UpdateSceneSelection(OBSSource source)
199 200 201 202 203
{
	if (source) {
		obs_source_type type;
		obs_source_gettype(source, &type, NULL);

J
jp9000 已提交
204
		if (type != OBS_SOURCE_TYPE_SCENE)
J
jp9000 已提交
205
			return;
206

J
jp9000 已提交
207 208 209 210 211 212
		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);

213 214 215 216 217
		if (items.count()) {
			sceneChanging = true;
			ui->scenes->setCurrentItem(items.first());
			sceneChanging = false;

J
jp9000 已提交
218
			UpdateSources(scene);
219
		}
J
jp9000 已提交
220
	}
221 222 223 224 225 226 227 228 229
}

/* 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 已提交
230

231 232
	QMetaObject::invokeMethod(window, "AddSceneItem",
			Q_ARG(OBSSceneItem, OBSSceneItem(item)));
J
jp9000 已提交
233 234
}

235
void OBSBasic::SceneItemRemoved(void *data, calldata_t params)
236
{
237
	OBSBasic *window = static_cast<OBSBasic*>(data);
238

239 240
	obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");

241 242
	QMetaObject::invokeMethod(window, "RemoveSceneItem",
			Q_ARG(OBSSceneItem, OBSSceneItem(item)));
243 244 245 246 247
}

void OBSBasic::SourceAdded(void *data, calldata_t params)
{
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
248 249 250 251

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

J
jp9000 已提交
252
	if (type == OBS_SOURCE_TYPE_SCENE)
253 254 255
		QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
				"AddScene",
				Q_ARG(OBSSource, OBSSource(source)));
256 257
}

J
jp9000 已提交
258
void OBSBasic::SourceRemoved(void *data, calldata_t params)
259
{
260
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
261

J
jp9000 已提交
262 263 264
	obs_source_type type;
	obs_source_gettype(source, &type, NULL);

J
jp9000 已提交
265
	if (type == OBS_SOURCE_TYPE_SCENE)
266 267 268
		QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
				"RemoveScene",
				Q_ARG(OBSSource, OBSSource(source)));
269 270
}

271 272 273
void OBSBasic::ChannelChanged(void *data, calldata_t params)
{
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
274
	uint32_t channel = (uint32_t)calldata_int(params, "channel");
275 276

	if (channel == 0)
277 278 279
		QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
				"UpdateSceneSelection",
				Q_ARG(OBSSource, OBSSource(source)));
280 281
}

282 283
void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
{
J
jp9000 已提交
284
	obs_render_main_view();
J
jp9000 已提交
285 286 287 288

	UNUSED_PARAMETER(data);
	UNUSED_PARAMETER(cx);
	UNUSED_PARAMETER(cy);
289 290
}

291 292
/* Main class functions */

293
bool OBSBasic::ResetVideo()
J
jp9000 已提交
294 295
{
	struct obs_video_info ovi;
J
jp9000 已提交
296 297 298 299

	App()->GetConfigFPS(ovi.fps_num, ovi.fps_den);

	ovi.graphics_module = App()->GetRenderModule();
J
jp9000 已提交
300
	ovi.base_width     = (uint32_t)config_get_uint(GetGlobalConfig(),
J
jp9000 已提交
301
			"Video", "BaseCX");
J
jp9000 已提交
302
	ovi.base_height    = (uint32_t)config_get_uint(GetGlobalConfig(),
J
jp9000 已提交
303
			"Video", "BaseCY");
J
jp9000 已提交
304
	ovi.output_width   = (uint32_t)config_get_uint(GetGlobalConfig(),
J
jp9000 已提交
305
			"Video", "OutputCX");
J
jp9000 已提交
306
	ovi.output_height  = (uint32_t)config_get_uint(GetGlobalConfig(),
J
jp9000 已提交
307
			"Video", "OutputCY");
J
jp9000 已提交
308 309 310
	ovi.output_format  = VIDEO_FORMAT_I420;
	ovi.adapter        = 0;
	ovi.gpu_conversion = true;
311

J
jp9000 已提交
312
	QTToGSWindow(ui->preview, ovi.window);
J
jp9000 已提交
313 314

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

J
jp9000 已提交
317 318 319
	QSize size = ui->preview->size();
	ovi.window_width  = size.width();
	ovi.window_height = size.height();
J
jp9000 已提交
320

321 322 323 324 325
	if (!obs_reset_video(&ovi))
		return false;

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

328
bool OBSBasic::ResetAudio()
J
jp9000 已提交
329
{
J
jp9000 已提交
330
	struct audio_output_info ai;
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
	ai.name = "Main Audio Track";
	ai.format = AUDIO_FORMAT_FLOAT;

	ai.samples_per_sec = config_get_uint(GetGlobalConfig(), "Audio",
			"SampleRate");

	const char *channelSetupStr = config_get_string(GetGlobalConfig(),
			"Audio", "ChannelSetup");

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

	ai.buffer_ms = config_get_uint(GetGlobalConfig(), "Audio",
			"BufferingTime");
J
jp9000 已提交
347 348

	return obs_reset_audio(&ai);
J
jp9000 已提交
349 350
}

J
jp9000 已提交
351
void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
352
{
J
jp9000 已提交
353
	double targetAspect, baseAspect;
354 355
	QSize  targetSize;
	int x, y;
J
jp9000 已提交
356

357
	/* resize preview panel to fix to the top section of the window */
J
jp9000 已提交
358 359 360
	targetSize   = ui->previewContainer->size();
	targetAspect = double(targetSize.width()) / double(targetSize.height());
	baseAspect   = double(cx) / double(cy);
361

362 363 364 365 366 367 368 369 370 371
	if (targetAspect > baseAspect) {
		cx = targetSize.height() * baseAspect;
		cy = targetSize.height();
	} else {
		cx = targetSize.width();
		cy = targetSize.width() / baseAspect;
	}

	x = targetSize.width() /2 - cx/2;
	y = targetSize.height()/2 - cy/2;
372

373
	ui->preview->setGeometry(x, y, cx, cy);
J
jp9000 已提交
374

375 376
	if (isVisible())
		obs_resize(cx, cy);
J
jp9000 已提交
377 378
}

J
jp9000 已提交
379
void OBSBasic::closeEvent(QCloseEvent *event)
J
jp9000 已提交
380
{
J
jp9000 已提交
381 382
	/* TODO */
	UNUSED_PARAMETER(event);
383 384
}

J
jp9000 已提交
385
void OBSBasic::changeEvent(QEvent *event)
386
{
J
jp9000 已提交
387 388
	/* TODO */
	UNUSED_PARAMETER(event);
389 390
}

J
jp9000 已提交
391
void OBSBasic::resizeEvent(QResizeEvent *event)
392
{
J
jp9000 已提交
393 394 395 396
	struct obs_video_info ovi;

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

	UNUSED_PARAMETER(event);
399 400
}

J
jp9000 已提交
401
void OBSBasic::on_action_New_triggered()
402
{
J
jp9000 已提交
403
	/* TODO */
404 405
}

J
jp9000 已提交
406
void OBSBasic::on_action_Open_triggered()
407
{
J
jp9000 已提交
408
	/* TODO */
409 410
}

J
jp9000 已提交
411
void OBSBasic::on_action_Save_triggered()
412
{
J
jp9000 已提交
413
	/* TODO */
414 415
}

416 417
void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current,
		QListWidgetItem *prev)
418 419
{
	obs_source_t source = NULL;
J
jp9000 已提交
420

421 422 423 424
	if (sceneChanging)
		return;

	if (current) {
J
jp9000 已提交
425 426
		obs_scene_t scene;

427
		scene = current->data(Qt::UserRole).value<OBSScene>();
428 429 430
		source = obs_scene_getsource(scene);
	}

431
	/* TODO: allow transitions */
432
	obs_set_output_source(0, source);
433 434

	UNUSED_PARAMETER(prev);
435 436
}

J
jp9000 已提交
437
void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
438
{
J
jp9000 已提交
439 440
	/* TODO */
	UNUSED_PARAMETER(pos);
441 442
}

J
jp9000 已提交
443
void OBSBasic::on_actionAddScene_triggered()
444
{
445
	string name;
J
jp9000 已提交
446 447 448
	bool accepted = NameDialog::AskForName(this,
			QTStr("MainWindow.AddSceneDlg.Title"),
			QTStr("MainWindow.AddSceneDlg.Text"),
449 450
			name);

J
jp9000 已提交
451
	if (accepted) {
452 453
		obs_source_t source = obs_get_source_by_name(name.c_str());
		if (source) {
J
jp9000 已提交
454 455 456
			QMessageBox::information(this,
					QTStr("MainWindow.NameExists.Title"),
					QTStr("MainWindow.NameExists.Text"));
457 458

			obs_source_release(source);
J
jp9000 已提交
459
			on_actionAddScene_triggered();
460 461 462
			return;
		}

463
		obs_scene_t scene = obs_scene_create(name.c_str());
464 465
		source = obs_scene_getsource(scene);
		obs_add_source(source);
466
		obs_scene_release(scene);
467 468

		obs_set_output_source(0, source);
469
	}
470 471
}

J
jp9000 已提交
472
void OBSBasic::on_actionRemoveScene_triggered()
473
{
J
jp9000 已提交
474 475
	QListWidgetItem *item = ui->scenes->currentItem();
	if (!item)
J
jp9000 已提交
476 477
		return;

J
jp9000 已提交
478
	QVariant userData = item->data(Qt::UserRole);
J
jp9000 已提交
479
	obs_scene_t scene = userData.value<OBSScene>();
J
jp9000 已提交
480 481
	obs_source_t source = obs_scene_getsource(scene);
	obs_source_remove(source);
482 483
}

J
jp9000 已提交
484
void OBSBasic::on_actionSceneProperties_triggered()
485
{
J
jp9000 已提交
486
	/* TODO */
487 488
}

J
jp9000 已提交
489
void OBSBasic::on_actionSceneUp_triggered()
490
{
J
jp9000 已提交
491
	/* TODO */
492 493
}

J
jp9000 已提交
494
void OBSBasic::on_actionSceneDown_triggered()
495
{
J
jp9000 已提交
496
	/* TODO */
497 498
}

499 500
void OBSBasic::on_sources_currentItemChanged(QListWidgetItem *current,
		QListWidgetItem *prev)
501
{
J
jp9000 已提交
502
	/* TODO */
503 504
	UNUSED_PARAMETER(current);
	UNUSED_PARAMETER(prev);
505 506
}

J
jp9000 已提交
507
void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
508
{
J
jp9000 已提交
509 510
	/* TODO */
	UNUSED_PARAMETER(pos);
511 512
}

513 514 515 516 517 518
void OBSBasic::AddSource(obs_scene_t scene, const char *id)
{
	string name;

	bool success = false;
	while (!success) {
J
jp9000 已提交
519
		bool accepted = NameDialog::AskForName(this,
520 521 522 523
				Str("MainWindow.AddSourceDlg.Title"),
				Str("MainWindow.AddSourceDlg.Text"),
				name);

J
jp9000 已提交
524
		if (!accepted)
525 526
			break;

J
jp9000 已提交
527
		obs_source_t source = obs_get_source_by_name(name.c_str());
528 529 530
		if (!source) {
			success = true;
		} else {
J
jp9000 已提交
531 532 533
			QMessageBox::information(this,
					QTStr("MainWindow.NameExists.Title"),
					QTStr("MainWindow.NameExists.Text"));
534 535 536 537 538
			obs_source_release(source);
		}
	}

	if (success) {
J
jp9000 已提交
539 540
		obs_source_t source = obs_source_create(OBS_SOURCE_TYPE_INPUT,
				id, name.c_str(), NULL);
J
jp9000 已提交
541 542 543

		sourceSceneRefs[source] = 0;

544
		obs_add_source(source);
J
jp9000 已提交
545
		obs_scene_add(scene, source);
546 547 548 549
		obs_source_release(source);
	}
}

J
jp9000 已提交
550
void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
551
{
552
	OBSScene scene = GetCurrentScene();
553
	const char *type;
J
jp9000 已提交
554 555
	bool foundValues = false;
	size_t idx = 0;
556

J
jp9000 已提交
557
	if (!scene)
558 559
		return;

J
jp9000 已提交
560 561
	QMenu popup;
	while (obs_enum_input_types(idx++, &type)) {
J
jp9000 已提交
562 563
		const char *name = obs_source_getdisplayname(
				OBS_SOURCE_TYPE_INPUT,
J
jp9000 已提交
564
				type, App()->GetLocale());
565

J
jp9000 已提交
566 567 568
		QAction *popupItem = new QAction(QT_UTF8(name), this);
		popupItem->setData(QT_UTF8(type));
		popup.addAction(popupItem);
569

J
jp9000 已提交
570
		foundValues = true;
571 572
	}

J
jp9000 已提交
573 574 575 576
	if (foundValues) {
		QAction *ret = popup.exec(pos);
		if (ret)
			AddSource(scene, ret->data().toString().toUtf8());
577 578 579
	}
}

J
jp9000 已提交
580
void OBSBasic::on_actionAddSource_triggered()
581
{
J
jp9000 已提交
582
	AddSourcePopupMenu(QCursor::pos());
583 584
}

J
jp9000 已提交
585
void OBSBasic::on_actionRemoveSource_triggered()
586
{
587
	OBSSceneItem item = GetCurrentSceneItem();
J
jp9000 已提交
588 589
	if (item)
		obs_sceneitem_remove(item);
590 591
}

J
jp9000 已提交
592
void OBSBasic::on_actionSourceProperties_triggered()
593 594 595
{
}

J
jp9000 已提交
596
void OBSBasic::on_actionSourceUp_triggered()
597 598
{
}
J
jp9000 已提交
599

J
jp9000 已提交
600
void OBSBasic::on_actionSourceDown_triggered()
601 602 603
{
}

J
jp9000 已提交
604 605 606 607 608 609 610 611 612
void OBSBasic::on_recordButton_clicked()
{
	if (outputTest) {
		obs_output_destroy(outputTest);
		outputTest = NULL;
		ui->recordButton->setText("Start Recording");
	} else {
		QString path = QFileDialog::getSaveFileName(this,
				"Please enter a file name", QString(),
613
				"MP4 Files (*.mp4)");
J
jp9000 已提交
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633

		if (path.isNull() || path.isEmpty())
			return;

		obs_data_t data = obs_data_create();
		obs_data_setstring(data, "filename", QT_TO_UTF8(path));

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

		if (!obs_output_start(outputTest)) {
			obs_output_destroy(outputTest);
			outputTest = NULL;
			return;
		}

		ui->recordButton->setText("Stop Recording");
	}
}

J
jp9000 已提交
634
void OBSBasic::on_settingsButton_clicked()
J
jp9000 已提交
635
{
636 637
	OBSBasicSettings settings(this);
	settings.exec();
J
jp9000 已提交
638
}