start implementation of multicam panel

This commit is contained in:
itsmattkc
2022-09-21 13:16:19 -07:00
parent 2dc2edf3e0
commit 487cd166d7
21 changed files with 347 additions and 25 deletions
+1
View File
@@ -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)
+22
View File
@@ -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
)
+26
View File
@@ -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"));
}
}
+37
View File
@@ -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
+2 -5
View File
@@ -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;
+19 -5
View File
@@ -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_) {
+1
View File
@@ -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);
+7
View File
@@ -0,0 +1,7 @@
uniform int rows;
uniform int cols;
void main(void)
{
}
+1
View File
@@ -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) {
+4 -1
View File
@@ -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
+22
View File
@@ -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
)
+104
View File
@@ -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;
}
}
+59
View File
@@ -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
+2 -2
View File
@@ -47,10 +47,10 @@ void HistogramScope::OnInit()
void HistogramScope::OnDestroy()
{
super::OnDestroy();
pipeline_secondary_.clear();
texture_row_sums_ = nullptr;
super::OnDestroy();
}
ShaderCode HistogramScope::GenerateShaderCode()
+3 -5
View File
@@ -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();
}
}
+1 -3
View File
@@ -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);
+2
View File
@@ -100,6 +100,8 @@ public:
enable_audio_scrubbing_ = e;
}
PreviewAutoCacher *GetCacher() const { return auto_cacher_; }
public slots:
void Play(bool in_to_out_only);
+1 -4
View File
@@ -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()) {
+23
View File
@@ -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_);
+2
View File
@@ -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_;