multicam: implement clip splitting
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,6 +17,11 @@ public:
|
||||
widget_->SetMulticamNode(n);
|
||||
}
|
||||
|
||||
void SetClip(ClipBlock* clip)
|
||||
{
|
||||
widget_->SetClip(clip);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Retranslate() override;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user