multicam: implement clip splitting

This commit is contained in:
itsmattkc
2022-09-28 17:57:09 -07:00
parent 80a900d105
commit 74a1ec1479
8 changed files with 129 additions and 52 deletions
+10
View File
@@ -541,4 +541,14 @@ TimeRange ClipBlock::media_range() const
return InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
}
MultiCamNode *ClipBlock::FindMulticam()
{
auto v = FindInputNodesConnectedToInput<MultiCamNode>(NodeInput(this, kBufferIn));
if (v.empty()) {
return nullptr;
} else {
return v.first();
}
}
}
+3
View File
@@ -24,6 +24,7 @@
#include "audio/audiovisualwaveform.h"
#include "codec/decoder.h"
#include "node/block/block.h"
#include "node/input/multicam/multicamnode.h"
#include "node/output/track/track.h"
namespace olive {
@@ -197,6 +198,8 @@ public:
SetStandardValue(kLoopModeInput, int(l));
}
MultiCamNode *FindMulticam();
static const QString kBufferIn;
static const QString kMediaInInput;
static const QString kSpeedInput;
+5
View File
@@ -17,6 +17,11 @@ public:
widget_->SetMulticamNode(n);
}
void SetClip(ClipBlock* clip)
{
widget_->SetClip(clip);
}
protected:
virtual void Retranslate() override;
+37 -2
View File
@@ -20,6 +20,7 @@
#include "multicamwidget.h"
#include "widget/nodeparamview/nodeparamviewundo.h"
#include "widget/timelinewidget/undo/timelineundosplit.h"
namespace olive {
@@ -27,7 +28,8 @@ namespace olive {
MulticamWidget::MulticamWidget(QWidget *parent) :
super{new MulticamDisplay(), parent},
node_(nullptr)
node_(nullptr),
clip_(nullptr)
{
auto_cacher()->SetMulticamMode(true);
auto_cacher()->SetIgnoreCacheRequests(true);
@@ -41,6 +43,11 @@ void MulticamWidget::SetMulticamNode(MultiCamNode *n)
static_cast<MulticamDisplay*>(display_widget())->SetMulticamNode(n);
}
void MulticamWidget::SetClip(ClipBlock *clip)
{
clip_ = clip;
}
RenderTicketPtr MulticamWidget::GetSingleFrame(const rational &t, bool dry)
{
if (node_) {
@@ -73,8 +80,36 @@ void MulticamWidget::DisplayClicked(const QPoint &p)
int r = click.y() / (height/multi);
MultiUndoCommand *command = new MultiUndoCommand();
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(node_, node_->kCurrentInput)), node_->RowsColsToIndex(r, c, rows, cols)));
const bool enable_split = true;
MultiCamNode *cam = node_;
ClipBlock *clip = clip_;
if (clip_ && enable_split && clip_->in() < GetTime() && clip_->out() > GetTime()) {
QVector<Block*> blocks;
blocks.append(clip_);
blocks.append(clip_->block_links());
auto split = new BlockSplitPreservingLinksCommand(blocks, {GetTime()});
split->redo_now();
command->add_child(split);
clip = static_cast<ClipBlock*>(split->GetSplit(clip_, 0));
cam = clip->FindMulticam();
}
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(cam, cam->kCurrentInput)), cam->RowsColsToIndex(r, c, rows, cols)));
Core::instance()->undo_stack()->push(command);
if (cam != node_) {
SetMulticamNode(cam);
}
if (clip != clip_) {
SetClip(clip);
}
}
}
+4
View File
@@ -35,12 +35,16 @@ public:
void SetMulticamNode(MultiCamNode *n);
void SetClip(ClipBlock *clip);
protected:
virtual RenderTicketPtr GetSingleFrame(const rational &t, bool dry = false) override;
private:
MultiCamNode *node_;
ClipBlock *clip_;
private slots:
void DisplayClicked(const QPoint &p);
@@ -97,62 +97,68 @@ void BlockSplitCommand::undo()
//
// BlockSplitPreservingLinksCommand
//
void BlockSplitPreservingLinksCommand::redo()
Block *BlockSplitPreservingLinksCommand::GetSplit(Block *original, int time_index) const
{
if (commands_.isEmpty()) {
QVector< QVector<Block*> > split_blocks(times_.size());
for (int i=0;i<times_.size();i++) {
const rational& time = times_.at(i);
// FIXME: I realize this isn't going to work if the times aren't ordered. I'm lazy so rather
// than writing in a sorting algorithm here, I'll just put an assert as a reminder
// if this ever becomes an issue.
Q_ASSERT(i == 0 || time > times_.at(i-1));
QVector<Block*> splits(blocks_.size());
for (int j=0;j<blocks_.size();j++) {
Block* b = blocks_.at(j);
if (b->in() < time && b->out() > time) {
BlockSplitCommand* split_command = new BlockSplitCommand(b, time);
split_command->redo_now();
splits.replace(j, split_command->new_block());
commands_.append(split_command);
} else {
splits.replace(j, nullptr);
}
}
split_blocks.replace(i, splits);
if (time_index >= 0 && time_index < times_.size()) {
int original_index = blocks_.indexOf(original);
if (original_index != -1) {
return splits_.at(time_index).at(original_index);
}
}
// Now that we've determined all the splits, we can relink everything
for (int i=0;i<blocks_.size();i++) {
Block* a = blocks_.at(i);
return nullptr;
}
for (int j=0;j<blocks_.size();j++) {
if (i == j) {
continue;
}
void BlockSplitPreservingLinksCommand::prepare()
{
splits_.resize(times_.size());
Block* b = blocks_.at(j);
for (int i=0;i<times_.size();i++) {
const rational& time = times_.at(i);
if (Block::AreLinked(a, b)) {
// These blocks are linked, ensure all the splits are linked too
// FIXME: I realize this isn't going to work if the times aren't ordered. I'm lazy so rather
// than writing in a sorting algorithm here, I'll just put an assert as a reminder
// if this ever becomes an issue.
Q_ASSERT(i == 0 || time > times_.at(i-1));
foreach (const QVector<Block*>& split_list, split_blocks) {
NodeLinkCommand* blc = new NodeLinkCommand(split_list.at(i), split_list.at(j), true);
blc->redo_now();
commands_.append(blc);
}
}
QVector<Block*> splits(blocks_.size());
for (int j=0;j<blocks_.size();j++) {
Block* b = blocks_.at(j);
if (b->in() < time && b->out() > time) {
BlockSplitCommand* split_command = new BlockSplitCommand(b, time);
split_command->redo_now();
splits.replace(j, split_command->new_block());
commands_.append(split_command);
} else {
splits.replace(j, nullptr);
}
}
} else {
for (int i=0; i<commands_.size(); i++) {
commands_.at(i)->redo_now();
splits_.replace(i, splits);
}
// Now that we've determined all the splits, we can relink everything
for (int i=0;i<blocks_.size();i++) {
Block* a = blocks_.at(i);
for (int j=0;j<blocks_.size();j++) {
if (i == j) {
continue;
}
Block* b = blocks_.at(j);
if (Block::AreLinked(a, b)) {
// These blocks are linked, ensure all the splits are linked too
foreach (const QVector<Block*>& split_list, splits_) {
NodeLinkCommand* blc = new NodeLinkCommand(split_list.at(i), split_list.at(j), true);
blc->redo_now();
commands_.append(blc);
}
}
}
}
}
@@ -91,8 +91,17 @@ public:
return blocks_.first()->project();
}
Block *GetSplit(Block *original, int time_index) const;
protected:
virtual void redo() override;
virtual void prepare() override;
virtual void redo() override
{
for (int i=0; i<commands_.size(); i++) {
commands_.at(i)->redo_now();
}
}
virtual void undo() override
{
@@ -108,6 +117,8 @@ private:
QVector<UndoCommand*> commands_;
QVector< QVector<Block*> > splits_;
};
class TrackSplitAtTimeCommand : public UndoCommand {
+5 -2
View File
@@ -498,11 +498,12 @@ void MainWindow::TimelinePanelSelectionChanged(const QVector<Block *> &blocks)
if (PanelManager::instance()->CurrentlyFocused(false) == panel) {
UpdateNodePanelContextFromTimelinePanel(panel);
ClipBlock *clip = nullptr;
MultiCamNode *multicam = nullptr;
for (Block *b : blocks) {
if (ClipBlock *c = dynamic_cast<ClipBlock*>(b)) {
if ((multicam = dynamic_cast<MultiCamNode*>(c->GetConnectedOutput(c->kBufferIn)))) {
if ((clip = dynamic_cast<ClipBlock*>(b))) {
if ((multicam = clip->FindMulticam())) {
break;
}
}
@@ -510,10 +511,12 @@ void MainWindow::TimelinePanelSelectionChanged(const QVector<Block *> &blocks)
if (multicam) {
multicam_panel_->SetMulticamNode(multicam);
multicam_panel_->SetClip(clip);
multicam_panel_->ConnectViewerNode(panel->GetConnectedViewer());
} else {
multicam_panel_->ConnectViewerNode(nullptr);
multicam_panel_->SetMulticamNode(nullptr);
multicam_panel_->SetClip(nullptr);
}
}
}