浏览代码

UI: Refactor output handling

To accommodate multiple types of outputs, there has to be some level of
abstraction.  The BasicOutputHandler structure will give us a way that
we can switch between different output configurations.
jp9000 10 年之前
父节点
当前提交
07007bcc6e
共有 5 个文件被更改,包括 310 次插入195 次删除
  1. 2 0
      obs/CMakeLists.txt
  2. 251 0
      obs/window-basic-main-outputs.cpp
  3. 27 0
      obs/window-basic-main-outputs.hpp
  4. 25 187
      obs/window-basic-main.cpp
  5. 5 8
      obs/window-basic-main.hpp

+ 2 - 0
obs/CMakeLists.txt

@@ -93,6 +93,7 @@ set(obs_SOURCES
 	window-basic-settings.cpp
 	window-basic-settings.cpp
 	window-basic-interaction.cpp
 	window-basic-interaction.cpp
 	window-basic-properties.cpp
 	window-basic-properties.cpp
+	window-basic-main-outputs.cpp
 	window-basic-source-select.cpp
 	window-basic-source-select.cpp
 	window-license-agreement.cpp
 	window-license-agreement.cpp
 	window-basic-status-bar.cpp
 	window-basic-status-bar.cpp
@@ -118,6 +119,7 @@ set(obs_HEADERS
 	window-basic-settings.hpp
 	window-basic-settings.hpp
 	window-basic-interaction.hpp
 	window-basic-interaction.hpp
 	window-basic-properties.hpp
 	window-basic-properties.hpp
+	window-basic-main-outputs.hpp
 	window-basic-source-select.hpp
 	window-basic-source-select.hpp
 	window-license-agreement.hpp
 	window-license-agreement.hpp
 	window-basic-status-bar.hpp
 	window-basic-status-bar.hpp

+ 251 - 0
obs/window-basic-main-outputs.cpp

@@ -0,0 +1,251 @@
+#include <string>
+#include <QMessageBox>
+#include "window-basic-main.hpp"
+#include "window-basic-main-outputs.hpp"
+
+using namespace std;
+
+static void OBSStartStreaming(void *data, calldata_t *params)
+{
+	BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
+	QMetaObject::invokeMethod(output->main, "StreamingStart");
+
+	UNUSED_PARAMETER(params);
+}
+
+static void OBSStopStreaming(void *data, calldata_t *params)
+{
+	BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
+	int code = (int)calldata_int(params, "code");
+
+	QMetaObject::invokeMethod(output->main,
+			"StreamingStop", Q_ARG(int, code));
+	output->activeRefs--;
+}
+
+static void OBSStartRecording(void *data, calldata_t *params)
+{
+	BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
+
+	QMetaObject::invokeMethod(output->main, "RecordingStart");
+
+	UNUSED_PARAMETER(params);
+}
+
+static void OBSStopRecording(void *data, calldata_t *params)
+{
+	BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
+
+	QMetaObject::invokeMethod(output->main, "RecordingStop");
+	output->activeRefs--;
+
+	UNUSED_PARAMETER(params);
+}
+
+/* ------------------------------------------------------------------------ */
+
+struct SimpleOutput : BasicOutputHandler {
+	OBSEncoder             aac;
+	OBSEncoder             h264;
+
+	SimpleOutput(OBSBasic *main_);
+
+	virtual void Update() override;
+
+	void SetupOutputs();
+
+	virtual bool StartStreaming(obs_service_t *service) override;
+	virtual bool StartRecording() override;
+	virtual void StopStreaming() override;
+	virtual void StopRecording() override;
+	virtual bool StreamingActive() const override;
+	virtual bool RecordingActive() const override;
+};
+
+SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_)
+{
+	streamOutput = obs_output_create("rtmp_output", "simple_stream",
+			nullptr);
+	if (!streamOutput)
+		throw "Failed to create stream output (simple output)";
+
+	fileOutput = obs_output_create("flv_output", "simple_file_output",
+			nullptr);
+	if (!fileOutput)
+		throw "Failed to create recording output (simple output)";
+
+	h264 = obs_video_encoder_create("obs_x264", "simple_h264", nullptr);
+	if (!h264)
+		throw "Failed to create h264 encoder (simple output)";
+
+	aac = obs_audio_encoder_create("libfdk_aac", "simple_aac", nullptr, 0);
+	if (!aac)
+		aac = obs_audio_encoder_create("ffmpeg_aac", "simple_aac",
+				nullptr, 0);
+	if (!aac)
+		throw "Failed to create audio encoder (simple output)";
+
+	signal_handler_connect(obs_output_get_signal_handler(streamOutput),
+			"start", OBSStartStreaming, this);
+	signal_handler_connect(obs_output_get_signal_handler(streamOutput),
+			"stop", OBSStopStreaming, this);
+
+	signal_handler_connect(obs_output_get_signal_handler(fileOutput),
+			"start", OBSStartRecording, this);
+	signal_handler_connect(obs_output_get_signal_handler(fileOutput),
+			"stop", OBSStopRecording, this);
+}
+
+void SimpleOutput::Update()
+{
+	obs_data_t *h264Settings = obs_data_create();
+	obs_data_t *aacSettings  = obs_data_create();
+
+	int videoBitrate = config_get_uint(main->Config(), "SimpleOutput",
+			"VBitrate");
+	int videoBufsize = config_get_uint(main->Config(), "SimpleOutput",
+			"Bufsize");
+	int audioBitrate = config_get_uint(main->Config(), "SimpleOutput",
+			"ABitrate");
+	bool advanced = config_get_bool(main->Config(), "SimpleOutput",
+			"UseAdvanced");
+	bool useCBR = config_get_bool(main->Config(), "SimpleOutput",
+			"UseCBR");
+	bool useBufsize = config_get_bool(main->Config(), "SimpleOutput",
+			"UseBufsize");
+	const char *preset = config_get_string(main->Config(),
+			"SimpleOutput", "Preset");
+	const char *custom = config_get_string(main->Config(),
+			"SimpleOutput", "x264Settings");
+
+	obs_data_set_int(h264Settings, "bitrate", videoBitrate);
+	obs_data_set_bool(h264Settings, "use_bufsize", useBufsize);
+	obs_data_set_int(h264Settings, "buffer_size", videoBufsize);
+
+	if (advanced) {
+		obs_data_set_string(h264Settings, "preset", preset);
+		obs_data_set_string(h264Settings, "x264opts", custom);
+		obs_data_set_bool(h264Settings, "cbr", useCBR);
+	} else {
+		obs_data_set_bool(h264Settings, "cbr", true);
+	}
+
+	obs_data_set_int(aacSettings, "bitrate", audioBitrate);
+
+	obs_encoder_update(h264, h264Settings);
+	obs_encoder_update(aac,  aacSettings);
+
+	obs_data_release(h264Settings);
+	obs_data_release(aacSettings);
+}
+
+inline void SimpleOutput::SetupOutputs()
+{
+	SimpleOutput::Update();
+	obs_encoder_set_video(h264, obs_get_video());
+	obs_encoder_set_audio(aac,  obs_get_audio());
+}
+
+bool SimpleOutput::StartStreaming(obs_service_t *service)
+{
+	if (!Active())
+		SetupOutputs();
+
+	obs_output_set_video_encoder(streamOutput, h264);
+	obs_output_set_audio_encoder(streamOutput, aac, 0);
+	obs_output_set_service(streamOutput, service);
+
+	bool reconnect = config_get_bool(main->Config(), "SimpleOutput",
+			"Reconnect");
+	int retryDelay = config_get_uint(main->Config(), "SimpleOutput",
+			"RetryDelay");
+	int maxRetries = config_get_uint(main->Config(), "SimpleOutput",
+			"MaxRetries");
+	if (!reconnect)
+		maxRetries = 0;
+
+	obs_output_set_reconnect_settings(streamOutput, maxRetries,
+			retryDelay);
+
+	if (obs_output_start(streamOutput)) {
+		activeRefs++;
+		return true;
+	}
+
+	return false;
+}
+
+bool SimpleOutput::StartRecording()
+{
+	if (!Active())
+		SetupOutputs();
+
+	const char *path = config_get_string(main->Config(),
+			"SimpleOutput", "FilePath");
+
+	os_dir_t *dir = path ? os_opendir(path) : nullptr;
+
+	if (!dir) {
+		QMessageBox::information(main,
+				QTStr("Output.BadPath.Title"),
+				QTStr("Output.BadPath.Text"));
+		return false;
+	}
+
+	os_closedir(dir);
+
+	string strPath;
+	strPath += path;
+
+	char lastChar = strPath.back();
+	if (lastChar != '/' && lastChar != '\\')
+		strPath += "/";
+
+	strPath += GenerateTimeDateFilename("flv");
+
+	SetupOutputs();
+
+	obs_output_set_video_encoder(fileOutput, h264);
+	obs_output_set_audio_encoder(fileOutput, aac, 0);
+
+	obs_data_t *settings = obs_data_create();
+	obs_data_set_string(settings, "path", strPath.c_str());
+
+	obs_output_update(fileOutput, settings);
+
+	obs_data_release(settings);
+
+	if (obs_output_start(fileOutput)) {
+		activeRefs++;
+		return true;
+	}
+
+	return false;
+}
+
+void SimpleOutput::StopStreaming()
+{
+	obs_output_stop(streamOutput);
+}
+
+void SimpleOutput::StopRecording()
+{
+	obs_output_stop(fileOutput);
+}
+
+bool SimpleOutput::StreamingActive() const
+{
+	return obs_output_active(streamOutput);
+}
+
+bool SimpleOutput::RecordingActive() const
+{
+	return obs_output_active(fileOutput);
+}
+
+/* ------------------------------------------------------------------------ */
+
+BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main)
+{
+	return new SimpleOutput(main);
+}

+ 27 - 0
obs/window-basic-main-outputs.hpp

@@ -0,0 +1,27 @@
+#pragma once
+
+class OBSBasic;
+
+struct BasicOutputHandler {
+	OBSOutput              fileOutput;
+	OBSOutput              streamOutput;
+	int                    activeRefs = 0;
+	OBSBasic               *main;
+
+	inline BasicOutputHandler(OBSBasic *main_) : main(main_) {}
+
+	virtual ~BasicOutputHandler() {};
+
+	virtual bool StartStreaming(obs_service_t *service) = 0;
+	virtual bool StartRecording() = 0;
+	virtual void StopStreaming() = 0;
+	virtual void StopRecording() = 0;
+	virtual bool StreamingActive() const = 0;
+	virtual bool RecordingActive() const = 0;
+
+	virtual void Update() = 0;
+
+	inline bool Active() const {return !!activeRefs;}
+};
+
+BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main);

+ 25 - 187
obs/window-basic-main.cpp

@@ -36,6 +36,7 @@
 #include "window-namedialog.hpp"
 #include "window-namedialog.hpp"
 #include "window-basic-source-select.hpp"
 #include "window-basic-source-select.hpp"
 #include "window-basic-main.hpp"
 #include "window-basic-main.hpp"
+#include "window-basic-main-outputs.hpp"
 #include "window-basic-properties.hpp"
 #include "window-basic-properties.hpp"
 #include "window-log-reply.hpp"
 #include "window-log-reply.hpp"
 #include "window-remux.hpp"
 #include "window-remux.hpp"
@@ -294,36 +295,6 @@ static inline bool HasAudioDevices(const char *source_id)
 	return count != 0;
 	return count != 0;
 }
 }
 
 
-static void OBSStartStreaming(void *data, calldata_t *params)
-{
-	UNUSED_PARAMETER(params);
-	QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
-			"StreamingStart");
-}
-
-static void OBSStopStreaming(void *data, calldata_t *params)
-{
-	int code = (int)calldata_int(params, "code");
-	QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
-			"StreamingStop", Q_ARG(int, code));
-}
-
-static void OBSStartRecording(void *data, calldata_t *params)
-{
-	UNUSED_PARAMETER(params);
-
-	QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
-			"RecordingStart");
-}
-
-static void OBSStopRecording(void *data, calldata_t *params)
-{
-	UNUSED_PARAMETER(params);
-
-	QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
-			"RecordingStop");
-}
-
 #define SERVICE_PATH "obs-studio/basic/service.json"
 #define SERVICE_PATH "obs-studio/basic/service.json"
 
 
 void OBSBasic::SaveService()
 void OBSBasic::SaveService()
@@ -380,49 +351,6 @@ bool OBSBasic::LoadService()
 	return !!service;
 	return !!service;
 }
 }
 
 
-bool OBSBasic::InitOutputs()
-{
-	fileOutput = obs_output_create("flv_output", "default_file_output",
-			nullptr);
-	if (!fileOutput)
-		return false;
-
-	streamOutput = obs_output_create("rtmp_output", "default_stream",
-			nullptr);
-	if (!streamOutput)
-		return false;
-
-	signal_handler_connect(obs_output_get_signal_handler(streamOutput),
-			"start", OBSStartStreaming, this);
-	signal_handler_connect(obs_output_get_signal_handler(streamOutput),
-			"stop", OBSStopStreaming, this);
-
-	signal_handler_connect(obs_output_get_signal_handler(fileOutput),
-			"start", OBSStartRecording, this);
-	signal_handler_connect(obs_output_get_signal_handler(fileOutput),
-			"stop", OBSStopRecording, this);
-
-	return true;
-}
-
-bool OBSBasic::InitEncoders()
-{
-	x264 = obs_video_encoder_create("obs_x264", "default_h264", nullptr);
-	if (!x264)
-		return false;
-
-	aac = obs_audio_encoder_create("libfdk_aac", "default_aac", nullptr, 0);
-
-	if (!aac)
-		aac = obs_audio_encoder_create("ffmpeg_aac", "default_aac",
-				nullptr, 0);
-
-	if (!aac)
-		return false;
-
-	return true;
-}
-
 bool OBSBasic::InitService()
 bool OBSBasic::InitService()
 {
 {
 	if (LoadService())
 	if (LoadService())
@@ -458,6 +386,8 @@ bool OBSBasic::InitBasicConfigDefaults()
 	uint32_t cy = monitors[0].cy;
 	uint32_t cy = monitors[0].cy;
 
 
 	/* TODO: temporary */
 	/* TODO: temporary */
+	config_set_default_string(basicConfig, "Output", "Type", "Simple");
+
 	config_set_default_string(basicConfig, "SimpleOutput", "FilePath",
 	config_set_default_string(basicConfig, "SimpleOutput", "FilePath",
 			GetDefaultVideoSavePath().c_str());
 			GetDefaultVideoSavePath().c_str());
 	config_set_default_uint  (basicConfig, "SimpleOutput", "VBitrate",
 	config_set_default_uint  (basicConfig, "SimpleOutput", "VBitrate",
@@ -569,6 +499,16 @@ void OBSBasic::InitPrimitives()
 	obs_leave_graphics();
 	obs_leave_graphics();
 }
 }
 
 
+void OBSBasic::ResetOutputs()
+{
+	if (!outputHandler || !outputHandler->Active()) {
+		outputHandler.reset();
+		outputHandler.reset(CreateSimpleOutputHandler(this));
+	} else {
+		outputHandler->Update();
+	}
+}
+
 void OBSBasic::OBSInit()
 void OBSBasic::OBSInit()
 {
 {
 	char savePath[512];
 	char savePath[512];
@@ -609,10 +549,8 @@ void OBSBasic::OBSInit()
 	AddExtraModulePaths();
 	AddExtraModulePaths();
 	obs_load_all_modules();
 	obs_load_all_modules();
 
 
-	if (!InitOutputs())
-		throw "Failed to initialize outputs";
-	if (!InitEncoders())
-		throw "Failed to initialize encoders";
+	ResetOutputs();
+
 	if (!InitService())
 	if (!InitService())
 		throw "Failed to initialize service";
 		throw "Failed to initialize service";
 
 
@@ -642,6 +580,8 @@ OBSBasic::~OBSBasic()
 	delete cpuUsageTimer;
 	delete cpuUsageTimer;
 	os_cpu_usage_info_destroy(cpuUsageInfo);
 	os_cpu_usage_info_destroy(cpuUsageInfo);
 
 
+	outputHandler.reset();
+
 	if (interaction)
 	if (interaction)
 		delete interaction;
 		delete interaction;
 
 
@@ -2181,7 +2121,7 @@ void OBSBasic::StreamingStart()
 {
 {
 	ui->streamButton->setText(QTStr("Basic.Main.StopStreaming"));
 	ui->streamButton->setText(QTStr("Basic.Main.StopStreaming"));
 	ui->streamButton->setEnabled(true);
 	ui->streamButton->setEnabled(true);
-	ui->statusbar->StreamStarted(streamOutput);
+	ui->statusbar->StreamStarted(outputHandler->streamOutput);
 }
 }
 
 
 void OBSBasic::StreamingStop(int code)
 void OBSBasic::StreamingStop(int code)
@@ -2212,7 +2152,6 @@ void OBSBasic::StreamingStop(int code)
 		errorMessage = Str("Output.ConnectFail.Disconnected");
 		errorMessage = Str("Output.ConnectFail.Disconnected");
 	}
 	}
 
 
-	activeRefs--;
 	ui->statusbar->StreamStopped();
 	ui->statusbar->StreamStopped();
 
 
 	ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
 	ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
@@ -2226,89 +2165,25 @@ void OBSBasic::StreamingStop(int code)
 
 
 void OBSBasic::RecordingStart()
 void OBSBasic::RecordingStart()
 {
 {
-	ui->statusbar->RecordingStarted(fileOutput);
+	ui->statusbar->RecordingStarted(outputHandler->fileOutput);
 }
 }
 
 
 void OBSBasic::RecordingStop()
 void OBSBasic::RecordingStop()
 {
 {
 	ui->statusbar->RecordingStopped();
 	ui->statusbar->RecordingStopped();
-	activeRefs--;
 	ui->recordButton->setText(QTStr("Basic.Main.StartRecording"));
 	ui->recordButton->setText(QTStr("Basic.Main.StartRecording"));
 }
 }
 
 
-void OBSBasic::SetupEncoders()
-{
-	if (activeRefs == 0) {
-		obs_data_t *x264Settings = obs_data_create();
-		obs_data_t *aacSettings  = obs_data_create();
-
-		int videoBitrate = config_get_uint(basicConfig, "SimpleOutput",
-				"VBitrate");
-		int audioBitrate = config_get_uint(basicConfig, "SimpleOutput",
-				"ABitrate");
-		bool advanced = config_get_bool(basicConfig, "SimpleOutput",
-				"UseAdvanced");
-		bool useCBR = config_get_bool(basicConfig, "SimpleOutput",
-				"UseCBR");
-		const char *preset = config_get_string(basicConfig,
-				"SimpleOutput", "Preset");
-		const char *custom = config_get_string(basicConfig,
-				"SimpleOutput", "x264Settings");
-
-		obs_data_set_int(x264Settings, "bitrate", videoBitrate);
-		obs_data_set_int(x264Settings, "buffer_size", videoBitrate);
-
-		if (advanced) {
-			obs_data_set_string(x264Settings, "preset", preset);
-			obs_data_set_string(x264Settings, "x264opts", custom);
-			obs_data_set_bool(x264Settings, "cbr", useCBR);
-		} else {
-			obs_data_set_bool(x264Settings, "cbr", true);
-		}
-
-		obs_data_set_int(aacSettings, "bitrate", audioBitrate);
-
-		obs_encoder_update(x264, x264Settings);
-		obs_encoder_update(aac,  aacSettings);
-
-		obs_data_release(x264Settings);
-		obs_data_release(aacSettings);
-
-		obs_encoder_set_video(x264, obs_get_video());
-		obs_encoder_set_audio(aac,  obs_get_audio());
-	}
-}
-
 void OBSBasic::on_streamButton_clicked()
 void OBSBasic::on_streamButton_clicked()
 {
 {
 	SaveProject();
 	SaveProject();
 
 
-	if (obs_output_active(streamOutput)) {
-		obs_output_stop(streamOutput);
+	if (outputHandler->StreamingActive()) {
+		outputHandler->StopStreaming();
 	} else {
 	} else {
-
 		SaveService();
 		SaveService();
-		SetupEncoders();
-
-		obs_output_set_video_encoder(streamOutput, x264);
-		obs_output_set_audio_encoder(streamOutput, aac, 0);
-		obs_output_set_service(streamOutput, service);
-
-		bool reconnect = config_get_bool(basicConfig, "SimpleOutput",
-				"Reconnect");
-		int retryDelay = config_get_uint(basicConfig, "SimpleOutput",
-				"RetryDelay");
-		int maxRetries = config_get_uint(basicConfig, "SimpleOutput",
-				"MaxRetries");
-		if (!reconnect)
-			maxRetries = 0;
-
-		obs_output_set_reconnect_settings(streamOutput, maxRetries,
-				retryDelay);
-
-		if (obs_output_start(streamOutput)) {
-			activeRefs++;
 
 
+		if (outputHandler->StartStreaming(service)) {
 			ui->streamButton->setEnabled(false);
 			ui->streamButton->setEnabled(false);
 			ui->streamButton->setText(
 			ui->streamButton->setText(
 					QTStr("Basic.Main.Connecting"));
 					QTStr("Basic.Main.Connecting"));
@@ -2320,48 +2195,11 @@ void OBSBasic::on_recordButton_clicked()
 {
 {
 	SaveProject();
 	SaveProject();
 
 
-	if (obs_output_active(fileOutput)) {
-		obs_output_stop(fileOutput);
+	if (outputHandler->RecordingActive()) {
+		outputHandler->StopRecording();
 	} else {
 	} else {
 
 
-		const char *path = config_get_string(basicConfig,
-				"SimpleOutput", "FilePath");
-
-		os_dir_t *dir = path ? os_opendir(path) : nullptr;
-
-		if (!dir) {
-			QMessageBox::information(this,
-					QTStr("Output.BadPath.Title"),
-					QTStr("Output.BadPath.Text"));
-			return;
-		}
-
-		os_closedir(dir);
-
-		string strPath;
-		strPath += path;
-
-		char lastChar = strPath.back();
-		if (lastChar != '/' && lastChar != '\\')
-			strPath += "/";
-
-		strPath += GenerateTimeDateFilename("flv");
-
-		SetupEncoders();
-
-		obs_output_set_video_encoder(fileOutput, x264);
-		obs_output_set_audio_encoder(fileOutput, aac, 0);
-
-		obs_data_t *settings = obs_data_create();
-		obs_data_set_string(settings, "path", strPath.c_str());
-
-		obs_output_update(fileOutput, settings);
-
-		obs_data_release(settings);
-
-		if (obs_output_start(fileOutput)) {
-			activeRefs++;
-
+		if (outputHandler->StartRecording()) {
 			ui->recordButton->setText(
 			ui->recordButton->setText(
 					QTStr("Basic.Main.StopRecording"));
 					QTStr("Basic.Main.StopRecording"));
 		}
 		}

+ 5 - 8
obs/window-basic-main.hpp

@@ -47,6 +47,8 @@ class QNetworkReply;
 #define AUX_AUDIO_2     Str("AuxAudioDevice2")
 #define AUX_AUDIO_2     Str("AuxAudioDevice2")
 #define AUX_AUDIO_3     Str("AuxAudioDevice3")
 #define AUX_AUDIO_3     Str("AuxAudioDevice3")
 
 
+struct BasicOutputHandler;
+
 class OBSBasic : public OBSMainWindow {
 class OBSBasic : public OBSMainWindow {
 	Q_OBJECT
 	Q_OBJECT
 
 
@@ -69,11 +71,8 @@ private:
 	QPointer<QTimer>    cpuUsageTimer;
 	QPointer<QTimer>    cpuUsageTimer;
 	os_cpu_usage_info_t *cpuUsageInfo = nullptr;
 	os_cpu_usage_info_t *cpuUsageInfo = nullptr;
 
 
-	obs_output_t  *fileOutput = nullptr;
-	obs_output_t  *streamOutput = nullptr;
 	obs_service_t *service = nullptr;
 	obs_service_t *service = nullptr;
-	obs_encoder_t *aac = nullptr;
-	obs_encoder_t *x264 = nullptr;
+	std::unique_ptr<BasicOutputHandler> outputHandler;
 
 
 	gs_vertbuffer_t *box = nullptr;
 	gs_vertbuffer_t *box = nullptr;
 	gs_vertbuffer_t *circle = nullptr;
 	gs_vertbuffer_t *circle = nullptr;
@@ -87,8 +86,6 @@ private:
 
 
 	ConfigFile    basicConfig;
 	ConfigFile    basicConfig;
 
 
-	int           activeRefs = 0;
-
 	void          DrawBackdrop(float cx, float cy);
 	void          DrawBackdrop(float cx, float cy);
 
 
 	void          SetupEncoders();
 	void          SetupEncoders();
@@ -105,8 +102,6 @@ private:
 	void          SaveService();
 	void          SaveService();
 	bool          LoadService();
 	bool          LoadService();
 
 
-	bool          InitOutputs();
-	bool          InitEncoders();
 	bool          InitService();
 	bool          InitService();
 
 
 	bool          InitBasicConfigDefaults();
 	bool          InitBasicConfigDefaults();
@@ -200,6 +195,8 @@ public:
 	int  ResetVideo();
 	int  ResetVideo();
 	bool ResetAudio();
 	bool ResetAudio();
 
 
+	void ResetOutputs();
+
 	void ResetAudioDevice(const char *sourceId, const char *deviceName,
 	void ResetAudioDevice(const char *sourceId, const char *deviceName,
 			const char *deviceDesc, int channel);
 			const char *deviceDesc, int channel);
 	void ResetAudioDevices();
 	void ResetAudioDevices();