use strings to connect nodes
This makes the node system somewhat more high-level with the intent of making working with them far more flexible and stable. By making the architecture more abstracted, it becomes far less rigid which should allow us to do even more with it and make it much less crash prone.
This commit is contained in:
+169
-169
@@ -33,24 +33,21 @@ const double Track::kTrackHeightDefault = 3.0;
|
||||
const double Track::kTrackHeightMinimum = 1.5;
|
||||
const double Track::kTrackHeightInterval = 0.5;
|
||||
|
||||
const QString Track::kBlockInput = QStringLiteral("block_in");
|
||||
const QString Track::kMutedInput = QStringLiteral("muted_in");
|
||||
|
||||
Track::Track() :
|
||||
track_type_(Track::kNone),
|
||||
index_(-1),
|
||||
locked_(false)
|
||||
{
|
||||
block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone);
|
||||
block_input_->SetKeyframable(false);
|
||||
block_input_->SetIsArray(true);
|
||||
connect(block_input_, &NodeInput::InputConnected, this, &Track::BlockConnected);
|
||||
connect(block_input_, &NodeInput::InputDisconnected, this, &Track::BlockDisconnected);
|
||||
AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable));
|
||||
|
||||
// Since blocks are time based, we can handle the invalidate timing a little more intelligently
|
||||
// on our end
|
||||
IgnoreInvalidationsFrom(block_input_);
|
||||
IgnoreInvalidationsFrom(kBlockInput);
|
||||
|
||||
muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean);
|
||||
muted_input_->SetKeyframable(false);
|
||||
connect(muted_input_, &NodeInput::ValueChanged, this, &Track::MutedInputValueChanged);
|
||||
AddInput(kMutedInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
// Set default height
|
||||
track_height_ = kTrackHeightDefault;
|
||||
@@ -97,9 +94,9 @@ QString Track::Description() const
|
||||
"a Sequence.");
|
||||
}
|
||||
|
||||
TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const
|
||||
TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||
{
|
||||
if (input == block_input_ && element >= 0) {
|
||||
if (input == kBlockInput && element >= 0) {
|
||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||
|
||||
return TransformRangeForBlock(blocks_.at(cache_index), input_time);
|
||||
@@ -108,9 +105,9 @@ TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRa
|
||||
return Node::InputTimeAdjustment(input, element, input_time);
|
||||
}
|
||||
|
||||
TimeRange Track::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const
|
||||
TimeRange Track::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const
|
||||
{
|
||||
if (input == block_input_ && element >= 0) {
|
||||
if (input == kBlockInput && element >= 0) {
|
||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||
const rational& block_in = blocks_.at(cache_index)->in();
|
||||
|
||||
@@ -157,12 +154,153 @@ void Track::SaveInternal(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(GetTrackHeight()));
|
||||
}
|
||||
|
||||
void Track::InputConnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||
{
|
||||
if (input == kBlockInput) {
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InvalidateAll(kBlockInput, element);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if a block was connected, if not, ignore
|
||||
Block* block = dynamic_cast<Block*>(output.node());
|
||||
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine where in the cache this block will be
|
||||
int cache_index = -1;
|
||||
Block *previous = nullptr, *next = nullptr;
|
||||
|
||||
int arr_sz = InputArraySize(kBlockInput);
|
||||
for (int i=element+1; i<arr_sz; i++) {
|
||||
// Find next block because this will be the index that we want to insert at
|
||||
cache_index = GetCacheIndexFromArrayIndex(i);
|
||||
|
||||
if (cache_index >= 0) {
|
||||
next = blocks_.at(cache_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no next, this will be inserted at the end
|
||||
if (cache_index == -1) {
|
||||
cache_index = blocks_.size();
|
||||
}
|
||||
|
||||
// Determine previous block, either by using next's previous or the last block if there was no
|
||||
// next. If there are neither, they'll both remain null
|
||||
if (next) {
|
||||
previous = next->previous();
|
||||
} else if (!blocks_.isEmpty()) {
|
||||
previous = blocks_.last();
|
||||
}
|
||||
|
||||
// Insert at index
|
||||
blocks_.insert(cache_index, block);
|
||||
block_array_indexes_.insert(cache_index, element);
|
||||
|
||||
// Update previous/next
|
||||
if (previous) {
|
||||
previous->set_next(block);
|
||||
block->set_previous(previous);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
block->set_next(next);
|
||||
next->set_previous(block);
|
||||
}
|
||||
|
||||
block->set_track(this);
|
||||
|
||||
// Update ins/outs
|
||||
UpdateInOutFrom(cache_index);
|
||||
|
||||
// Connect to the block
|
||||
connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||
|
||||
// Invalidate cache now that block should have an in point
|
||||
InvalidateCache(TimeRange(block->in(), track_length()));
|
||||
|
||||
// Emit block added signal
|
||||
emit BlockAdded(block);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||
{
|
||||
if (input == kBlockInput) {
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InvalidateAll(kBlockInput, element);
|
||||
return;
|
||||
}
|
||||
|
||||
Block* b = dynamic_cast<Block*>(output.node());
|
||||
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit BlockRemoved(b);
|
||||
|
||||
TimeRange invalidate_range(b->in(), track_length());
|
||||
|
||||
// Get cache index
|
||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||
|
||||
// Remove block here
|
||||
blocks_.removeAt(cache_index);
|
||||
block_array_indexes_.removeAt(cache_index);
|
||||
|
||||
// Update previous/nexts
|
||||
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);
|
||||
b->set_track(nullptr);
|
||||
|
||||
// Update lengths
|
||||
if (next) {
|
||||
UpdateInOutFrom(blocks_.indexOf(next));
|
||||
} else if (blocks_.isEmpty()) {
|
||||
SetLengthInternal(rational());
|
||||
} else {
|
||||
SetLengthInternal(blocks_.last()->out());
|
||||
}
|
||||
|
||||
disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||
|
||||
InvalidateCache(invalidate_range);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (input == kMutedInput) {
|
||||
emit MutedChanged(IsMuted());
|
||||
}
|
||||
}
|
||||
|
||||
void Track::Retranslate()
|
||||
{
|
||||
Node::Retranslate();
|
||||
|
||||
block_input_->set_name(tr("Blocks"));
|
||||
muted_input_->set_name(tr("Muted"));
|
||||
SetInputName(kBlockInput, tr("Blocks"));
|
||||
SetInputName(kMutedInput, tr("Muted"));
|
||||
}
|
||||
|
||||
void Track::SetIndex(const int &index)
|
||||
@@ -276,15 +414,15 @@ QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
|
||||
return list;
|
||||
}
|
||||
|
||||
void Track::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
||||
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element)
|
||||
{
|
||||
TimeRange limited;
|
||||
|
||||
Block* b;
|
||||
const Block* b;
|
||||
|
||||
if (from.input == block_input_
|
||||
&& from.element >= 0
|
||||
&& (b = dynamic_cast<Block*>(from.input->GetConnectedNode(from.element)))) {
|
||||
if (from == kBlockInput
|
||||
&& element >= 0
|
||||
&& (b = dynamic_cast<const Block*>(GetConnectedOutput(from, element).node()))) {
|
||||
// Limit the range signal to the corresponding block
|
||||
if (range.out() <= b->in() || range.in() >= b->out()) {
|
||||
return;
|
||||
@@ -329,8 +467,8 @@ void Track::PrependBlock(Block *block)
|
||||
{
|
||||
BeginOperation();
|
||||
|
||||
block_input_->ArrayPrepend();
|
||||
Node::ConnectEdge(block, block_input_, 0);
|
||||
InputArrayPrepend(kBlockInput);
|
||||
Node::ConnectEdge(block, NodeInput(this, kBlockInput, 0));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -343,8 +481,8 @@ void Track::InsertBlockAtIndex(Block *block, int index)
|
||||
BeginOperation();
|
||||
|
||||
int insert_index = GetArrayIndexFromCacheIndex(index);
|
||||
block_input_->ArrayInsert(insert_index);
|
||||
Node::ConnectEdge(block, block_input_, insert_index);
|
||||
InputArrayInsert(kBlockInput, insert_index);
|
||||
Node::ConnectEdge(block, NodeInput(this, kBlockInput, insert_index));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -355,8 +493,8 @@ void Track::AppendBlock(Block *block)
|
||||
{
|
||||
BeginOperation();
|
||||
|
||||
block_input_->ArrayAppend();
|
||||
Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1);
|
||||
InputArrayAppend(kBlockInput);
|
||||
Node::ConnectEdge(block, NodeInput(this, kBlockInput, InputArraySize(kBlockInput) - 1));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -371,7 +509,7 @@ void Track::RippleRemoveBlock(Block *block)
|
||||
rational remove_in = block->in();
|
||||
rational remove_out = block->out();
|
||||
|
||||
block_input_->ArrayRemove(GetArrayIndexFromBlock(block));
|
||||
InputArrayRemove(kBlockInput, GetArrayIndexFromBlock(block));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -384,9 +522,9 @@ void Track::ReplaceBlock(Block *old, Block *replace)
|
||||
|
||||
int index_of_old_block = GetArrayIndexFromBlock(old);
|
||||
|
||||
DisconnectEdge(old, block_input_, index_of_old_block);
|
||||
DisconnectEdge(old, NodeInput(this, kBlockInput, index_of_old_block));
|
||||
|
||||
ConnectEdge(replace, block_input_, index_of_old_block);
|
||||
ConnectEdge(replace, NodeInput(this, kBlockInput, index_of_old_block));
|
||||
|
||||
EndOperation();
|
||||
|
||||
@@ -421,7 +559,7 @@ QString Track::GetDefaultTrackName(Track::Type type, int index)
|
||||
|
||||
bool Track::IsMuted() const
|
||||
{
|
||||
return muted_input_->GetStandardValue().toBool();
|
||||
return GetStandardValue(kMutedInput).toBool();
|
||||
}
|
||||
|
||||
bool Track::IsLocked() const
|
||||
@@ -429,11 +567,6 @@ bool Track::IsLocked() const
|
||||
return locked_;
|
||||
}
|
||||
|
||||
NodeInput *Track::block_input() const
|
||||
{
|
||||
return block_input_;
|
||||
}
|
||||
|
||||
void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
||||
{
|
||||
Block* b = BlockAtTime(time);
|
||||
@@ -446,8 +579,7 @@ void Track::Hash(QCryptographicHash &hash, const rational &time) const
|
||||
|
||||
void Track::SetMuted(bool e)
|
||||
{
|
||||
muted_input_->SetStandardValue(e);
|
||||
InvalidateCache(TimeRange(0, track_length()));
|
||||
SetStandardValue(kMutedInput, e);
|
||||
}
|
||||
|
||||
void Track::SetLocked(bool e)
|
||||
@@ -511,133 +643,6 @@ void Track::SetLengthInternal(const rational &r, bool invalidate)
|
||||
}
|
||||
}
|
||||
|
||||
void Track::BlockConnected(Node *node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InvalidateAll(block_input_, element);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if a block was connected, if not, ignore
|
||||
Block* block = dynamic_cast<Block*>(node);
|
||||
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine where in the cache this block will be
|
||||
int cache_index = -1;
|
||||
Block *previous = nullptr, *next = nullptr;
|
||||
|
||||
for (int i=element+1; i<block_input_->ArraySize(); i++) {
|
||||
// Find next block because this will be the index that we want to insert at
|
||||
cache_index = GetCacheIndexFromArrayIndex(i);
|
||||
|
||||
if (cache_index >= 0) {
|
||||
next = blocks_.at(cache_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no next, this will be inserted at the end
|
||||
if (cache_index == -1) {
|
||||
cache_index = blocks_.size();
|
||||
}
|
||||
|
||||
// Determine previous block, either by using next's previous or the last block if there was no
|
||||
// next. If there are neither, they'll both remain null
|
||||
if (next) {
|
||||
previous = next->previous();
|
||||
} else if (!blocks_.isEmpty()) {
|
||||
previous = blocks_.last();
|
||||
}
|
||||
|
||||
// Insert at index
|
||||
blocks_.insert(cache_index, block);
|
||||
block_array_indexes_.insert(cache_index, element);
|
||||
|
||||
// Update previous/next
|
||||
if (previous) {
|
||||
previous->set_next(block);
|
||||
block->set_previous(previous);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
block->set_next(next);
|
||||
next->set_previous(block);
|
||||
}
|
||||
|
||||
block->set_track(this);
|
||||
|
||||
// Update ins/outs
|
||||
UpdateInOutFrom(cache_index);
|
||||
|
||||
// Connect to the block
|
||||
connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||
|
||||
// Invalidate cache now that block should have an in point
|
||||
InvalidateCache(TimeRange(block->in(), track_length()));
|
||||
|
||||
// Emit block added signal
|
||||
emit BlockAdded(block);
|
||||
}
|
||||
|
||||
void Track::BlockDisconnected(Node* node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
InvalidateAll(block_input_, element);
|
||||
return;
|
||||
}
|
||||
|
||||
Block* b = dynamic_cast<Block*>(node);
|
||||
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit BlockRemoved(b);
|
||||
|
||||
TimeRange invalidate_range(b->in(), track_length());
|
||||
|
||||
// Get cache index
|
||||
int cache_index = GetCacheIndexFromArrayIndex(element);
|
||||
|
||||
// Remove block here
|
||||
blocks_.removeAt(cache_index);
|
||||
block_array_indexes_.removeAt(cache_index);
|
||||
|
||||
// Update previous/nexts
|
||||
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);
|
||||
b->set_track(nullptr);
|
||||
|
||||
// Update lengths
|
||||
if (next) {
|
||||
UpdateInOutFrom(blocks_.indexOf(next));
|
||||
} else if (blocks_.isEmpty()) {
|
||||
SetLengthInternal(rational());
|
||||
} else {
|
||||
SetLengthInternal(blocks_.last()->out());
|
||||
}
|
||||
|
||||
disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged);
|
||||
|
||||
InvalidateCache(invalidate_range);
|
||||
}
|
||||
|
||||
void Track::BlockLengthChanged()
|
||||
{
|
||||
// Assumes sender is a Block
|
||||
@@ -654,11 +659,6 @@ void Track::BlockLengthChanged()
|
||||
InvalidateCache(invalidate_region);
|
||||
}
|
||||
|
||||
void Track::MutedInputValueChanged()
|
||||
{
|
||||
emit MutedChanged(IsMuted());
|
||||
}
|
||||
|
||||
uint qHash(const Track::Reference &r, uint seed)
|
||||
{
|
||||
// Not super efficient, but couldn't think of any better way to ensure a different hash each time
|
||||
|
||||
@@ -56,9 +56,9 @@ public:
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override;
|
||||
virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||
|
||||
virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override;
|
||||
virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override;
|
||||
|
||||
static rational TransformTimeForBlock(Block* block, const rational& time);
|
||||
|
||||
@@ -222,7 +222,7 @@ public:
|
||||
return blocks_;
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection()) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from = QString(), int element = -1) override;
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
@@ -274,8 +274,6 @@ public:
|
||||
|
||||
bool IsLocked() const;
|
||||
|
||||
NodeInput* block_input() const;
|
||||
|
||||
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
|
||||
|
||||
AudioVisualWaveform& waveform()
|
||||
@@ -287,6 +285,9 @@ public:
|
||||
static const double kTrackHeightMinimum;
|
||||
static const double kTrackHeightInterval;
|
||||
|
||||
static const QString kBlockInput;
|
||||
static const QString kMutedInput;
|
||||
|
||||
public slots:
|
||||
void SetMuted(bool e);
|
||||
|
||||
@@ -338,6 +339,12 @@ protected:
|
||||
|
||||
virtual void SaveInternal(QXmlStreamWriter* writer) const override;
|
||||
|
||||
virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override;
|
||||
|
||||
virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override;
|
||||
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
private:
|
||||
void UpdateInOutFrom(int index);
|
||||
|
||||
@@ -352,10 +359,6 @@ private:
|
||||
QVector<Block*> blocks_;
|
||||
QVector<int> block_array_indexes_;
|
||||
|
||||
NodeInput* block_input_;
|
||||
|
||||
NodeInput* muted_input_;
|
||||
|
||||
Track::Type track_type_;
|
||||
|
||||
rational track_length_;
|
||||
@@ -371,14 +374,8 @@ private:
|
||||
AudioVisualWaveform waveform_;
|
||||
|
||||
private slots:
|
||||
void BlockConnected(Node* node, int element);
|
||||
|
||||
void BlockDisconnected(Node* node, int element);
|
||||
|
||||
void BlockLengthChanged();
|
||||
|
||||
void MutedInputValueChanged();
|
||||
|
||||
};
|
||||
|
||||
uint qHash(const Track::Reference& r, uint seed = 0);
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, NodeInput *track_input) :
|
||||
TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, const QString &track_input) :
|
||||
QObject(parent),
|
||||
track_input_(track_input),
|
||||
type_(type)
|
||||
{
|
||||
connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected);
|
||||
connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected);
|
||||
}
|
||||
|
||||
Track *TrackList::GetTrackAt(int index) const
|
||||
@@ -48,7 +46,7 @@ Track *TrackList::GetTrackAt(int index) const
|
||||
void TrackList::TrackConnected(Node *node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
parent()->InvalidateAll(track_input_, element);
|
||||
parent()->InvalidateAll(track_input(), element);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ void TrackList::TrackConnected(Node *node, int element)
|
||||
|
||||
// Determine where in the cache this block will be
|
||||
int cache_index = -1;
|
||||
for (int i=element+1; i<track_input_->ArraySize(); i++) {
|
||||
for (int i=element+1; i<ArraySize(); i++) {
|
||||
// Find next track because this will be the index we insert at
|
||||
cache_index = GetCacheIndexFromArrayIndex(i);
|
||||
|
||||
@@ -97,7 +95,7 @@ void TrackList::TrackDisconnected(Node *node, int element)
|
||||
{
|
||||
if (element == -1) {
|
||||
// User has replaced the entire array, we will invalidate everything
|
||||
parent()->InvalidateAll(track_input_, element);
|
||||
parent()->InvalidateAll(track_input(), element);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,11 +139,36 @@ NodeGraph *TrackList::GetParentGraph() const
|
||||
return static_cast<NodeGraph*>(parent()->parent());
|
||||
}
|
||||
|
||||
const QString& TrackList::track_input() const
|
||||
{
|
||||
return track_input_;
|
||||
}
|
||||
|
||||
NodeInput TrackList::track_input(int element) const
|
||||
{
|
||||
return NodeInput(parent(), track_input(), element);
|
||||
}
|
||||
|
||||
ViewerOutput *TrackList::parent() const
|
||||
{
|
||||
return static_cast<ViewerOutput*>(QObject::parent());
|
||||
}
|
||||
|
||||
int TrackList::ArraySize() const
|
||||
{
|
||||
return parent()->InputArraySize(track_input());
|
||||
}
|
||||
|
||||
void TrackList::ArrayAppend(bool undoable)
|
||||
{
|
||||
parent()->InputArrayAppend(track_input(), undoable);
|
||||
}
|
||||
|
||||
void TrackList::ArrayRemoveLast(bool undoable)
|
||||
{
|
||||
parent()->InputArrayRemoveLast(track_input(), undoable);
|
||||
}
|
||||
|
||||
void TrackList::UpdateTotalLength()
|
||||
{
|
||||
total_length_ = 0;
|
||||
|
||||
@@ -35,7 +35,7 @@ class TrackList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TrackList(ViewerOutput *parent, const Track::Type& type, NodeInput* track_input);
|
||||
TrackList(ViewerOutput *parent, const Track::Type& type, const QString& track_input);
|
||||
|
||||
const Track::Type& type() const
|
||||
{
|
||||
@@ -61,13 +61,27 @@ public:
|
||||
|
||||
NodeGraph* GetParentGraph() const;
|
||||
|
||||
NodeInput* track_input() const
|
||||
{
|
||||
return track_input_;
|
||||
}
|
||||
const QString &track_input() const;
|
||||
NodeInput track_input(int element) const;
|
||||
|
||||
ViewerOutput* parent() const;
|
||||
|
||||
int ArraySize() const;
|
||||
|
||||
void ArrayAppend(bool undoable = false);
|
||||
void ArrayRemoveLast(bool undoable = false);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Slot for when the track connection is added
|
||||
*/
|
||||
void TrackConnected(Node* node, int element);
|
||||
|
||||
/**
|
||||
* @brief Slot for when the track connection is removed
|
||||
*/
|
||||
void TrackDisconnected(Node* node, int element);
|
||||
|
||||
signals:
|
||||
void TrackListChanged();
|
||||
|
||||
@@ -96,23 +110,13 @@ private:
|
||||
return track_array_indexes_.indexOf(index);
|
||||
}
|
||||
|
||||
NodeInput* track_input_;
|
||||
QString track_input_;
|
||||
|
||||
rational total_length_;
|
||||
|
||||
enum Track::Type type_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot for when the track connection is added
|
||||
*/
|
||||
void TrackConnected(Node* node, int element);
|
||||
|
||||
/**
|
||||
* @brief Slot for when the track connection is removed
|
||||
*/
|
||||
void TrackDisconnected(Node* node, int element);
|
||||
|
||||
/**
|
||||
* @brief Slot for when any of the track's length changes so we can update the length of the tracklist
|
||||
*/
|
||||
|
||||
@@ -24,31 +24,31 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString ViewerOutput::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
|
||||
const QString ViewerOutput::kTrackInputFormat = QStringLiteral("track_in_%1");
|
||||
|
||||
ViewerOutput::ViewerOutput() :
|
||||
video_frame_cache_(this),
|
||||
audio_playback_cache_(this),
|
||||
operation_stack_(0)
|
||||
{
|
||||
texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture);
|
||||
texture_input_->SetKeyframable(false);
|
||||
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples);
|
||||
samples_input_->SetKeyframable(false);
|
||||
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
|
||||
|
||||
// Create TrackList instances
|
||||
track_inputs_.resize(Track::kCount);
|
||||
track_lists_.resize(Track::kCount);
|
||||
|
||||
for (int i=0;i<Track::kCount;i++) {
|
||||
// Create track input
|
||||
NodeInput* track_input = new NodeInput(this, QStringLiteral("track_in_%1").arg(i), NodeValue::kNone);
|
||||
track_input->SetIsArray(true);
|
||||
track_input->SetKeyframable(false);
|
||||
QString track_input_id = kTrackInputFormat.arg(i);
|
||||
|
||||
IgnoreInvalidationsFrom(track_input);
|
||||
track_inputs_.replace(i, track_input);
|
||||
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
|
||||
|
||||
TrackList* list = new TrackList(this, static_cast<Track::Type>(i), track_input);
|
||||
IgnoreInvalidationsFrom(track_input_id);
|
||||
|
||||
TrackList* list = new TrackList(this, static_cast<Track::Type>(i), track_input_id);
|
||||
track_lists_.replace(i, list);
|
||||
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
|
||||
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
|
||||
@@ -110,15 +110,17 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
||||
ShiftAudioCache(from, to);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from)
|
||||
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element)
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (operation_stack_ == 0) {
|
||||
if (from.input == texture_input_ || from.input == samples_input_) {
|
||||
if (from == kTextureInput || from == kSamplesInput) {
|
||||
TimeRange invalidated_range(qMax(rational(), range.in()),
|
||||
qMin(GetLength(), range.out()));
|
||||
|
||||
if (invalidated_range.in() != invalidated_range.out()) {
|
||||
if (from.input == texture_input_) {
|
||||
if (from == kTextureInput) {
|
||||
video_frame_cache_.Invalidate(invalidated_range);
|
||||
} else {
|
||||
audio_playback_cache_.Invalidate(invalidated_range);
|
||||
@@ -216,8 +218,8 @@ void ViewerOutput::VerifyLength()
|
||||
{
|
||||
video_length = track_lists_.at(Track::kVideo)->GetTotalLength();
|
||||
|
||||
if (video_length.isNull() && texture_input_->IsConnected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0);
|
||||
if (video_length.isNull() && IsInputConnected(kTextureInput)) {
|
||||
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0));
|
||||
video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
||||
}
|
||||
|
||||
@@ -227,8 +229,8 @@ void ViewerOutput::VerifyLength()
|
||||
{
|
||||
audio_length = track_lists_.at(Track::kAudio)->GetTotalLength();
|
||||
|
||||
if (audio_length.isNull() && samples_input_->IsConnected()) {
|
||||
NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0);
|
||||
if (audio_length.isNull() && IsInputConnected(kSamplesInput)) {
|
||||
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0));
|
||||
audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
|
||||
}
|
||||
|
||||
@@ -251,11 +253,11 @@ void ViewerOutput::Retranslate()
|
||||
{
|
||||
Node::Retranslate();
|
||||
|
||||
texture_input_->set_name(tr("Texture"));
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
|
||||
samples_input_->set_name(tr("Samples"));
|
||||
SetInputName(kSamplesInput, tr("Samples"));
|
||||
|
||||
for (int i=0;i<track_inputs_.size();i++) {
|
||||
for (int i=0;i<Track::kCount;i++) {
|
||||
QString input_name;
|
||||
|
||||
switch (static_cast<Track::Type>(i)) {
|
||||
@@ -274,7 +276,7 @@ void ViewerOutput::Retranslate()
|
||||
}
|
||||
|
||||
if (!input_name.isEmpty()) {
|
||||
track_inputs_.at(i)->set_name(input_name);
|
||||
SetInputName(kTrackInputFormat.arg(i), input_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,4 +295,32 @@ void ViewerOutput::EndOperation()
|
||||
Node::EndOperation();
|
||||
}
|
||||
|
||||
void ViewerOutput::InputConnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||
{
|
||||
if (input == kTextureInput) {
|
||||
emit TextureInputChanged();
|
||||
} else {
|
||||
foreach (TrackList* list, track_lists_) {
|
||||
if (list->track_input() == input) {
|
||||
list->TrackConnected(output.node(), element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output)
|
||||
{
|
||||
if (input == kTextureInput) {
|
||||
emit TextureInputChanged();
|
||||
} else {
|
||||
foreach (TrackList* list, track_lists_) {
|
||||
if (list->track_input() == input) {
|
||||
list->TrackDisconnected(output.node(), element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,17 +59,7 @@ public:
|
||||
void ShiftAudioCache(const rational& from, const rational& to);
|
||||
void ShiftCache(const rational& from, const rational& to);
|
||||
|
||||
NodeInput* texture_input() const
|
||||
{
|
||||
return texture_input_;
|
||||
}
|
||||
|
||||
NodeInput* samples_input() const
|
||||
{
|
||||
return samples_input_;
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
|
||||
|
||||
const VideoParams& video_params() const
|
||||
{
|
||||
@@ -106,11 +96,6 @@ public:
|
||||
*/
|
||||
QVector<Track *> GetUnlockedTracks() const;
|
||||
|
||||
NodeInput* track_input(Track::Type type) const
|
||||
{
|
||||
return track_inputs_.at(type);
|
||||
}
|
||||
|
||||
TrackList* track_list(Track::Type type) const
|
||||
{
|
||||
return track_lists_.at(type);
|
||||
@@ -132,6 +117,10 @@ public:
|
||||
|
||||
virtual void EndOperation() override;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kSamplesInput;
|
||||
static const QString kTrackInputFormat;
|
||||
|
||||
signals:
|
||||
void TimebaseChanged(const rational&);
|
||||
|
||||
@@ -149,19 +138,20 @@ signals:
|
||||
void TrackAdded(Track* track);
|
||||
void TrackRemoved(Track* track);
|
||||
|
||||
void TextureInputChanged();
|
||||
|
||||
protected:
|
||||
void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override;
|
||||
|
||||
void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override;
|
||||
|
||||
private:
|
||||
QUuid uuid_;
|
||||
|
||||
NodeInput* texture_input_;
|
||||
|
||||
NodeInput* samples_input_;
|
||||
|
||||
VideoParams video_params_;
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
QVector<NodeInput*> track_inputs_;
|
||||
|
||||
QVector<TrackList*> track_lists_;
|
||||
|
||||
QVector<Track*> track_cache_;
|
||||
|
||||
Reference in New Issue
Block a user