Pārlūkot izejas kodu

UI: Defer device properties to separate thread

Because devices can take significant time to enumerate, defer the
properties creation to a separate thread.  The author of this commit
feels a great amount of displeasure over having to write this.
jp9000 5 gadi atpakaļ
vecāks
revīzija
fcf01304d2

+ 2 - 0
UI/CMakeLists.txt

@@ -238,6 +238,7 @@ set(obs_SOURCES
 	item-widget-helpers.cpp
 	context-bar-controls.cpp
 	horizontal-scroll-area.cpp
+	context-bar-controls-devices.cpp
 	vertical-scroll-area.cpp
 	visibility-item-widget.cpp
 	slider-absoluteset-style.cpp
@@ -305,6 +306,7 @@ set(obs_HEADERS
 	item-widget-helpers.hpp
 	visibility-checkbox.hpp
 	context-bar-controls.hpp
+	context-bar-controls-devices.hpp
 	locked-checkbox.hpp
 	horizontal-scroll-area.hpp
 	expand-checkbox.hpp

+ 149 - 0
UI/context-bar-controls-devices.cpp

@@ -0,0 +1,149 @@
+#include "context-bar-controls.hpp"
+#include "context-bar-controls-devices.hpp"
+#include "window-basic-main.hpp"
+
+#include "ui_device-select-toolbar.h"
+
+#ifdef _WIN32
+#define get_os_module(win, mac, linux) obs_get_module(win)
+#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)
+#elif __APPLE__
+#define get_os_module(win, mac, linux) obs_get_module(mac)
+#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)
+#else
+#define get_os_module(win, mac, linux) obs_get_module(linux)
+#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)
+#endif
+
+/* ========================================================================= */
+
+DeviceToolbarPropertiesThread::~DeviceToolbarPropertiesThread()
+{
+	obs_properties_destroy(props);
+}
+
+void DeviceToolbarPropertiesThread::run()
+{
+	props = obs_source_properties(source);
+	source = nullptr;
+	QMetaObject::invokeMethod(this, "Ready");
+}
+
+void DeviceToolbarPropertiesThread::Ready()
+{
+	OBSBasic *main = OBSBasic::Get();
+	QLayoutItem *la = main->ui->emptySpace->layout()->itemAt(0);
+	if (la) {
+		DeviceCaptureToolbar *dct =
+			qobject_cast<DeviceCaptureToolbar *>(la->widget());
+		if (dct) {
+			dct->SetProperties(props);
+			props = nullptr;
+		}
+	}
+}
+
+/* ========================================================================= */
+
+DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
+	: QWidget(parent),
+	  weakSource(OBSGetWeakRef(source)),
+	  ui(new Ui_DeviceSelectToolbar)
+{
+	ui->setupUi(this);
+
+#ifndef _WIN32
+	delete ui->activateButton;
+	ui->activateButton = nullptr;
+#endif
+
+	setEnabled(false);
+
+	obs_module_t *mod =
+		get_os_module("win-dshow", "mac-avcapture", "linux-v4l2");
+	const char *device_str = obs_module_get_locale_text(mod, "Device");
+	ui->deviceLabel->setText(device_str);
+
+	OBSBasic *main = OBSBasic::Get();
+	if (!main->devicePropertiesThread ||
+	    !main->devicePropertiesThread->isRunning()) {
+		main->devicePropertiesThread.reset(
+			new DeviceToolbarPropertiesThread(source));
+		main->devicePropertiesThread->start();
+	}
+}
+
+DeviceCaptureToolbar::~DeviceCaptureToolbar()
+{
+	delete ui;
+	obs_properties_destroy(props);
+}
+
+void DeviceCaptureToolbar::UpdateActivateButtonName()
+{
+	obs_property_t *p = obs_properties_get(props, "activate");
+	ui->activateButton->setText(obs_property_description(p));
+}
+
+extern void UpdateSourceComboToolbarProperties(QComboBox *combo,
+					       OBSSource source,
+					       obs_properties_t *props,
+					       const char *prop_name,
+					       bool is_int);
+extern void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source,
+					  int idx, const char *prop_name,
+					  bool is_int);
+
+void DeviceCaptureToolbar::SetProperties(obs_properties_t *props_)
+{
+	OBSSource source = OBSGetStrongRef(weakSource);
+	if (!source) {
+		obs_properties_destroy(props_);
+		return;
+	}
+
+#ifdef _WIN32
+	prop_name = "video_device_id";
+#elif __APPLE__
+	prop_name = "device";
+#else
+	prop_name = "device_id";
+#endif
+
+	props = props_;
+	UpdateSourceComboToolbarProperties(ui->device, source, props, prop_name,
+					   false);
+#ifdef _WIN32
+	UpdateActivateButtonName();
+#endif
+	setEnabled(true);
+}
+
+void DeviceCaptureToolbar::on_device_currentIndexChanged(int idx)
+{
+	OBSSource source = OBSGetStrongRef(weakSource);
+	if (idx == -1 || !source) {
+		return;
+	}
+
+	UpdateSourceComboToolbarValue(ui->device, source, idx, prop_name,
+				      false);
+}
+
+void DeviceCaptureToolbar::on_activateButton_clicked()
+{
+	OBSSource source = OBSGetStrongRef(weakSource);
+	if (!source) {
+		return;
+	}
+
+	obs_property_t *p = obs_properties_get(props, "activate");
+	if (!p) {
+		return;
+	}
+
+	obs_property_button_clicked(p, source.Get());
+#ifdef _WIN32
+	UpdateActivateButtonName();
+#endif
+}

+ 25 - 0
UI/context-bar-controls-devices.hpp

@@ -0,0 +1,25 @@
+#pragma once
+
+#include <obs.hpp>
+#include <string>
+#include <QThread>
+
+class DeviceToolbarPropertiesThread : public QThread {
+	Q_OBJECT
+
+	OBSSource source;
+	obs_properties_t *props;
+
+	void run() override;
+
+public:
+	inline DeviceToolbarPropertiesThread(OBSSource source_)
+		: source(source_)
+	{
+	}
+
+	~DeviceToolbarPropertiesThread() override;
+
+public slots:
+	void Ready();
+};

+ 0 - 54
UI/context-bar-controls.cpp

@@ -152,12 +152,6 @@ void ComboSelectToolbar::Init()
 					   prop_name, is_int);
 }
 
-void ComboSelectToolbar::UpdateActivateButtonName()
-{
-	obs_property_t *p = obs_properties_get(props.get(), "activate");
-	ui->activateButton->setText(obs_property_description(p));
-}
-
 void UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx,
 				   const char *prop_name, bool is_int)
 {
@@ -184,22 +178,6 @@ void ComboSelectToolbar::on_device_currentIndexChanged(int idx)
 				      is_int);
 }
 
-void ComboSelectToolbar::on_activateButton_clicked()
-{
-	OBSSource source = GetSource();
-	if (!source) {
-		return;
-	}
-
-	obs_property_t *p = obs_properties_get(props.get(), "activate");
-	if (!p) {
-		return;
-	}
-
-	obs_property_button_clicked(p, source.Get());
-	UpdateActivateButtonName();
-}
-
 AudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source)
 	: ComboSelectToolbar(parent, source)
 {
@@ -278,38 +256,6 @@ void DisplayCaptureToolbar::Init()
 	ComboSelectToolbar::Init();
 }
 
-DeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)
-	: ComboSelectToolbar(parent, source)
-{
-}
-
-void DeviceCaptureToolbar::Init()
-{
-#ifndef _WIN32
-	delete ui->activateButton;
-	ui->activateButton = nullptr;
-#endif
-
-	obs_module_t *mod =
-		get_os_module("win-dshow", "mac-avcapture", "linux-v4l2");
-	const char *device_str = obs_module_get_locale_text(mod, "Device");
-	ui->deviceLabel->setText(device_str);
-
-#ifdef _WIN32
-	prop_name = "video_device_id";
-#elif __APPLE__
-	prop_name = "device";
-#else
-	prop_name = "device_id";
-#endif
-
-#ifdef _WIN32
-	UpdateActivateButtonName();
-#endif
-
-	ComboSelectToolbar::Init();
-}
-
 /* ========================================================================= */
 
 GameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)

+ 15 - 5
UI/context-bar-controls.hpp

@@ -53,8 +53,6 @@ protected:
 	const char *prop_name;
 	bool is_int = false;
 
-	void UpdateActivateButtonName();
-
 public:
 	ComboSelectToolbar(QWidget *parent, OBSSource source);
 	~ComboSelectToolbar();
@@ -62,7 +60,6 @@ public:
 
 public slots:
 	void on_device_currentIndexChanged(int idx);
-	void on_activateButton_clicked();
 };
 
 class AudioCaptureToolbar : public ComboSelectToolbar {
@@ -89,12 +86,25 @@ public:
 	void Init() override;
 };
 
-class DeviceCaptureToolbar : public ComboSelectToolbar {
+class DeviceCaptureToolbar : public QWidget {
 	Q_OBJECT
 
+	OBSWeakSource weakSource;
+	Ui_DeviceSelectToolbar *ui;
+	obs_properties_t *props = nullptr;
+	const char *prop_name;
+
+	void UpdateActivateButtonName();
+
 public:
 	DeviceCaptureToolbar(QWidget *parent, OBSSource source);
-	void Init() override;
+	~DeviceCaptureToolbar();
+
+	void SetProperties(obs_properties_t *prpos);
+
+public slots:
+	void on_device_currentIndexChanged(int idx);
+	void on_activateButton_clicked();
 };
 
 class GameCaptureToolbar : public SourceToolbar {

+ 13 - 1
UI/window-basic-main.cpp

@@ -908,6 +908,13 @@ void OBSBasic::Load(const char *file)
 	InitDefaultTransitions();
 	ClearContextBar();
 
+	if (devicePropertiesThread && devicePropertiesThread->isRunning()) {
+		devicePropertiesThread->wait();
+		devicePropertiesThread.reset();
+	}
+
+	QApplication::sendPostedEvents(this);
+
 	obs_data_t *modulesObj = obs_data_get_obj(data, "modules");
 	if (api)
 		api->on_preload(modulesObj);
@@ -2951,7 +2958,6 @@ void OBSBasic::UpdateContextBar()
 			   strcmp(id, "v4l2_input") == 0) {
 			DeviceCaptureToolbar *c = new DeviceCaptureToolbar(
 				ui->emptySpace, source);
-			c->Init();
 			ui->emptySpace->layout()->addWidget(c);
 
 		} else if (strcmp(id, "game_capture") == 0) {
@@ -4193,6 +4199,12 @@ void OBSBasic::closeEvent(QCloseEvent *event)
 		updateCheckThread->wait();
 	if (logUploadThread)
 		logUploadThread->wait();
+	if (devicePropertiesThread && devicePropertiesThread->isRunning()) {
+		devicePropertiesThread->wait();
+		devicePropertiesThread.reset();
+	}
+
+	QApplication::sendPostedEvents(this);
 
 	signalHandlers.clear();
 

+ 3 - 0
UI/window-basic-main.hpp

@@ -163,6 +163,8 @@ class OBSBasic : public OBSMainWindow {
 	friend class ReplayBufferButton;
 	friend class ExtraBrowsersModel;
 	friend class ExtraBrowsersDelegate;
+	friend class DeviceCaptureToolbar;
+	friend class DeviceToolbarPropertiesThread;
 	friend struct BasicOutputHandler;
 	friend struct OBSStudioAPI;
 
@@ -198,6 +200,7 @@ private:
 	bool copyVisible = true;
 
 	bool closing = false;
+	QScopedPointer<QThread> devicePropertiesThread;
 	QScopedPointer<QThread> whatsNewInitThread;
 	QScopedPointer<QThread> updateCheckThread;
 	QScopedPointer<QThread> introCheckThread;