mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-19 20:32:15 +02:00
UI: Add Virtual Camera source selector dialog
This commit is contained in:
parent
0a87797a21
commit
df446c3f6e
@ -103,6 +103,7 @@ target_sources(
|
||||
forms/OBSBasicSettings.ui
|
||||
forms/OBSBasicSourceSelect.ui
|
||||
forms/OBSBasicTransform.ui
|
||||
forms/OBSBasicVCamConfig.ui
|
||||
forms/OBSExtraBrowsers.ui
|
||||
forms/OBSImporter.ui
|
||||
forms/OBSLogReply.ui
|
||||
@ -257,6 +258,8 @@ target_sources(
|
||||
window-basic-transform.cpp
|
||||
window-basic-transform.hpp
|
||||
window-basic-preview.hpp
|
||||
window-basic-vcam-config.cpp
|
||||
window-basic-vcam-config.hpp
|
||||
window-dock.cpp
|
||||
window-dock.hpp
|
||||
window-importer.cpp
|
||||
|
@ -700,6 +700,17 @@ Basic.Main.Ungroup="Ungroup"
|
||||
Basic.Main.GridMode="Grid Mode"
|
||||
Basic.Main.ListMode="List Mode"
|
||||
|
||||
# virtual camera configuration
|
||||
Basic.Main.VirtualCamConfig="Configure Virtual Camera"
|
||||
Basic.VCam.VirtualCamera="Virtual Camera"
|
||||
Basic.VCam.OutputType="Output Type"
|
||||
Basic.VCam.OutputSelection="Output Selection"
|
||||
Basic.VCam.Internal="Internal"
|
||||
Basic.VCam.InternalDefault="Program Output (Default)"
|
||||
Basic.VCam.InternalPreview="Preview Output"
|
||||
Basic.VCam.Start="Start"
|
||||
Basic.VCam.Update="Update"
|
||||
|
||||
# basic mode file menu
|
||||
Basic.MainMenu.File="&File"
|
||||
Basic.MainMenu.File.Export="&Export"
|
||||
|
113
UI/forms/OBSBasicVCamConfig.ui
Normal file
113
UI/forms/OBSBasicVCamConfig.ui
Normal file
@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OBSBasicVCamConfig</class>
|
||||
<widget class="QDialog" name="OBSBasicVCamConfig">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>170</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Basic.VCam.VirtualCamera</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="outputTypeLabel">
|
||||
<property name="text">
|
||||
<string>Basic.VCam.OutputType</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="outputType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Basic.VCam.Internal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Basic.Scene</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Basic.Main.Source</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="outputSelectionLabel">
|
||||
<property name="text">
|
||||
<string>Basic.VCam.OutputSelection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="outputSelection"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>OBSBasicVCamConfig</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>OBSBasicVCamConfig</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -18,19 +18,143 @@ void RecordButton::resizeEvent(QResizeEvent *event)
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void ReplayBufferButton::resizeEvent(QResizeEvent *event)
|
||||
static QWidget *firstWidget(QLayoutItem *item)
|
||||
{
|
||||
auto widget = item->widget();
|
||||
if (widget)
|
||||
return widget;
|
||||
|
||||
auto layout = item->layout();
|
||||
if (!layout)
|
||||
return nullptr;
|
||||
|
||||
auto n = layout->count();
|
||||
for (auto i = 0, n = layout->count(); i < n; i++) {
|
||||
widget = firstWidget(layout->itemAt(i));
|
||||
if (widget)
|
||||
return widget;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static QWidget *lastWidget(QLayoutItem *item)
|
||||
{
|
||||
auto widget = item->widget();
|
||||
if (widget)
|
||||
return widget;
|
||||
|
||||
auto layout = item->layout();
|
||||
if (!layout)
|
||||
return nullptr;
|
||||
|
||||
auto n = layout->count();
|
||||
for (auto i = layout->count(); i > 0; i--) {
|
||||
widget = lastWidget(layout->itemAt(i - 1));
|
||||
if (widget)
|
||||
return widget;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static QWidget *getNextWidget(QBoxLayout *container, QLayoutItem *item)
|
||||
{
|
||||
for (auto i = 1, n = container->count(); i < n; i++) {
|
||||
if (container->itemAt(i - 1) == item)
|
||||
return firstWidget(container->itemAt(i));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ControlsSplitButton::ControlsSplitButton(const QString &text,
|
||||
const QVariant &themeID,
|
||||
void (OBSBasic::*clicked)())
|
||||
: QHBoxLayout(OBSBasic::Get())
|
||||
{
|
||||
button.reset(new QPushButton(text));
|
||||
button->setCheckable(true);
|
||||
button->setProperty("themeID", themeID);
|
||||
|
||||
button->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
|
||||
button->installEventFilter(this);
|
||||
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
connect(button.data(), &QPushButton::clicked, main, clicked);
|
||||
|
||||
addWidget(button.data());
|
||||
}
|
||||
|
||||
void ControlsSplitButton::addIcon(const QString &name, const QVariant &themeID,
|
||||
void (OBSBasic::*clicked)())
|
||||
{
|
||||
icon.reset(new QPushButton());
|
||||
icon->setAccessibleName(name);
|
||||
icon->setToolTip(name);
|
||||
icon->setChecked(false);
|
||||
icon->setProperty("themeID", themeID);
|
||||
|
||||
QSizePolicy sp;
|
||||
sp.setHeightForWidth(true);
|
||||
icon->setSizePolicy(sp);
|
||||
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
connect(icon.data(), &QAbstractButton::clicked, main, clicked);
|
||||
|
||||
addWidget(icon.data());
|
||||
QWidget::setTabOrder(button.data(), icon.data());
|
||||
|
||||
auto next = getNextWidget(main->ui->buttonsVLayout, this);
|
||||
if (next)
|
||||
QWidget::setTabOrder(icon.data(), next);
|
||||
}
|
||||
|
||||
void ControlsSplitButton::removeIcon()
|
||||
{
|
||||
icon.reset();
|
||||
}
|
||||
|
||||
void ControlsSplitButton::insert(int index)
|
||||
{
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
if (!main->replay)
|
||||
return;
|
||||
auto count = main->ui->buttonsVLayout->count();
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
else if (index > count)
|
||||
index = count;
|
||||
|
||||
QSize replaySize = main->replay->size();
|
||||
int height = main->ui->recordButton->size().height();
|
||||
main->ui->buttonsVLayout->insertLayout(index, this);
|
||||
|
||||
if (replaySize.height() != height || replaySize.width() != height) {
|
||||
main->replay->setMinimumSize(height, height);
|
||||
main->replay->setMaximumSize(height, height);
|
||||
QWidget *prev = button.data();
|
||||
|
||||
if (index > 0) {
|
||||
prev = lastWidget(main->ui->buttonsVLayout->itemAt(index - 1));
|
||||
if (prev)
|
||||
QWidget::setTabOrder(prev, button.data());
|
||||
prev = button.data();
|
||||
}
|
||||
|
||||
event->accept();
|
||||
if (icon) {
|
||||
QWidget::setTabOrder(button.data(), icon.data());
|
||||
prev = icon.data();
|
||||
}
|
||||
|
||||
if (index < count) {
|
||||
auto next = firstWidget(
|
||||
main->ui->buttonsVLayout->itemAt(index + 1));
|
||||
if (next)
|
||||
QWidget::setTabOrder(prev, next);
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlsSplitButton::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::Resize && icon) {
|
||||
QSize iconSize = icon->size();
|
||||
int height = button->height();
|
||||
|
||||
if (iconSize.height() != height || iconSize.width() != height) {
|
||||
icon->setMinimumSize(height, height);
|
||||
icon->setMaximumSize(height, height);
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QBoxLayout>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class RecordButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
@ -11,15 +13,27 @@ public:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
};
|
||||
|
||||
class ReplayBufferButton : public QPushButton {
|
||||
class OBSBasic;
|
||||
|
||||
class ControlsSplitButton : public QHBoxLayout {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline ReplayBufferButton(const QString &text,
|
||||
QWidget *parent = nullptr)
|
||||
: QPushButton(text, parent)
|
||||
{
|
||||
}
|
||||
ControlsSplitButton(const QString &text, const QVariant &themeID,
|
||||
void (OBSBasic::*clicked)());
|
||||
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
void addIcon(const QString &name, const QVariant &themeID,
|
||||
void (OBSBasic::*clicked)());
|
||||
void removeIcon();
|
||||
void insert(int index);
|
||||
|
||||
inline QPushButton *first() { return button.data(); }
|
||||
inline QPushButton *second() { return icon.data(); }
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private:
|
||||
QScopedPointer<QPushButton> button;
|
||||
QScopedPointer<QPushButton> icon;
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "audio-encoders.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
#include "window-basic-vcam-config.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -178,6 +179,9 @@ static void OBSStopVirtualCam(void *data, calldata_t *params)
|
||||
os_atomic_set_bool(&virtualcam_active, false);
|
||||
QMetaObject::invokeMethod(output->main, "OnVirtualCamStop",
|
||||
Q_ARG(int, code));
|
||||
|
||||
obs_output_set_media(output->virtualCam, nullptr, nullptr);
|
||||
OBSBasicVCamConfig::StopVideo();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@ -226,8 +230,11 @@ inline BasicOutputHandler::BasicOutputHandler(OBSBasic *main_) : main(main_)
|
||||
bool BasicOutputHandler::StartVirtualCam()
|
||||
{
|
||||
if (main->vcamEnabled) {
|
||||
obs_output_set_media(virtualCam, obs_get_video(),
|
||||
obs_get_audio());
|
||||
video_t *video = OBSBasicVCamConfig::StartVideo();
|
||||
if (!video)
|
||||
return false;
|
||||
|
||||
obs_output_set_media(virtualCam, video, obs_get_audio());
|
||||
if (!Active())
|
||||
SetupOutputs();
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <util/dstr.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-vcam-config.hpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include "window-namedialog.hpp"
|
||||
#include "menu-button.hpp"
|
||||
@ -283,6 +284,9 @@ void OBSBasic::OverrideTransition(OBSSource transition)
|
||||
obs_transition_swap_begin(transition, oldTransition);
|
||||
obs_set_output_source(0, transition);
|
||||
obs_transition_swap_end(transition, oldTransition);
|
||||
|
||||
// Transition overrides don't raise an event so we need to call update directly
|
||||
OBSBasicVCamConfig::UpdateOutputSource();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-stats.hpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
#include "window-basic-vcam-config.hpp"
|
||||
#include "window-log-reply.hpp"
|
||||
#include "window-projector.hpp"
|
||||
#include "window-remux.hpp"
|
||||
@ -1622,16 +1623,15 @@ void OBSBasic::ReplayBufferClicked()
|
||||
|
||||
void OBSBasic::AddVCamButton()
|
||||
{
|
||||
vcamButton = new ReplayBufferButton(QTStr("Basic.Main.StartVirtualCam"),
|
||||
this);
|
||||
vcamButton->setCheckable(true);
|
||||
connect(vcamButton.data(), &QPushButton::clicked, this,
|
||||
&OBSBasic::VCamButtonClicked);
|
||||
OBSBasicVCamConfig::Init();
|
||||
|
||||
vcamButton->setProperty("themeID", "vcamButton");
|
||||
ui->buttonsVLayout->insertWidget(2, vcamButton);
|
||||
setTabOrder(ui->recordButton, vcamButton);
|
||||
setTabOrder(vcamButton, ui->modeSwitch);
|
||||
vcamButton = new ControlsSplitButton(
|
||||
QTStr("Basic.Main.StartVirtualCam"), "vcamButton",
|
||||
&OBSBasic::VCamButtonClicked);
|
||||
vcamButton->addIcon(QTStr("Basic.Main.VirtualCamConfig"),
|
||||
QStringLiteral("configIconSmall"),
|
||||
&OBSBasic::VCamConfigButtonClicked);
|
||||
vcamButton->insert(2);
|
||||
}
|
||||
|
||||
void OBSBasic::ResetOutputs()
|
||||
@ -1647,28 +1647,13 @@ void OBSBasic::ResetOutputs()
|
||||
: CreateSimpleOutputHandler(this));
|
||||
|
||||
delete replayBufferButton;
|
||||
delete replayLayout;
|
||||
|
||||
if (outputHandler->replayBuffer) {
|
||||
replayBufferButton = new ReplayBufferButton(
|
||||
QTStr("Basic.Main.StartReplayBuffer"), this);
|
||||
replayBufferButton->setCheckable(true);
|
||||
connect(replayBufferButton.data(),
|
||||
&QPushButton::clicked, this,
|
||||
replayBufferButton = new ControlsSplitButton(
|
||||
QTStr("Basic.Main.StartReplayBuffer"),
|
||||
"replayBufferButton",
|
||||
&OBSBasic::ReplayBufferClicked);
|
||||
|
||||
replayBufferButton->setSizePolicy(QSizePolicy::Ignored,
|
||||
QSizePolicy::Fixed);
|
||||
|
||||
replayLayout = new QHBoxLayout(this);
|
||||
replayLayout->addWidget(replayBufferButton);
|
||||
|
||||
replayBufferButton->setProperty("themeID",
|
||||
"replayBufferButton");
|
||||
ui->buttonsVLayout->insertLayout(2, replayLayout);
|
||||
setTabOrder(ui->recordButton, replayBufferButton);
|
||||
setTabOrder(replayBufferButton,
|
||||
ui->buttonsVLayout->itemAt(3)->widget());
|
||||
replayBufferButton->insert(2);
|
||||
}
|
||||
|
||||
if (sysTrayReplayBuffer)
|
||||
@ -7257,19 +7242,19 @@ void OBSBasic::StartReplayBuffer()
|
||||
return;
|
||||
|
||||
if (!UIValidation::NoSourcesConfirmation(this)) {
|
||||
replayBufferButton->setChecked(false);
|
||||
replayBufferButton->first()->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OutputPathValid()) {
|
||||
OutputPathInvalidMessage();
|
||||
replayBufferButton->setChecked(false);
|
||||
replayBufferButton->first()->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LowDiskSpace()) {
|
||||
DiskSpaceMessage();
|
||||
replayBufferButton->setChecked(false);
|
||||
replayBufferButton->first()->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7279,7 +7264,7 @@ void OBSBasic::StartReplayBuffer()
|
||||
SaveProject();
|
||||
|
||||
if (!outputHandler->StartReplayBuffer()) {
|
||||
replayBufferButton->setChecked(false);
|
||||
replayBufferButton->first()->setChecked(false);
|
||||
} else if (os_atomic_load_bool(&recording_paused)) {
|
||||
ShowReplayBufferPauseWarning();
|
||||
}
|
||||
@ -7290,10 +7275,12 @@ void OBSBasic::ReplayBufferStopping()
|
||||
if (!outputHandler || !outputHandler->replayBuffer)
|
||||
return;
|
||||
|
||||
replayBufferButton->setText(QTStr("Basic.Main.StoppingReplayBuffer"));
|
||||
replayBufferButton->first()->setText(
|
||||
QTStr("Basic.Main.StoppingReplayBuffer"));
|
||||
|
||||
if (sysTrayReplayBuffer)
|
||||
sysTrayReplayBuffer->setText(replayBufferButton->text());
|
||||
sysTrayReplayBuffer->setText(
|
||||
replayBufferButton->first()->text());
|
||||
|
||||
replayBufferStopping = true;
|
||||
if (api)
|
||||
@ -7318,11 +7305,13 @@ void OBSBasic::ReplayBufferStart()
|
||||
if (!outputHandler || !outputHandler->replayBuffer)
|
||||
return;
|
||||
|
||||
replayBufferButton->setText(QTStr("Basic.Main.StopReplayBuffer"));
|
||||
replayBufferButton->setChecked(true);
|
||||
replayBufferButton->first()->setText(
|
||||
QTStr("Basic.Main.StopReplayBuffer"));
|
||||
replayBufferButton->first()->setChecked(true);
|
||||
|
||||
if (sysTrayReplayBuffer)
|
||||
sysTrayReplayBuffer->setText(replayBufferButton->text());
|
||||
sysTrayReplayBuffer->setText(
|
||||
replayBufferButton->first()->text());
|
||||
|
||||
replayBufferStopping = false;
|
||||
if (api)
|
||||
@ -7375,11 +7364,13 @@ void OBSBasic::ReplayBufferStop(int code)
|
||||
if (!outputHandler || !outputHandler->replayBuffer)
|
||||
return;
|
||||
|
||||
replayBufferButton->setText(QTStr("Basic.Main.StartReplayBuffer"));
|
||||
replayBufferButton->setChecked(false);
|
||||
replayBufferButton->first()->setText(
|
||||
QTStr("Basic.Main.StartReplayBuffer"));
|
||||
replayBufferButton->first()->setChecked(false);
|
||||
|
||||
if (sysTrayReplayBuffer)
|
||||
sysTrayReplayBuffer->setText(replayBufferButton->text());
|
||||
sysTrayReplayBuffer->setText(
|
||||
replayBufferButton->first()->text());
|
||||
|
||||
blog(LOG_INFO, REPLAY_BUFFER_STOP);
|
||||
|
||||
@ -7428,7 +7419,7 @@ void OBSBasic::StartVirtualCam()
|
||||
SaveProject();
|
||||
|
||||
if (!outputHandler->StartVirtualCam()) {
|
||||
vcamButton->setChecked(false);
|
||||
vcamButton->first()->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7450,10 +7441,10 @@ void OBSBasic::OnVirtualCamStart()
|
||||
if (!outputHandler || !outputHandler->virtualCam)
|
||||
return;
|
||||
|
||||
vcamButton->setText(QTStr("Basic.Main.StopVirtualCam"));
|
||||
vcamButton->first()->setText(QTStr("Basic.Main.StopVirtualCam"));
|
||||
if (sysTrayVirtualCam)
|
||||
sysTrayVirtualCam->setText(QTStr("Basic.Main.StopVirtualCam"));
|
||||
vcamButton->setChecked(true);
|
||||
vcamButton->first()->setChecked(true);
|
||||
|
||||
if (api)
|
||||
api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED);
|
||||
@ -7468,10 +7459,10 @@ void OBSBasic::OnVirtualCamStop(int)
|
||||
if (!outputHandler || !outputHandler->virtualCam)
|
||||
return;
|
||||
|
||||
vcamButton->setText(QTStr("Basic.Main.StartVirtualCam"));
|
||||
vcamButton->first()->setText(QTStr("Basic.Main.StartVirtualCam"));
|
||||
if (sysTrayVirtualCam)
|
||||
sysTrayVirtualCam->setText(QTStr("Basic.Main.StartVirtualCam"));
|
||||
vcamButton->setChecked(false);
|
||||
vcamButton->first()->setChecked(false);
|
||||
|
||||
if (api)
|
||||
api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED);
|
||||
@ -7623,7 +7614,7 @@ void OBSBasic::VCamButtonClicked()
|
||||
StopVirtualCam();
|
||||
} else {
|
||||
if (!UIValidation::NoSourcesConfirmation(this)) {
|
||||
vcamButton->setChecked(false);
|
||||
vcamButton->first()->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7631,6 +7622,12 @@ void OBSBasic::VCamButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasic::VCamConfigButtonClicked()
|
||||
{
|
||||
OBSBasicVCamConfig config(this);
|
||||
config.exec();
|
||||
}
|
||||
|
||||
void OBSBasic::on_settingsButton_clicked()
|
||||
{
|
||||
on_action_Settings_triggered();
|
||||
@ -9757,6 +9754,7 @@ void OBSBasic::PauseRecording()
|
||||
|
||||
os_atomic_set_bool(&recording_paused, true);
|
||||
|
||||
auto replay = replayBufferButton->second();
|
||||
if (replay)
|
||||
replay->setEnabled(false);
|
||||
|
||||
@ -9801,6 +9799,7 @@ void OBSBasic::UnpauseRecording()
|
||||
|
||||
os_atomic_set_bool(&recording_paused, false);
|
||||
|
||||
auto replay = replayBufferButton->second();
|
||||
if (replay)
|
||||
replay->setEnabled(true);
|
||||
|
||||
@ -9877,28 +9876,13 @@ void OBSBasic::UpdateReplayBuffer(bool activate)
|
||||
{
|
||||
if (!activate || !outputHandler ||
|
||||
!outputHandler->ReplayBufferActive()) {
|
||||
replay.reset();
|
||||
replayBufferButton->removeIcon();
|
||||
return;
|
||||
}
|
||||
|
||||
replay.reset(new QPushButton());
|
||||
replay->setAccessibleName(QTStr("Basic.Main.SaveReplay"));
|
||||
replay->setToolTip(QTStr("Basic.Main.SaveReplay"));
|
||||
replay->setChecked(false);
|
||||
replay->setProperty("themeID",
|
||||
QVariant(QStringLiteral("replayIconSmall")));
|
||||
|
||||
QSizePolicy sp;
|
||||
sp.setHeightForWidth(true);
|
||||
replay->setSizePolicy(sp);
|
||||
|
||||
connect(replay.data(), &QAbstractButton::clicked, this,
|
||||
&OBSBasic::ReplayBufferSave);
|
||||
replayLayout->addWidget(replay.data());
|
||||
setTabOrder(replayLayout->itemAt(0)->widget(),
|
||||
replayLayout->itemAt(1)->widget());
|
||||
setTabOrder(replayLayout->itemAt(1)->widget(),
|
||||
ui->buttonsVLayout->itemAt(3)->widget());
|
||||
replayBufferButton->addIcon(QTStr("Basic.Main.SaveReplay"),
|
||||
QStringLiteral("replayIconSmall"),
|
||||
&OBSBasic::ReplayBufferSave);
|
||||
}
|
||||
|
||||
#define MBYTE (1024ULL * 1024ULL)
|
||||
|
@ -178,7 +178,7 @@ class OBSBasic : public OBSMainWindow {
|
||||
friend class AutoConfig;
|
||||
friend class AutoConfigStreamPage;
|
||||
friend class RecordButton;
|
||||
friend class ReplayBufferButton;
|
||||
friend class ControlsSplitButton;
|
||||
friend class ExtraBrowsersModel;
|
||||
friend class ExtraBrowsersDelegate;
|
||||
friend class DeviceCaptureToolbar;
|
||||
@ -298,12 +298,10 @@ private:
|
||||
QPointer<QMenu> startStreamMenu;
|
||||
|
||||
QPointer<QPushButton> transitionButton;
|
||||
QPointer<QPushButton> replayBufferButton;
|
||||
QPointer<QHBoxLayout> replayLayout;
|
||||
QPointer<ControlsSplitButton> replayBufferButton;
|
||||
QScopedPointer<QPushButton> pause;
|
||||
QScopedPointer<QPushButton> replay;
|
||||
|
||||
QPointer<QPushButton> vcamButton;
|
||||
QPointer<ControlsSplitButton> vcamButton;
|
||||
bool vcamEnabled = false;
|
||||
|
||||
QScopedPointer<QSystemTrayIcon> trayIcon;
|
||||
@ -1038,6 +1036,7 @@ private slots:
|
||||
void on_streamButton_clicked();
|
||||
void on_recordButton_clicked();
|
||||
void VCamButtonClicked();
|
||||
void VCamConfigButtonClicked();
|
||||
void on_settingsButton_clicked();
|
||||
void Screenshot(OBSSource source_ = nullptr);
|
||||
void ScreenshotSelectedSource();
|
||||
|
264
UI/window-basic-vcam-config.cpp
Normal file
264
UI/window-basic-vcam-config.cpp
Normal file
@ -0,0 +1,264 @@
|
||||
#include "window-basic-vcam-config.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "remote-text.hpp"
|
||||
#include <util/util.hpp>
|
||||
#include <util/platform.h>
|
||||
#include <platform.hpp>
|
||||
#include <mutex>
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum class VCamOutputType {
|
||||
Internal,
|
||||
Scene,
|
||||
Source,
|
||||
};
|
||||
|
||||
enum class VCamInternalType {
|
||||
Default,
|
||||
Preview,
|
||||
};
|
||||
|
||||
struct VCamConfig {
|
||||
VCamOutputType type = VCamOutputType::Internal;
|
||||
VCamInternalType internal = VCamInternalType::Default;
|
||||
string scene;
|
||||
string source;
|
||||
};
|
||||
|
||||
static VCamConfig *vCamConfig = nullptr;
|
||||
|
||||
OBSBasicVCamConfig::OBSBasicVCamConfig(QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::OBSBasicVCamConfig)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
auto type = (int)vCamConfig->type;
|
||||
ui->outputType->setCurrentIndex(type);
|
||||
OutputTypeChanged(type);
|
||||
connect(ui->outputType,
|
||||
static_cast<void (QComboBox::*)(int)>(
|
||||
&QComboBox::currentIndexChanged),
|
||||
this, &OBSBasicVCamConfig::OutputTypeChanged);
|
||||
|
||||
auto start = ui->buttonBox->button(QDialogButtonBox::Ok);
|
||||
if (!obs_frontend_virtualcam_active())
|
||||
start->setText(QTStr("Basic.VCam.Start"));
|
||||
else
|
||||
start->setText(QTStr("Basic.VCam.Update"));
|
||||
connect(start, &QPushButton::clicked, this,
|
||||
&OBSBasicVCamConfig::SaveAndStart);
|
||||
}
|
||||
|
||||
void OBSBasicVCamConfig::OutputTypeChanged(int type)
|
||||
{
|
||||
auto list = ui->outputSelection;
|
||||
list->clear();
|
||||
|
||||
switch ((VCamOutputType)type) {
|
||||
case VCamOutputType::Internal:
|
||||
list->addItem(QTStr("Basic.VCam.InternalDefault"));
|
||||
list->addItem(QTStr("Basic.VCam.InternalPreview"));
|
||||
list->setCurrentIndex((int)vCamConfig->internal);
|
||||
break;
|
||||
|
||||
case VCamOutputType::Scene: {
|
||||
// Scenes in default order
|
||||
BPtr<char *> scenes = obs_frontend_get_scene_names();
|
||||
int idx = 0;
|
||||
for (char **temp = scenes; *temp; temp++) {
|
||||
list->addItem(*temp);
|
||||
|
||||
if (vCamConfig->scene.compare(*temp) == 0)
|
||||
list->setCurrentIndex(list->count() - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VCamOutputType::Source: {
|
||||
// Sources in alphabetical order
|
||||
vector<string> sources;
|
||||
auto AddSource = [&](obs_source_t *source) {
|
||||
auto name = obs_source_get_name(source);
|
||||
auto flags = obs_source_get_output_flags(source);
|
||||
|
||||
if (!(obs_source_get_output_flags(source) &
|
||||
OBS_SOURCE_VIDEO))
|
||||
return;
|
||||
|
||||
sources.push_back(name);
|
||||
};
|
||||
using AddSource_t = decltype(AddSource);
|
||||
|
||||
obs_enum_sources(
|
||||
[](void *data, obs_source_t *source) {
|
||||
auto &AddSource =
|
||||
*static_cast<AddSource_t *>(data);
|
||||
if (!obs_source_removed(source))
|
||||
AddSource(source);
|
||||
return true;
|
||||
},
|
||||
static_cast<void *>(&AddSource));
|
||||
|
||||
// Sort and select current item
|
||||
sort(sources.begin(), sources.end());
|
||||
for (auto &&source : sources) {
|
||||
list->addItem(source.c_str());
|
||||
|
||||
if (vCamConfig->source == source)
|
||||
list->setCurrentIndex(list->count() - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicVCamConfig::SaveAndStart()
|
||||
{
|
||||
auto type = (VCamOutputType)ui->outputType->currentIndex();
|
||||
auto out = ui->outputSelection;
|
||||
switch (type) {
|
||||
case VCamOutputType::Internal:
|
||||
vCamConfig->internal = (VCamInternalType)out->currentIndex();
|
||||
break;
|
||||
case VCamOutputType::Scene:
|
||||
vCamConfig->scene = out->currentText().toStdString();
|
||||
break;
|
||||
case VCamOutputType::Source:
|
||||
vCamConfig->source = out->currentText().toStdString();
|
||||
break;
|
||||
default:
|
||||
// unknown value, don't save type
|
||||
return;
|
||||
}
|
||||
|
||||
vCamConfig->type = type;
|
||||
|
||||
// Start the vcam if needed, if already running just update the source
|
||||
if (!obs_frontend_virtualcam_active())
|
||||
obs_frontend_start_virtualcam();
|
||||
else
|
||||
UpdateOutputSource();
|
||||
}
|
||||
|
||||
static void SaveCallback(obs_data_t *data, bool saving, void *)
|
||||
{
|
||||
if (saving) {
|
||||
OBSDataAutoRelease obj = obs_data_create();
|
||||
|
||||
obs_data_set_int(obj, "type", (int)vCamConfig->type);
|
||||
obs_data_set_int(obj, "internal", (int)vCamConfig->internal);
|
||||
obs_data_set_string(obj, "scene", vCamConfig->scene.c_str());
|
||||
obs_data_set_string(obj, "source", vCamConfig->source.c_str());
|
||||
|
||||
obs_data_set_obj(data, "virtual-camera", obj);
|
||||
} else {
|
||||
OBSDataAutoRelease obj =
|
||||
obs_data_get_obj(data, "virtual-camera");
|
||||
|
||||
vCamConfig->type =
|
||||
(VCamOutputType)obs_data_get_int(obj, "type");
|
||||
vCamConfig->internal =
|
||||
(VCamInternalType)obs_data_get_int(obj, "internal");
|
||||
vCamConfig->scene = obs_data_get_string(obj, "scene");
|
||||
vCamConfig->source = obs_data_get_string(obj, "source");
|
||||
}
|
||||
}
|
||||
|
||||
static void EventCallback(enum obs_frontend_event event, void *)
|
||||
{
|
||||
if (vCamConfig->type != VCamOutputType::Internal)
|
||||
return;
|
||||
|
||||
// Update output source if the preview scene changes
|
||||
// or if the default transition is changed
|
||||
switch (event) {
|
||||
case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:
|
||||
if (vCamConfig->internal != VCamInternalType::Preview)
|
||||
return;
|
||||
break;
|
||||
case OBS_FRONTEND_EVENT_TRANSITION_CHANGED:
|
||||
if (vCamConfig->internal != VCamInternalType::Default)
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
OBSBasicVCamConfig::UpdateOutputSource();
|
||||
}
|
||||
|
||||
void OBSBasicVCamConfig::Init()
|
||||
{
|
||||
if (vCamConfig)
|
||||
return;
|
||||
|
||||
vCamConfig = new VCamConfig;
|
||||
|
||||
obs_frontend_add_save_callback(SaveCallback, nullptr);
|
||||
obs_frontend_add_event_callback(EventCallback, nullptr);
|
||||
}
|
||||
|
||||
static obs_view_t *view = nullptr;
|
||||
static video_t *video = nullptr;
|
||||
|
||||
video_t *OBSBasicVCamConfig::StartVideo()
|
||||
{
|
||||
if (!video) {
|
||||
view = obs_view_create();
|
||||
video = obs_view_add(view);
|
||||
}
|
||||
UpdateOutputSource();
|
||||
return video;
|
||||
}
|
||||
|
||||
void OBSBasicVCamConfig::StopVideo()
|
||||
{
|
||||
if (view) {
|
||||
obs_view_remove(view);
|
||||
obs_view_set_source(view, 0, nullptr);
|
||||
obs_view_destroy(view);
|
||||
view = nullptr;
|
||||
}
|
||||
video = nullptr;
|
||||
}
|
||||
|
||||
void OBSBasicVCamConfig::UpdateOutputSource()
|
||||
{
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
obs_source_t *source = nullptr;
|
||||
|
||||
switch ((VCamOutputType)vCamConfig->type) {
|
||||
case VCamOutputType::Internal:
|
||||
switch (vCamConfig->internal) {
|
||||
case VCamInternalType::Default:
|
||||
source = obs_get_output_source(0);
|
||||
break;
|
||||
case VCamInternalType::Preview:
|
||||
OBSSource s = OBSBasic::Get()->GetCurrentSceneSource();
|
||||
obs_source_get_ref(s);
|
||||
source = s;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case VCamOutputType::Scene:
|
||||
source = obs_get_source_by_name(vCamConfig->scene.c_str());
|
||||
break;
|
||||
|
||||
case VCamOutputType::Source:
|
||||
source = obs_get_source_by_name(vCamConfig->source.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
auto current = obs_view_get_source(view, 0);
|
||||
if (source != current)
|
||||
obs_view_set_source(view, 0, source);
|
||||
obs_source_release(source);
|
||||
obs_source_release(current);
|
||||
}
|
27
UI/window-basic-vcam-config.hpp
Normal file
27
UI/window-basic-vcam-config.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
#include "ui_OBSBasicVCamConfig.h"
|
||||
|
||||
class OBSBasicVCamConfig : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
static video_t *StartVideo();
|
||||
static void StopVideo();
|
||||
static void UpdateOutputSource();
|
||||
|
||||
explicit OBSBasicVCamConfig(QWidget *parent = 0);
|
||||
|
||||
private slots:
|
||||
void OutputTypeChanged(int type);
|
||||
void SaveAndStart();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::OBSBasicVCamConfig> ui;
|
||||
};
|
Loading…
Reference in New Issue
Block a user