start implementation of multicam panel
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
add_subdirectory(audiomonitor)
|
||||
add_subdirectory(curve)
|
||||
add_subdirectory(footageviewer)
|
||||
add_subdirectory(multicam)
|
||||
add_subdirectory(node)
|
||||
add_subdirectory(param)
|
||||
add_subdirectory(pixelsampler)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
panel/multicam/multicampanel.h
|
||||
panel/multicam/multicampanel.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "multicampanel.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super PanelWidget
|
||||
|
||||
MulticamPanel::MulticamPanel(ViewerPanelBase *sibling, QWidget *parent) :
|
||||
super(QStringLiteral("MultiCamPanel"), parent)
|
||||
{
|
||||
widget_ = new MulticamWidget();
|
||||
SetWidgetWithPadding(widget_);
|
||||
|
||||
connect(sibling, &ViewerPanelBase::ColorManagerChanged, widget_, &MulticamWidget::ConnectColorManager);
|
||||
widget_->ConnectCacher(static_cast<ViewerWidget*>(sibling->GetTimeBasedWidget())->GetCacher());
|
||||
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
void MulticamPanel::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetTitle(tr("Multi-Cam"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef MULTICAMPANEL_H
|
||||
#define MULTICAMPANEL_H
|
||||
|
||||
#include "panel/viewer/viewerbase.h"
|
||||
#include "widget/multicam/multicamwidget.h"
|
||||
#include "widget/panel/panel.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class MulticamPanel : public PanelWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MulticamPanel(ViewerPanelBase *sibling, QWidget* parent = nullptr);
|
||||
|
||||
void SetNode(MultiCamNode *n)
|
||||
{
|
||||
widget_->SetNode(n);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void SetTime(const rational &t)
|
||||
{
|
||||
widget_->SetTime(t);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Retranslate() override;
|
||||
|
||||
private:
|
||||
MulticamWidget *widget_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MULTICAMPANEL_H
|
||||
@@ -106,6 +106,8 @@ public:
|
||||
|
||||
virtual void Paste() override;
|
||||
|
||||
TimeBasedWidget* GetTimeBasedWidget() const { return widget_; }
|
||||
|
||||
public slots:
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
@@ -127,11 +129,6 @@ signals:
|
||||
void ShuttleRightRequested();
|
||||
|
||||
protected:
|
||||
TimeBasedWidget* GetTimeBasedWidget() const
|
||||
{
|
||||
return widget_;
|
||||
}
|
||||
|
||||
void SetTimeBasedWidget(TimeBasedWidget* widget);
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -62,6 +62,11 @@ PreviewAutoCacher::~PreviewAutoCacher()
|
||||
}
|
||||
|
||||
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, bool dry)
|
||||
{
|
||||
return GetSingleFrame(viewer_node_->GetConnectedTextureOutput(), t, dry);
|
||||
}
|
||||
|
||||
RenderTicketPtr PreviewAutoCacher::GetSingleFrame(Node *n, const rational &t, bool dry)
|
||||
{
|
||||
// If we have a single frame render queued (but not yet sent to the RenderManager), cancel it now
|
||||
CancelQueuedSingleFrameRender();
|
||||
@@ -71,6 +76,7 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, bool dry)
|
||||
sfr->Start();
|
||||
sfr->setProperty("time", QVariant::fromValue(t));
|
||||
sfr->setProperty("dry", dry);
|
||||
sfr->setProperty("node", Node::PtrToValue(n));
|
||||
|
||||
// Queue it and try to render
|
||||
single_frame_render_ = sfr;
|
||||
@@ -611,11 +617,19 @@ void PreviewAutoCacher::TryRender()
|
||||
single_frame_render_ = nullptr;
|
||||
|
||||
// Check if already caching this
|
||||
RenderTicketWatcher *watcher = RenderFrame(copied_viewer_node_->GetConnectedTextureOutput(),
|
||||
t->property("time").value<rational>(),
|
||||
nullptr,
|
||||
t->property("dry").toBool());
|
||||
video_immediate_passthroughs_[watcher].append(t);
|
||||
Node *n = Node::ValueToPtr<Node>(t->property("node"));
|
||||
Node *copy = copy_map_.value(n);
|
||||
|
||||
if (copy) {
|
||||
RenderTicketWatcher *watcher = RenderFrame(copy,
|
||||
t->property("time").value<rational>(),
|
||||
nullptr,
|
||||
t->property("dry").toBool());
|
||||
video_immediate_passthroughs_[watcher].append(t);
|
||||
} else {
|
||||
qWarning() << "Failed to find copied node for SFR ticket";
|
||||
t->Finish();
|
||||
}
|
||||
}
|
||||
|
||||
if (!pause_renders_) {
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
virtual ~PreviewAutoCacher() override;
|
||||
|
||||
RenderTicketPtr GetSingleFrame(const rational& t, bool dry = false);
|
||||
RenderTicketPtr GetSingleFrame(Node *n, const rational& t, bool dry = false);
|
||||
|
||||
RenderTicketPtr GetRangeOfAudio(TimeRange range);
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
uniform int rows;
|
||||
uniform int cols;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -30,6 +30,7 @@ add_subdirectory(handmovableview)
|
||||
add_subdirectory(keyframeview)
|
||||
add_subdirectory(manageddisplay)
|
||||
add_subdirectory(menu)
|
||||
add_subdirectory(multicam)
|
||||
add_subdirectory(nodecombobox)
|
||||
add_subdirectory(nodeparamview)
|
||||
add_subdirectory(nodetableview)
|
||||
|
||||
@@ -275,6 +275,14 @@ void ManagedDisplayWidget::SetInnerMouseTracking(bool e)
|
||||
}
|
||||
}
|
||||
|
||||
VideoParams ManagedDisplayWidget::GetViewportParams() const
|
||||
{
|
||||
int device_width = width() * devicePixelRatioF();
|
||||
int device_height = height() * devicePixelRatioF();
|
||||
VideoParams::Format device_format = static_cast<VideoParams::Format>(OLIVE_CONFIG("OfflinePixelFormat").toInt());
|
||||
return VideoParams(device_width, device_height, device_format, VideoParams::kInternalChannelCount);
|
||||
}
|
||||
|
||||
void ManagedDisplayWidget::update()
|
||||
{
|
||||
if (RenderManager::instance()->backend() == RenderManager::kOpenGL) {
|
||||
|
||||
@@ -58,7 +58,8 @@ protected:
|
||||
virtual void initializeGL() override
|
||||
{
|
||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed,
|
||||
this, &ManagedDisplayWidgetOpenGL::OnDestroy);
|
||||
this, &ManagedDisplayWidgetOpenGL::DestroyListener,
|
||||
Qt::DirectConnection);
|
||||
|
||||
emit OnInit();
|
||||
}
|
||||
@@ -211,6 +212,8 @@ protected:
|
||||
return wrapper_ ? wrapper_->rect() : QRect();
|
||||
}
|
||||
|
||||
VideoParams GetViewportParams() const;
|
||||
|
||||
protected slots:
|
||||
/**
|
||||
* @brief Called whenever the internal rendering context has been created
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# 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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/multicam/multicamwidget.cpp
|
||||
widget/multicam/multicamwidget.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "multicamwidget.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super ManagedDisplayWidget
|
||||
|
||||
MulticamWidget::MulticamWidget(QWidget *parent) :
|
||||
super{parent},
|
||||
node_(nullptr),
|
||||
cacher_(nullptr),
|
||||
tex_(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MulticamWidget::SetNode(MultiCamNode *n)
|
||||
{
|
||||
if (node_ == n) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node_) {
|
||||
// Disconnect
|
||||
}
|
||||
|
||||
node_ = n;
|
||||
|
||||
if (node_) {
|
||||
// Connect
|
||||
}
|
||||
|
||||
SetTime(time_);
|
||||
}
|
||||
|
||||
void MulticamWidget::SetTime(const rational &r)
|
||||
{
|
||||
time_ = r;
|
||||
|
||||
if (node_) {
|
||||
if (cacher_) {
|
||||
int sources = node_->InputArraySize(node_->kSourcesInput);
|
||||
watchers_.resize(sources);
|
||||
textures_.resize(sources);
|
||||
|
||||
watchers_.fill(nullptr);
|
||||
textures_.fill(nullptr);
|
||||
|
||||
for (int i=0; i<sources; i++) {
|
||||
if (Node *n = node_->GetConnectedOutput(node_->kSourcesInput, 0)) {
|
||||
auto w = new RenderTicketWatcher(this);
|
||||
connect(w, &RenderTicketWatcher::Finished, this, &MulticamWidget::RenderedFrame);
|
||||
w->SetTicket(cacher_->GetSingleFrame(n, time_, false));
|
||||
watchers_[i] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MulticamWidget::OnInit()
|
||||
{
|
||||
super::OnInit();
|
||||
|
||||
pipeline_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/multicam.frag"))));
|
||||
}
|
||||
|
||||
void MulticamWidget::OnPaint()
|
||||
{
|
||||
// Clear display surface
|
||||
renderer()->ClearDestination();
|
||||
|
||||
if (tex_) {
|
||||
ColorTransformJob job;
|
||||
job.SetColorProcessor(color_service());
|
||||
job.SetInputTexture(tex_);
|
||||
job.SetInputAlphaAssociation(kAlphaNone);
|
||||
renderer()->BlitColorManaged(job, GetViewportParams());
|
||||
}
|
||||
}
|
||||
|
||||
void MulticamWidget::OnDestroy()
|
||||
{
|
||||
pipeline_.clear();
|
||||
tex_ = nullptr;
|
||||
|
||||
super::OnDestroy();
|
||||
}
|
||||
|
||||
void MulticamWidget::RenderedFrame()
|
||||
{
|
||||
auto watcher = static_cast<RenderTicketWatcher*>(sender());
|
||||
if (watcher->HasResult()) {
|
||||
int index = watchers_.indexOf(watcher);
|
||||
|
||||
if (index != -1) {
|
||||
if ((textures_[index] = watcher->Get().value<TexturePtr>())) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
delete watcher;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef MULTICAMWIDGET_H
|
||||
#define MULTICAMWIDGET_H
|
||||
|
||||
#include "node/input/multicam/multicamnode.h"
|
||||
#include "render/previewautocacher.h"
|
||||
#include "widget/manageddisplay/manageddisplay.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class MulticamWidget : public ManagedDisplayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MulticamWidget(QWidget *parent = nullptr);
|
||||
|
||||
MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR(MulticamWidget)
|
||||
|
||||
void SetNode(MultiCamNode *n);
|
||||
|
||||
void ConnectCacher(PreviewAutoCacher *c)
|
||||
{
|
||||
cacher_ = c;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void SetTime(const rational &r);
|
||||
|
||||
protected slots:
|
||||
virtual void OnInit() override;
|
||||
|
||||
virtual void OnPaint() override;
|
||||
|
||||
virtual void OnDestroy() override;
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
private:
|
||||
MultiCamNode *node_;
|
||||
|
||||
QVariant pipeline_;
|
||||
|
||||
PreviewAutoCacher *cacher_;
|
||||
|
||||
rational time_;
|
||||
|
||||
TexturePtr tex_;
|
||||
|
||||
QVector<RenderTicketWatcher*> watchers_;
|
||||
QVector<TexturePtr> textures_;
|
||||
|
||||
private slots:
|
||||
void RenderedFrame();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MULTICAMWIDGET_H
|
||||
@@ -47,10 +47,10 @@ void HistogramScope::OnInit()
|
||||
|
||||
void HistogramScope::OnDestroy()
|
||||
{
|
||||
super::OnDestroy();
|
||||
|
||||
pipeline_secondary_.clear();
|
||||
texture_row_sums_ = nullptr;
|
||||
|
||||
super::OnDestroy();
|
||||
}
|
||||
|
||||
ShaderCode HistogramScope::GenerateShaderCode()
|
||||
|
||||
@@ -52,9 +52,7 @@ void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
|
||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex)));
|
||||
|
||||
renderer()->Blit(pipeline, job, VideoParams(width(), height(),
|
||||
static_cast<VideoParams::Format>(OLIVE_CONFIG("OfflinePixelFormat").toInt()),
|
||||
VideoParams::kInternalChannelCount));
|
||||
renderer()->Blit(pipeline, job, GetViewportParams());
|
||||
}
|
||||
|
||||
void ScopeBase::OnInit()
|
||||
@@ -89,11 +87,11 @@ void ScopeBase::OnPaint()
|
||||
|
||||
void ScopeBase::OnDestroy()
|
||||
{
|
||||
super::OnDestroy();
|
||||
|
||||
managed_tex_ = nullptr;
|
||||
texture_ = nullptr;
|
||||
pipeline_.clear();
|
||||
|
||||
super::OnDestroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,9 +72,7 @@ void WaveformScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
job.Insert(QStringLiteral("ove_maintex"),
|
||||
NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex)));
|
||||
|
||||
renderer()->Blit(pipeline, job, VideoParams(width(), height(),
|
||||
static_cast<VideoParams::Format>(OLIVE_CONFIG("OfflinePixelFormat").toInt()),
|
||||
VideoParams::kInternalChannelCount));
|
||||
renderer()->Blit(pipeline, job, GetViewportParams());
|
||||
|
||||
float waveform_dim_x = ceil((width() - 1.0) * waveform_scale);
|
||||
float waveform_dim_y = ceil((height() - 1.0) * waveform_scale);
|
||||
|
||||
@@ -100,6 +100,8 @@ public:
|
||||
enable_audio_scrubbing_ = e;
|
||||
}
|
||||
|
||||
PreviewAutoCacher *GetCacher() const { return auto_cacher_; }
|
||||
|
||||
public slots:
|
||||
void Play(bool in_to_out_only);
|
||||
|
||||
|
||||
@@ -361,10 +361,7 @@ void ViewerDisplayWidget::OnPaint()
|
||||
// We only draw if we have a pipeline
|
||||
if (push_mode_ != kPushNull) {
|
||||
// Draw texture through color transform
|
||||
int device_width = width() * devicePixelRatioF();
|
||||
int device_height = height() * devicePixelRatioF();
|
||||
VideoParams::Format device_format = static_cast<VideoParams::Format>(OLIVE_CONFIG("OfflinePixelFormat").toInt());
|
||||
VideoParams device_params(device_width, device_height, device_format, VideoParams::kInternalChannelCount);
|
||||
VideoParams device_params = GetViewportParams();
|
||||
|
||||
if (push_mode_ == kPushBlank) {
|
||||
if (blank_shader_.isNull()) {
|
||||
|
||||
@@ -87,6 +87,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
param_panel_ = new ParamPanel(this);
|
||||
curve_panel_ = new CurvePanel(this);
|
||||
sequence_viewer_panel_ = new SequenceViewerPanel(this);
|
||||
multicam_panel_ = new MulticamPanel(sequence_viewer_panel_, this);
|
||||
pixel_sampler_panel_ = new PixelSamplerPanel(this);
|
||||
AppendProjectPanel();
|
||||
tool_panel_ = new ToolPanel(this);
|
||||
@@ -107,10 +108,13 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
// Connect time signals together
|
||||
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTime);
|
||||
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, curve_panel_, &NodeTablePanel::SetTime);
|
||||
connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, multicam_panel_, &MulticamPanel::SetTime);
|
||||
connect(param_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime);
|
||||
connect(param_panel_, &ParamPanel::TimeChanged, curve_panel_, &NodeTablePanel::SetTime);
|
||||
connect(param_panel_, &ParamPanel::TimeChanged, multicam_panel_, &MulticamPanel::SetTime);
|
||||
connect(curve_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime);
|
||||
connect(curve_panel_, &ParamPanel::TimeChanged, param_panel_, &NodeTablePanel::SetTime);
|
||||
connect(curve_panel_, &ParamPanel::TimeChanged, multicam_panel_, &MulticamPanel::SetTime);
|
||||
|
||||
connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged);
|
||||
|
||||
@@ -489,6 +493,20 @@ void MainWindow::TimelinePanelSelectionChanged(const QVector<Block *> &blocks)
|
||||
|
||||
if (PanelManager::instance()->CurrentlyFocused(false) == panel) {
|
||||
UpdateNodePanelContextFromTimelinePanel(panel);
|
||||
|
||||
bool found = false;
|
||||
for (Block *b : blocks) {
|
||||
if (ClipBlock *c = dynamic_cast<ClipBlock*>(b)) {
|
||||
if (MultiCamNode *m = dynamic_cast<MultiCamNode*>(c->GetConnectedOutput(c->kBufferIn))) {
|
||||
multicam_panel_->SetNode(m);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
multicam_panel_->SetNode(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,6 +617,7 @@ TimelinePanel* MainWindow::AppendTimelinePanel()
|
||||
connect(panel, &TimelinePanel::TimeChanged, curve_panel_, &ParamPanel::SetTime);
|
||||
connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTime);
|
||||
connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime);
|
||||
connect(panel, &TimelinePanel::TimeChanged, multicam_panel_, &MulticamPanel::SetTime);
|
||||
connect(panel, &TimelinePanel::RequestCaptureStart, sequence_viewer_panel_, &SequenceViewerPanel::StartCapture);
|
||||
connect(panel, &TimelinePanel::BlockSelectionChanged, this, &MainWindow::TimelinePanelSelectionChanged);
|
||||
connect(panel, &TimelinePanel::RevealViewerInProject, this, &MainWindow::RevealViewerInProject);
|
||||
@@ -837,6 +856,10 @@ void MainWindow::SetDefaultLayout()
|
||||
scope_panel_->setFloating(true);
|
||||
addDockWidget(Qt::TopDockWidgetArea, scope_panel_);
|
||||
|
||||
multicam_panel_->hide();
|
||||
multicam_panel_->setFloating(true);
|
||||
addDockWidget(Qt::TopDockWidgetArea, multicam_panel_);
|
||||
|
||||
sequence_viewer_panel_->show();
|
||||
addDockWidget(Qt::TopDockWidgetArea, sequence_viewer_panel_);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "mainwindowlayoutinfo.h"
|
||||
#include "node/project/project.h"
|
||||
#include "panel/multicam/multicampanel.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "panel/audiomonitor/audiomonitor.h"
|
||||
#include "panel/curve/curve.h"
|
||||
@@ -163,6 +164,7 @@ private:
|
||||
PixelSamplerPanel* pixel_sampler_panel_;
|
||||
ScopePanel* scope_panel_;
|
||||
QMap<ViewerOutput*, ViewerPanel*> viewer_panels_;
|
||||
MulticamPanel *multicam_panel_;
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
unsigned int taskbar_btn_id_;
|
||||
|
||||
Reference in New Issue
Block a user