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.
This commit is contained in:
jp9000 2020-08-25 06:30:59 -07:00
parent ab2f3edb2b
commit fcf01304d2
7 changed files with 207 additions and 60 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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();
};

View File

@ -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)

View File

@ -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 {

View File

@ -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();

View File

@ -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;