began new infrastructure
Yep, this is another one of my "famous" sweeping rewrites. Expect things to break. Goals for this are: - Greatly simplify node connections (particularly with arrays) so the code requires less maintenance/is more stable - Redesign node structure to address issues where UI would stall for lengthy periods of time - Less reliance on shared ptrs/greater reliance on QObject system for inheritance/memory management - General code cleanup and improvements
This commit is contained in:
@@ -16,9 +16,9 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/output/track/track.h
|
||||
node/output/track/track.cpp
|
||||
node/output/track/tracklist.h
|
||||
node/output/track/track.h
|
||||
node/output/track/tracklist.cpp
|
||||
node/output/track/tracklist.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
+146
-129
@@ -38,18 +38,18 @@ TrackOutput::TrackOutput() :
|
||||
index_(-1),
|
||||
locked_(false)
|
||||
{
|
||||
block_input_ = new NodeInputArray("block_in", NodeParam::kAny);
|
||||
block_input_->set_is_keyframable(false);
|
||||
AddInput(block_input_);
|
||||
connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected);
|
||||
connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected);
|
||||
disconnect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::InputConnectionChanged);
|
||||
disconnect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::InputConnectionChanged);
|
||||
block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone);
|
||||
block_input_->SetKeyframable(false);
|
||||
connect(block_input_, &NodeInput::InputConnected, this, &TrackOutput::BlockConnected);
|
||||
connect(block_input_, &NodeInput::InputDisconnected, this, &TrackOutput::BlockDisconnected);
|
||||
|
||||
muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean);
|
||||
muted_input_->set_is_keyframable(false);
|
||||
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
|
||||
// on our end
|
||||
IgnoreConnectionSignalsFrom(block_input_);
|
||||
|
||||
muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean);
|
||||
muted_input_->SetKeyframable(false);
|
||||
connect(muted_input_, &NodeInput::ValueChanged, this, &TrackOutput::MutedInputValueChanged);
|
||||
AddInput(muted_input_);
|
||||
|
||||
// Set default height
|
||||
track_height_ = kTrackHeightDefault;
|
||||
@@ -245,21 +245,16 @@ QList<Block *> TrackOutput::BlocksAtTimeRange(const TimeRange &range) const
|
||||
return list;
|
||||
}
|
||||
|
||||
const QList<Block *> &TrackOutput::Blocks() const
|
||||
{
|
||||
return block_cache_;
|
||||
}
|
||||
|
||||
void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
void TrackOutput::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
||||
{
|
||||
TimeRange limited;
|
||||
|
||||
if (block_input_->sub_params().contains(from)
|
||||
&& from->get_connected_node()
|
||||
&& from->get_connected_node()->IsBlock()) {
|
||||
// Limit the range signal to the corresponding block
|
||||
Block* b = static_cast<Block*>(from->get_connected_node());
|
||||
Block* b;
|
||||
|
||||
if (from.input == block_input_
|
||||
&& from.element >= 0
|
||||
&& (b = dynamic_cast<Block*>(from.input->GetConnectedNode(from.element)))) {
|
||||
// Limit the range signal to the corresponding block
|
||||
if (range.out() <= b->in() || range.in() >= b->out()) {
|
||||
return;
|
||||
}
|
||||
@@ -269,7 +264,7 @@ void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeI
|
||||
limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), track_length()));
|
||||
}
|
||||
|
||||
Node::InvalidateCache(limited, from, source);
|
||||
Node::InvalidateCache(limited, from);
|
||||
}
|
||||
|
||||
void TrackOutput::InsertBlockBefore(Block* block, Block* after)
|
||||
@@ -294,13 +289,13 @@ void TrackOutput::PrependBlock(Block *block)
|
||||
{
|
||||
BeginOperation();
|
||||
|
||||
block_input_->Prepend();
|
||||
NodeParam::ConnectEdge(block->output(), block_input_->First());
|
||||
block_input_->ArrayPrepend();
|
||||
Node::ConnectEdge(block, block_input_, 0);
|
||||
|
||||
EndOperation();
|
||||
|
||||
// Everything has shifted at this point
|
||||
Node::InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_);
|
||||
Node::InvalidateCache(TimeRange(0, track_length()), InputConnection());
|
||||
}
|
||||
|
||||
void TrackOutput::InsertBlockAtIndex(Block *block, int index)
|
||||
@@ -308,26 +303,25 @@ void TrackOutput::InsertBlockAtIndex(Block *block, int index)
|
||||
BeginOperation();
|
||||
|
||||
int insert_index = GetInputIndexFromCacheIndex(index);
|
||||
block_input_->InsertAt(insert_index);
|
||||
NodeParam::ConnectEdge(block->output(),
|
||||
block_input_->At(insert_index));
|
||||
block_input_->ArrayInsert(insert_index);
|
||||
Node::ConnectEdge(block, block_input_, insert_index);
|
||||
|
||||
EndOperation();
|
||||
|
||||
Node::InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_);
|
||||
Node::InvalidateCache(TimeRange(block->in(), track_length()));
|
||||
}
|
||||
|
||||
void TrackOutput::AppendBlock(Block *block)
|
||||
{
|
||||
BeginOperation();
|
||||
|
||||
block_input_->Append();
|
||||
NodeParam::ConnectEdge(block->output(), block_input_->Last());
|
||||
block_input_->ArrayAppend();
|
||||
Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1);
|
||||
|
||||
EndOperation();
|
||||
|
||||
// Invalidate area that block was added to
|
||||
Node::InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_);
|
||||
Node::InvalidateCache(TimeRange(block->in(), track_length()));
|
||||
}
|
||||
|
||||
void TrackOutput::RippleRemoveBlock(Block *block)
|
||||
@@ -337,11 +331,11 @@ void TrackOutput::RippleRemoveBlock(Block *block)
|
||||
rational remove_in = block->in();
|
||||
rational remove_out = block->out();
|
||||
|
||||
block_input_->RemoveAt(GetInputIndexFromCacheIndex(block));
|
||||
block_input_->ArrayRemove(GetInputIndexFromCacheIndex(block));
|
||||
|
||||
EndOperation();
|
||||
|
||||
Node::InvalidateCache(TimeRange(remove_in, qMax(track_length(), remove_out)), block_input_, block_input_);
|
||||
Node::InvalidateCache(TimeRange(remove_in, qMax(track_length(), remove_out)));
|
||||
}
|
||||
|
||||
void TrackOutput::ReplaceBlock(Block *old, Block *replace)
|
||||
@@ -350,30 +344,26 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace)
|
||||
|
||||
int index_of_old_block = GetInputIndexFromCacheIndex(old);
|
||||
|
||||
NodeParam::DisconnectEdge(old->output(),
|
||||
block_input_->At(index_of_old_block));
|
||||
DisconnectEdge(old, block_input_, index_of_old_block);
|
||||
|
||||
NodeParam::ConnectEdge(replace->output(),
|
||||
block_input_->At(index_of_old_block));
|
||||
ConnectEdge(replace, block_input_, index_of_old_block);
|
||||
|
||||
EndOperation();
|
||||
|
||||
if (old->length() == replace->length()) {
|
||||
Node::InvalidateCache(TimeRange(replace->in(), replace->out()), block_input_, block_input_);
|
||||
Node::InvalidateCache(TimeRange(replace->in(), replace->out()));
|
||||
} else {
|
||||
Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), block_input_, block_input_);
|
||||
Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
TrackOutput *TrackOutput::TrackFromBlock(const Block *block)
|
||||
{
|
||||
NodeOutput* output = block->output();
|
||||
foreach (const InputConnection& conn, block->edges()) {
|
||||
TrackOutput* track = dynamic_cast<TrackOutput*>(conn.input->parent());
|
||||
|
||||
foreach (NodeEdgePtr edge, output->edges()) {
|
||||
Node* n = edge->input()->parentNode();
|
||||
|
||||
if (n->IsTrack()) {
|
||||
return static_cast<TrackOutput*>(n);
|
||||
if (track) {
|
||||
return track;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,11 +375,6 @@ const rational &TrackOutput::track_length() const
|
||||
return track_length_;
|
||||
}
|
||||
|
||||
bool TrackOutput::IsTrack() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString TrackOutput::GetDefaultTrackName(Timeline::TrackType type, int index)
|
||||
{
|
||||
// Starts tracks at 1 rather than 0
|
||||
@@ -409,7 +394,7 @@ QString TrackOutput::GetDefaultTrackName(Timeline::TrackType type, int index)
|
||||
|
||||
bool TrackOutput::IsMuted() const
|
||||
{
|
||||
return muted_input_->get_standard_value().toBool();
|
||||
return muted_input_->GetStandardValue().toBool();
|
||||
}
|
||||
|
||||
bool TrackOutput::IsLocked() const
|
||||
@@ -417,7 +402,7 @@ bool TrackOutput::IsLocked() const
|
||||
return locked_;
|
||||
}
|
||||
|
||||
NodeInputArray *TrackOutput::block_input() const
|
||||
NodeInput *TrackOutput::block_input() const
|
||||
{
|
||||
return block_input_;
|
||||
}
|
||||
@@ -434,8 +419,8 @@ void TrackOutput::Hash(QCryptographicHash &hash, const rational &time) const
|
||||
|
||||
void TrackOutput::SetMuted(bool e)
|
||||
{
|
||||
muted_input_->set_standard_value(e);
|
||||
Node::InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_);
|
||||
muted_input_->SetStandardValue(e);
|
||||
Node::InvalidateCache(TimeRange(0, track_length()));
|
||||
}
|
||||
|
||||
void TrackOutput::SetLocked(bool e)
|
||||
@@ -472,8 +457,8 @@ int TrackOutput::GetInputIndexFromCacheIndex(int cache_index)
|
||||
|
||||
int TrackOutput::GetInputIndexFromCacheIndex(Block *block)
|
||||
{
|
||||
for (int i=0; i<block_input_->GetSize(); i++) {
|
||||
if (block_input_->At(i)->get_connected_node() == block) {
|
||||
for (int i=0; i<block_input_->ArraySize(); i++) {
|
||||
if (block_input_->GetConnectedNode(i) == block) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -490,96 +475,128 @@ void TrackOutput::SetLengthInternal(const rational &r, bool invalidate)
|
||||
emit TrackLengthChanged();
|
||||
|
||||
if (invalidate) {
|
||||
Node::InvalidateCache(invalidate_range,
|
||||
block_input_,
|
||||
block_input_);
|
||||
Node::InvalidateCache(invalidate_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::BlockConnected(NodeEdgePtr edge)
|
||||
void TrackOutput::BlockConnected(Node *node, int element)
|
||||
{
|
||||
QList<Block*> new_block_list;
|
||||
|
||||
foreach (NodeInput* i, block_input_->sub_params()) {
|
||||
Node* connected = i->get_connected_node();
|
||||
|
||||
if (connected
|
||||
&& connected->IsBlock()
|
||||
&& !new_block_list.contains(static_cast<Block*>(connected))) {
|
||||
Block* b = static_cast<Block*>(connected);
|
||||
|
||||
if (!new_block_list.isEmpty()) {
|
||||
new_block_list.last()->set_next(b);
|
||||
b->set_previous(new_block_list.last());
|
||||
}
|
||||
|
||||
new_block_list.append(b);
|
||||
|
||||
if (!block_cache_.contains(b)) {
|
||||
// Make connections to this block
|
||||
connect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
|
||||
|
||||
emit BlockAdded(b);
|
||||
}
|
||||
}
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InputConnectionChanged(node, element);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!new_block_list.isEmpty()) {
|
||||
new_block_list.first()->set_previous(nullptr);
|
||||
new_block_list.last()->set_next(nullptr);
|
||||
// Check if a block was connected, if not, ignore
|
||||
Block* block = dynamic_cast<Block*>(node);
|
||||
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
int new_index;
|
||||
Block *previous = nullptr, *next = nullptr;
|
||||
|
||||
for (new_index = 0; new_index < block_cache_.size(); new_index++) {
|
||||
if (block_cache_.at(new_index) != new_block_list.at(new_index)) {
|
||||
for (int i=element-1; i>=0; i--) {
|
||||
// Find previous block
|
||||
previous = dynamic_cast<Block*>(block_input_->GetConnectedNode(i));
|
||||
|
||||
if (previous) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
block_cache_ = new_block_list;
|
||||
// Find cache index
|
||||
int cache_index;
|
||||
if (previous) {
|
||||
// Insert block just after the previous block we found
|
||||
cache_index = block_cache_.indexOf(previous) + 1;
|
||||
|
||||
UpdateInOutFrom(new_index);
|
||||
// Use current previous' next as our next
|
||||
next = previous->next();
|
||||
} else {
|
||||
// Didn't find a previous, so insert block at 0 (prepend it)
|
||||
cache_index = 0;
|
||||
|
||||
InputConnectionChanged(edge);
|
||||
}
|
||||
|
||||
void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
|
||||
{
|
||||
Block* b = static_cast<Block*>(edge->output_node());
|
||||
|
||||
if (block_cache_.contains(b)) {
|
||||
block_cache_.removeOne(b);
|
||||
|
||||
Block* previous = b->previous();
|
||||
Block* next = b->next();
|
||||
|
||||
if (previous) {
|
||||
previous->set_next(next);
|
||||
if (!block_cache_.isEmpty()) {
|
||||
next = block_cache_.first();
|
||||
}
|
||||
|
||||
if (next) {
|
||||
next->set_previous(previous);
|
||||
}
|
||||
|
||||
b->set_previous(nullptr);
|
||||
b->set_next(nullptr);
|
||||
|
||||
if (next) {
|
||||
UpdateInOutFrom(block_cache_.indexOf(next));
|
||||
} else if (block_cache_.isEmpty()) {
|
||||
SetLengthInternal(rational());
|
||||
} else {
|
||||
SetLengthInternal(block_cache_.last()->out());
|
||||
}
|
||||
|
||||
disconnect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
|
||||
|
||||
emit BlockRemoved(b);
|
||||
}
|
||||
|
||||
InputConnectionChanged(edge);
|
||||
// Insert at index
|
||||
block_cache_.insert(cache_index, block);
|
||||
|
||||
// Update previous/next
|
||||
if (previous) {
|
||||
previous->set_next(block);
|
||||
block->set_previous(previous);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
block->set_next(next);
|
||||
next->set_previous(block);
|
||||
}
|
||||
|
||||
// Update ins/outs
|
||||
UpdateInOutFrom(cache_index);
|
||||
|
||||
// Connect to the block
|
||||
connect(block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
|
||||
|
||||
// Invalidate cache now that block should have an in point
|
||||
Node::InvalidateCache(TimeRange(block->in(), track_length()));
|
||||
|
||||
// Emit block added signal
|
||||
emit BlockAdded(block);
|
||||
}
|
||||
|
||||
void TrackOutput::BlockDisconnected(Node* node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InputConnectionChanged(node, element);
|
||||
return;
|
||||
}
|
||||
|
||||
Block* b = dynamic_cast<Block*>(node);
|
||||
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeRange invalidate_range(b->in(), track_length());
|
||||
|
||||
// FIXME: What happens if a user connects the same block twice? This must be addressed in the
|
||||
// upcoming timeline rewrite.
|
||||
block_cache_.removeOne(b);
|
||||
|
||||
Block* previous = b->previous();
|
||||
Block* next = b->next();
|
||||
|
||||
if (previous) {
|
||||
previous->set_next(next);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
next->set_previous(previous);
|
||||
}
|
||||
|
||||
b->set_previous(nullptr);
|
||||
b->set_next(nullptr);
|
||||
|
||||
if (next) {
|
||||
UpdateInOutFrom(block_cache_.indexOf(next));
|
||||
} else if (block_cache_.isEmpty()) {
|
||||
SetLengthInternal(rational());
|
||||
} else {
|
||||
SetLengthInternal(block_cache_.last()->out());
|
||||
}
|
||||
|
||||
disconnect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
|
||||
|
||||
emit BlockRemoved(b);
|
||||
|
||||
Node::InvalidateCache(invalidate_range);
|
||||
}
|
||||
|
||||
void TrackOutput::BlockLengthChanged()
|
||||
@@ -595,7 +612,7 @@ void TrackOutput::BlockLengthChanged()
|
||||
|
||||
TimeRange invalidate_region(qMin(old_out, new_out), track_length());
|
||||
|
||||
Node::InvalidateCache(invalidate_region, block_input_, block_input_);
|
||||
Node::InvalidateCache(invalidate_region);
|
||||
}
|
||||
|
||||
void TrackOutput::MutedInputValueChanged()
|
||||
|
||||
@@ -150,9 +150,12 @@ public:
|
||||
*/
|
||||
QList<Block*> BlocksAtTimeRange(const TimeRange& range) const;
|
||||
|
||||
const QList<Block *> &Blocks() const;
|
||||
const QList<Block *> &Blocks() const
|
||||
{
|
||||
return block_cache_;
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
@@ -200,15 +203,13 @@ public:
|
||||
|
||||
const rational& track_length() const;
|
||||
|
||||
virtual bool IsTrack() const override;
|
||||
|
||||
static QString GetDefaultTrackName(Timeline::TrackType type, int index);
|
||||
|
||||
bool IsMuted() const;
|
||||
|
||||
bool IsLocked() const;
|
||||
|
||||
NodeInputArray* block_input() const;
|
||||
NodeInput* block_input() const;
|
||||
|
||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
||||
|
||||
@@ -277,7 +278,7 @@ private:
|
||||
|
||||
QList<Block*> block_cache_;
|
||||
|
||||
NodeInputArray* block_input_;
|
||||
NodeInput* block_input_;
|
||||
|
||||
NodeInput* muted_input_;
|
||||
|
||||
@@ -294,9 +295,9 @@ private:
|
||||
AudioVisualWaveform waveform_;
|
||||
|
||||
private slots:
|
||||
void BlockConnected(NodeEdgePtr edge);
|
||||
void BlockConnected(Node* node, int element);
|
||||
|
||||
void BlockDisconnected(NodeEdgePtr edge);
|
||||
void BlockDisconnected(Node* node, int element);
|
||||
|
||||
void BlockLengthChanged();
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, NodeInputArray *track_input) :
|
||||
TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, NodeInput *track_input) :
|
||||
QObject(parent),
|
||||
track_input_(track_input),
|
||||
type_(type)
|
||||
{
|
||||
connect(track_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected);
|
||||
connect(track_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected);
|
||||
connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected);
|
||||
connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected);
|
||||
}
|
||||
|
||||
const Timeline::TrackType &TrackList::type() const
|
||||
@@ -75,173 +75,91 @@ int TrackList::GetTrackCount() const
|
||||
return track_cache_.size();
|
||||
}
|
||||
|
||||
TrackOutput* TrackList::AddTrack()
|
||||
void TrackList::TrackConnected(Node *node, int element)
|
||||
{
|
||||
TrackOutput* track = new TrackOutput();
|
||||
GetParentGraph()->AddNode(track);
|
||||
|
||||
track_input_->Append();
|
||||
|
||||
// Connect this track directly to this output
|
||||
NodeParam::ConnectEdge(track->output(),
|
||||
track_input_->At(track_input_->GetSize() - 1));
|
||||
|
||||
// Auto-merge with previous track
|
||||
if (track_input_->GetSize() > 1) {
|
||||
TrackOutput* last_track = nullptr;
|
||||
|
||||
for (int i=track_cache_.size()-1;i>=0;i--) {
|
||||
TrackOutput* test_track = track_cache_.at(i);
|
||||
|
||||
if (test_track && test_track != track) {
|
||||
last_track = test_track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_track && last_track->output()->is_connected()) {
|
||||
foreach (NodeEdgePtr edge, last_track->output()->edges()) {
|
||||
if (!track_input_->ContainsSubParameter(edge->input())) {
|
||||
switch (type_) {
|
||||
case Timeline::kTrackTypeVideo:
|
||||
{
|
||||
MergeNode* blend = new MergeNode();
|
||||
GetParentGraph()->AddNode(blend);
|
||||
|
||||
NodeParam::ConnectEdge(track->output(), blend->blend_in());
|
||||
NodeParam::ConnectEdge(last_track->output(), blend->base_in());
|
||||
NodeParam::ConnectEdge(blend->output(), edge->input());
|
||||
break;
|
||||
}
|
||||
case Timeline::kTrackTypeAudio:
|
||||
{
|
||||
MathNode* add = new MathNode();
|
||||
GetParentGraph()->AddNode(add);
|
||||
|
||||
NodeParam::ConnectEdge(track->output(), add->param_a_in());
|
||||
NodeParam::ConnectEdge(last_track->output(), add->param_b_in());
|
||||
NodeParam::ConnectEdge(add->output(), edge->input());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void TrackList::RemoveTrack(QObject* new_parent)
|
||||
{
|
||||
if (track_cache_.isEmpty()) {
|
||||
if (element == -1) {
|
||||
// FIXME: Should probably merge into Viewer so we can actually do this
|
||||
//static_cast<ViewerOutput*>(parent())->InputConnectionChanged(node, element);
|
||||
return;
|
||||
}
|
||||
|
||||
TrackOutput* track = track_cache_.last();
|
||||
TrackOutput* track = dynamic_cast<TrackOutput*>(node);
|
||||
|
||||
GetParentGraph()->TakeNode(track);
|
||||
|
||||
if (!new_parent) {
|
||||
delete track;
|
||||
} else {
|
||||
track->setParent(new_parent);
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
track_input_->RemoveLast();
|
||||
}
|
||||
// Find "real" index
|
||||
TrackOutput* next = nullptr;
|
||||
for (int i=element+1; i<track_input_->ArraySize(); i++) {
|
||||
next = dynamic_cast<TrackOutput*>(track_input_->GetConnectedNode(i));
|
||||
|
||||
void TrackList::TrackConnected(NodeEdgePtr edge)
|
||||
{
|
||||
int input_index = track_input_->IndexOfSubParameter(edge->input());
|
||||
|
||||
Q_ASSERT(input_index >= 0);
|
||||
|
||||
Node* connected_node = edge->output()->parentNode();
|
||||
|
||||
if (connected_node->IsTrack()) {
|
||||
TrackOutput* connected_track = static_cast<TrackOutput*>(connected_node);
|
||||
|
||||
{
|
||||
// Find "real" index
|
||||
TrackOutput* next = nullptr;
|
||||
for (int i=input_index+1; i<track_input_->GetSize(); i++) {
|
||||
Node* that_track = track_input_->At(i)->get_connected_node();
|
||||
|
||||
if (that_track && that_track->IsTrack()) {
|
||||
next = static_cast<TrackOutput*>(that_track);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int track_index;
|
||||
|
||||
if (next) {
|
||||
// Insert track before "next"
|
||||
track_index = track_cache_.indexOf(next);
|
||||
track_cache_.insert(track_index, connected_track);
|
||||
} else {
|
||||
// No "next", this track must come at the end
|
||||
track_index = track_cache_.size();
|
||||
track_cache_.append(connected_track);
|
||||
}
|
||||
|
||||
// Update track indexes in the list (including this track)
|
||||
UpdateTrackIndexesFrom(track_index);
|
||||
if (next) {
|
||||
break;
|
||||
}
|
||||
|
||||
connect(connected_track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
|
||||
connect(connected_track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
|
||||
connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
connect(connected_track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot);
|
||||
|
||||
connected_track->set_track_type(type_);
|
||||
|
||||
emit TrackListChanged();
|
||||
|
||||
// This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine
|
||||
// the track's index
|
||||
emit TrackAdded(connected_track);
|
||||
|
||||
UpdateTotalLength();
|
||||
|
||||
}
|
||||
|
||||
int track_index;
|
||||
|
||||
if (next) {
|
||||
// Insert track before "next"
|
||||
track_index = track_cache_.indexOf(next);
|
||||
track_cache_.insert(track_index, track);
|
||||
} else {
|
||||
// No "next", this track must come at the end
|
||||
track_index = track_cache_.size();
|
||||
track_cache_.append(track);
|
||||
}
|
||||
|
||||
// Update track indexes in the list (including this track)
|
||||
UpdateTrackIndexesFrom(track_index);
|
||||
|
||||
connect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
|
||||
connect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
|
||||
connect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
connect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot);
|
||||
|
||||
track->set_track_type(type_);
|
||||
|
||||
emit TrackListChanged();
|
||||
|
||||
// This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine
|
||||
// the track's index
|
||||
emit TrackAdded(track);
|
||||
|
||||
UpdateTotalLength();
|
||||
}
|
||||
|
||||
void TrackList::TrackDisconnected(NodeEdgePtr edge)
|
||||
void TrackList::TrackDisconnected(Node *node, int element)
|
||||
{
|
||||
int track_index = track_input_->IndexOfSubParameter(edge->input());
|
||||
Q_UNUSED(element)
|
||||
|
||||
Q_ASSERT(track_index >= 0);
|
||||
TrackOutput* track = dynamic_cast<TrackOutput*>(node);
|
||||
|
||||
Node* connected_node = edge->output()->parentNode();
|
||||
|
||||
if (connected_node->IsTrack()) {
|
||||
TrackOutput* track = static_cast<TrackOutput*>(connected_node);
|
||||
|
||||
int index_of_track = track_cache_.indexOf(track);
|
||||
track_cache_.removeAt(index_of_track);
|
||||
|
||||
// Update indices for all subsequent tracks
|
||||
UpdateTrackIndexesFrom(index_of_track);
|
||||
|
||||
// Traverse through Tracks uncaching and disconnecting them
|
||||
emit TrackRemoved(track);
|
||||
|
||||
track->SetIndex(-1);
|
||||
track->set_track_type(Timeline::kTrackTypeNone);
|
||||
|
||||
disconnect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
|
||||
disconnect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
|
||||
disconnect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
disconnect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot);
|
||||
|
||||
emit TrackListChanged();
|
||||
|
||||
UpdateTotalLength();
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index_of_track = track_cache_.indexOf(track);
|
||||
track_cache_.removeAt(index_of_track);
|
||||
|
||||
// Update indices for all subsequent tracks
|
||||
UpdateTrackIndexesFrom(index_of_track);
|
||||
|
||||
// Traverse through Tracks uncaching and disconnecting them
|
||||
emit TrackRemoved(track);
|
||||
|
||||
track->SetIndex(-1);
|
||||
track->set_track_type(Timeline::kTrackTypeNone);
|
||||
|
||||
disconnect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
|
||||
disconnect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
|
||||
disconnect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
disconnect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot);
|
||||
|
||||
emit TrackListChanged();
|
||||
|
||||
UpdateTotalLength();
|
||||
}
|
||||
|
||||
void TrackList::UpdateTrackIndexesFrom(int index)
|
||||
|
||||
@@ -31,10 +31,11 @@ namespace olive {
|
||||
|
||||
class ViewerOutput;
|
||||
|
||||
class TrackList : public QObject {
|
||||
class TrackList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TrackList(ViewerOutput *parent, const Timeline::TrackType& type, NodeInputArray* track_input);
|
||||
TrackList(ViewerOutput *parent, const Timeline::TrackType& type, NodeInput* track_input);
|
||||
|
||||
const Timeline::TrackType& type() const;
|
||||
|
||||
@@ -42,16 +43,17 @@ public:
|
||||
|
||||
TrackOutput* GetTrackAt(int index) const;
|
||||
|
||||
TrackOutput *AddTrack();
|
||||
|
||||
void RemoveTrack(QObject *new_parent);
|
||||
|
||||
const rational& GetTotalLength() const;
|
||||
|
||||
int GetTrackCount() const;
|
||||
|
||||
NodeGraph* GetParentGraph() const;
|
||||
|
||||
NodeInput* track_input() const
|
||||
{
|
||||
return track_input_;
|
||||
}
|
||||
|
||||
signals:
|
||||
void BlockAdded(Block* block, int index);
|
||||
|
||||
@@ -75,7 +77,7 @@ private:
|
||||
*/
|
||||
QVector<TrackOutput*> track_cache_;
|
||||
|
||||
NodeInputArray* track_input_;
|
||||
NodeInput* track_input_;
|
||||
|
||||
rational total_length_;
|
||||
|
||||
@@ -85,12 +87,12 @@ private slots:
|
||||
/**
|
||||
* @brief Slot for when the track connection is added
|
||||
*/
|
||||
void TrackConnected(NodeEdgePtr edge);
|
||||
void TrackConnected(Node* node, int element);
|
||||
|
||||
/**
|
||||
* @brief Slot for when the track connection is removed
|
||||
*/
|
||||
void TrackDisconnected(NodeEdgePtr edge);
|
||||
void TrackDisconnected(Node* node, int element);
|
||||
|
||||
/**
|
||||
* @brief Slot for when a connected Track has added a Block so we can update the UI
|
||||
|
||||
@@ -29,11 +29,9 @@ ViewerOutput::ViewerOutput() :
|
||||
audio_playback_cache_(this),
|
||||
operation_stack_(0)
|
||||
{
|
||||
texture_input_ = new NodeInput("tex_in", NodeInput::kTexture);
|
||||
AddInput(texture_input_);
|
||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
||||
|
||||
samples_input_ = new NodeInput("samples_in", NodeInput::kSamples);
|
||||
AddInput(samples_input_);
|
||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
||||
|
||||
// Create TrackList instances
|
||||
track_inputs_.resize(Timeline::kTrackTypeCount);
|
||||
@@ -41,10 +39,8 @@ ViewerOutput::ViewerOutput() :
|
||||
|
||||
for (int i=0;i<Timeline::kTrackTypeCount;i++) {
|
||||
// Create track input
|
||||
NodeInputArray* track_input = new NodeInputArray(QStringLiteral("track_in_%1").arg(i), NodeParam::kAny);
|
||||
AddInput(track_input);
|
||||
disconnect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &ViewerOutput::InputConnectionChanged);
|
||||
disconnect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &ViewerOutput::InputConnectionChanged);
|
||||
NodeInput* track_input = new NodeInput(this, QStringLiteral("track_in_%1").arg(i), NodeValue::kNone);
|
||||
IgnoreConnectionSignalsFrom(track_input);
|
||||
track_inputs_.replace(i, track_input);
|
||||
|
||||
TrackList* list = new TrackList(this, static_cast<Timeline::TrackType>(i), track_input);
|
||||
@@ -52,7 +48,7 @@ ViewerOutput::ViewerOutput() :
|
||||
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
|
||||
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
|
||||
connect(list, &TrackList::BlockAdded, this, &ViewerOutput::TrackListAddedBlock);
|
||||
connect(list, &TrackList::BlockRemoved, this, &ViewerOutput::SignalBlockRemoved);
|
||||
connect(list, &TrackList::BlockRemoved, this, &ViewerOutput::BlockRemoved);
|
||||
connect(list, &TrackList::TrackAdded, this, &ViewerOutput::TrackListAddedTrack);
|
||||
connect(list, &TrackList::TrackRemoved, this, &ViewerOutput::TrackRemoved);
|
||||
connect(list, &TrackList::TrackHeightChanged, this, &ViewerOutput::TrackHeightChangedSlot);
|
||||
@@ -112,17 +108,15 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
||||
ShiftAudioCache(from, to);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
||||
{
|
||||
emit GraphChangedFrom(source);
|
||||
|
||||
if (operation_stack_ == 0) {
|
||||
if (from == texture_input_ || from == samples_input_) {
|
||||
if (from.input == texture_input_ || from.input == samples_input_) {
|
||||
TimeRange invalidated_range(qMax(rational(), range.in()),
|
||||
qMin(GetLength(), range.out()));
|
||||
|
||||
if (invalidated_range.in() != invalidated_range.out()) {
|
||||
if (from == texture_input_) {
|
||||
if (from.input == texture_input_) {
|
||||
video_frame_cache_.Invalidate(invalidated_range);
|
||||
} else {
|
||||
audio_playback_cache_.Invalidate(invalidated_range);
|
||||
@@ -133,7 +127,7 @@ void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, Node
|
||||
VerifyLength();
|
||||
}
|
||||
|
||||
Node::InvalidateCache(range, from, source);
|
||||
Node::InvalidateCache(range, from);
|
||||
}
|
||||
|
||||
void ViewerOutput::set_video_params(const VideoParams &video)
|
||||
@@ -215,26 +209,33 @@ void ViewerOutput::VerifyLength()
|
||||
|
||||
NodeTraverser traverser;
|
||||
|
||||
rational video_length;
|
||||
rational video_length, audio_length, subtitle_length;
|
||||
|
||||
if (texture_input_->is_connected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(texture_input_->get_connected_node(), 0, 0);
|
||||
video_length = t.Get(NodeParam::kNumber, "length").value<rational>();
|
||||
{
|
||||
video_length = track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength();
|
||||
|
||||
if (video_length.isNull() && texture_input_->IsConnected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0);
|
||||
video_length = t.Get(NodeValue::kRational, "length").value<rational>();
|
||||
}
|
||||
|
||||
video_frame_cache_.SetLength(video_length);
|
||||
}
|
||||
|
||||
rational audio_length;
|
||||
{
|
||||
audio_length = track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength();
|
||||
|
||||
if (samples_input_->is_connected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(samples_input_->get_connected_node(), 0, 0);
|
||||
audio_length = t.Get(NodeParam::kNumber, "length").value<rational>();
|
||||
if (audio_length.isNull() && samples_input_->IsConnected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0);
|
||||
audio_length = t.Get(NodeValue::kRational, "length").value<rational>();
|
||||
}
|
||||
|
||||
audio_playback_cache_.SetLength(audio_length);
|
||||
}
|
||||
|
||||
video_length = qMax(video_length, track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength());
|
||||
audio_length = qMax(audio_length, track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength());
|
||||
rational subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength();
|
||||
|
||||
video_frame_cache_.SetLength(video_length);
|
||||
audio_playback_cache_.SetLength(audio_length);
|
||||
{
|
||||
subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength();
|
||||
}
|
||||
|
||||
rational real_length = qMax(subtitle_length, qMax(video_length, audio_length));
|
||||
|
||||
@@ -283,27 +284,6 @@ void ViewerOutput::set_media_name(const QString &name)
|
||||
emit MediaNameChanged(media_name_);
|
||||
}
|
||||
|
||||
void ViewerOutput::SignalBlockAdded(Block *block, const TrackReference& track)
|
||||
{
|
||||
if (!operation_stack_) {
|
||||
emit BlockAdded(block, track);
|
||||
} else {
|
||||
cached_block_removed_.removeOne(block);
|
||||
cached_block_added_.insert(block, track);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::SignalBlockRemoved(Block *block)
|
||||
{
|
||||
if (!operation_stack_) {
|
||||
emit BlockRemoved({block});
|
||||
} else {
|
||||
// We keep track of all blocks that are removed, even if we don't end up signalling them
|
||||
cached_block_added_.remove(block);
|
||||
cached_block_removed_.append(block);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::BeginOperation()
|
||||
{
|
||||
operation_stack_++;
|
||||
@@ -315,23 +295,13 @@ void ViewerOutput::EndOperation()
|
||||
{
|
||||
operation_stack_--;
|
||||
|
||||
if (!operation_stack_) {
|
||||
for (auto it=cached_block_added_.cbegin(); it!=cached_block_added_.cend(); it++) {
|
||||
emit BlockAdded(it.key(), it.value());
|
||||
}
|
||||
cached_block_added_.clear();
|
||||
|
||||
emit BlockRemoved(cached_block_removed_);
|
||||
cached_block_removed_.clear();
|
||||
}
|
||||
|
||||
Node::EndOperation();
|
||||
}
|
||||
|
||||
void ViewerOutput::TrackListAddedBlock(Block *block, int index)
|
||||
{
|
||||
Timeline::TrackType type = static_cast<TrackList*>(sender())->type();
|
||||
SignalBlockAdded(block, TrackReference(type, index));
|
||||
emit BlockAdded(block, TrackReference(type, index));
|
||||
}
|
||||
|
||||
void ViewerOutput::TrackListAddedTrack(TrackOutput *track)
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
return samples_input_;
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
|
||||
|
||||
const VideoParams& video_params() const {
|
||||
return video_params_;
|
||||
@@ -127,8 +127,6 @@ public:
|
||||
signals:
|
||||
void TimebaseChanged(const rational&);
|
||||
|
||||
void GraphChangedFrom(NodeInput* source);
|
||||
|
||||
void LengthChanged(const rational& length);
|
||||
|
||||
void SizeChanged(int width, int height);
|
||||
@@ -141,7 +139,7 @@ signals:
|
||||
void AudioParamsChanged();
|
||||
|
||||
void BlockAdded(Block* block, TrackReference track);
|
||||
void BlockRemoved(const QList<Block*>& blocks);
|
||||
void BlockRemoved(Block* block);
|
||||
|
||||
void TrackAdded(TrackOutput* track, Timeline::TrackType type);
|
||||
void TrackRemoved(TrackOutput* track);
|
||||
@@ -151,9 +149,6 @@ signals:
|
||||
void MediaNameChanged(const QString& name);
|
||||
|
||||
private:
|
||||
QMap<Block*, TrackReference> cached_block_added_;
|
||||
QList<Block*> cached_block_removed_;
|
||||
|
||||
QUuid uuid_;
|
||||
|
||||
NodeInput* texture_input_;
|
||||
@@ -191,9 +186,6 @@ private slots:
|
||||
|
||||
void TrackHeightChangedSlot(int index, int height);
|
||||
|
||||
void SignalBlockAdded(Block *block, const TrackReference &track);
|
||||
void SignalBlockRemoved(Block *block);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user