+12
-13
@@ -47,7 +47,8 @@ Node::Node(bool create_default_output) :
|
||||
can_be_deleted_(true),
|
||||
override_color_(-1),
|
||||
last_change_time_(0),
|
||||
folder_(nullptr)
|
||||
folder_(nullptr),
|
||||
operation_stack_(0)
|
||||
{
|
||||
if (create_default_output) {
|
||||
AddOutput();
|
||||
@@ -1027,18 +1028,14 @@ void Node::InvalidateCache(const TimeRange &range, const QString &from, int elem
|
||||
|
||||
void Node::BeginOperation()
|
||||
{
|
||||
// Ripple through graph
|
||||
for (const std::pair<NodeOutput, NodeInput>& output : output_connections_) {
|
||||
output.second.node()->BeginOperation();
|
||||
}
|
||||
// Increase operation stack
|
||||
operation_stack_++;
|
||||
}
|
||||
|
||||
void Node::EndOperation()
|
||||
{
|
||||
// Ripple through graph
|
||||
for (const std::pair<NodeOutput, NodeInput>& output : output_connections_) {
|
||||
output.second.node()->EndOperation();
|
||||
}
|
||||
// Decrease operation stack
|
||||
operation_stack_--;
|
||||
}
|
||||
|
||||
TimeRange Node::InputTimeAdjustment(const QString &, int, const TimeRange &input_time) const
|
||||
@@ -1177,11 +1174,13 @@ Node *Node::CopyNodeInGraph(const Node *node, MultiUndoCommand *command)
|
||||
|
||||
void Node::SendInvalidateCache(const TimeRange &range, qint64 job_time)
|
||||
{
|
||||
for (const OutputConnection& conn : output_connections_) {
|
||||
// Send clear cache signal to the Node
|
||||
const NodeInput& in = conn.second;
|
||||
if (GetOperationStack() == 0) {
|
||||
for (const OutputConnection& conn : output_connections_) {
|
||||
// Send clear cache signal to the Node
|
||||
const NodeInput& in = conn.second;
|
||||
|
||||
in.node()->InvalidateCache(range, in.input(), in.element(), job_time);
|
||||
in.node()->InvalidateCache(range, in.input(), in.element(), job_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -816,6 +816,11 @@ protected:
|
||||
|
||||
void IgnoreHashingFrom(const QString& input_id);
|
||||
|
||||
int GetOperationStack() const
|
||||
{
|
||||
return operation_stack_;
|
||||
}
|
||||
|
||||
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled);
|
||||
|
||||
virtual void SaveCustom(QXmlStreamWriter* writer) const;
|
||||
@@ -1179,6 +1184,8 @@ private:
|
||||
|
||||
Folder* folder_;
|
||||
|
||||
int operation_stack_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
|
||||
|
||||
@@ -417,6 +417,10 @@ QVector<Block *> Track::BlocksAtTimeRange(const TimeRange &range) const
|
||||
|
||||
void Track::InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time)
|
||||
{
|
||||
if (GetOperationStack() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeRange limited;
|
||||
|
||||
const Block* b;
|
||||
@@ -431,8 +435,8 @@ void Track::InvalidateCache(const TimeRange& range, const QString& from, int ele
|
||||
|
||||
limited = TimeRange(qMax(range.in(), b->in()), qMin(range.out(), b->out()));
|
||||
} else {
|
||||
limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), qMax(last_invalidated_length_, track_length())));
|
||||
last_invalidated_length_ = track_length();
|
||||
limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), qMax(preop_track_length_, track_length())));
|
||||
preop_track_length_ = track_length_;
|
||||
}
|
||||
|
||||
Node::InvalidateCache(limited, from, element, job_time);
|
||||
@@ -578,6 +582,15 @@ void Track::Hash(const QString &output, QCryptographicHash &hash, const rational
|
||||
}
|
||||
}
|
||||
|
||||
void Track::EndOperation()
|
||||
{
|
||||
super::EndOperation();
|
||||
|
||||
if (track_length_ != midop_track_length_) {
|
||||
SetLengthInternal(midop_track_length_);
|
||||
}
|
||||
}
|
||||
|
||||
void Track::SetMuted(bool e)
|
||||
{
|
||||
SetStandardValue(kMutedInput, e);
|
||||
@@ -629,13 +642,13 @@ int Track::GetCacheIndexFromArrayIndex(int index) const
|
||||
|
||||
void Track::SetLengthInternal(const rational &r, bool invalidate)
|
||||
{
|
||||
if (r != track_length_) {
|
||||
// TimeRange will automatically normalize so that the shorter number is the in and the longer
|
||||
// is the out
|
||||
TimeRange invalidate_range(track_length_, r);
|
||||
// Hold track length until operation stack is empty
|
||||
midop_track_length_ = r;
|
||||
|
||||
if (GetOperationStack() == 0 && track_length_ != r) {
|
||||
TimeRange invalidate_range(track_length_, r);
|
||||
track_length_ = r;
|
||||
last_invalidated_length_ = qMax(last_invalidated_length_, track_length_);
|
||||
preop_track_length_ = qMax(preop_track_length_, track_length_);
|
||||
emit TrackLengthChanged();
|
||||
|
||||
if (invalidate) {
|
||||
|
||||
@@ -343,6 +343,8 @@ public:
|
||||
return waveform_;
|
||||
}
|
||||
|
||||
virtual void EndOperation() override;
|
||||
|
||||
static const double kTrackHeightDefault;
|
||||
static const double kTrackHeightMinimum;
|
||||
static const double kTrackHeightInterval;
|
||||
@@ -425,7 +427,9 @@ private:
|
||||
|
||||
rational track_length_;
|
||||
|
||||
rational last_invalidated_length_;
|
||||
rational midop_track_length_;
|
||||
|
||||
rational preop_track_length_;
|
||||
|
||||
double track_height_;
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight
|
||||
|
||||
ViewerOutput::ViewerOutput(bool create_default_streams) :
|
||||
video_frame_cache_(this),
|
||||
audio_playback_cache_(this),
|
||||
operation_stack_(0)
|
||||
audio_playback_cache_(this)
|
||||
{
|
||||
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
|
||||
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
|
||||
@@ -218,24 +217,22 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from,
|
||||
{
|
||||
Q_UNUSED(element)
|
||||
|
||||
if (operation_stack_ == 0) {
|
||||
if (from == kTextureInput || from == kSamplesInput
|
||||
|| from == kVideoParamsInput || from == kAudioParamsInput) {
|
||||
TimeRange invalidated_range(qMax(rational(), range.in()),
|
||||
qMin(GetLength(), range.out()));
|
||||
if (from == kTextureInput || from == kSamplesInput
|
||||
|| from == kVideoParamsInput || from == kAudioParamsInput) {
|
||||
TimeRange invalidated_range(qMax(rational(), range.in()),
|
||||
qMin(GetLength(), range.out()));
|
||||
|
||||
if (invalidated_range.in() != invalidated_range.out()) {
|
||||
if (from == kTextureInput || from == kVideoParamsInput) {
|
||||
video_frame_cache_.Invalidate(invalidated_range, job_time);
|
||||
} else {
|
||||
audio_playback_cache_.Invalidate(invalidated_range, job_time);
|
||||
}
|
||||
if (invalidated_range.in() != invalidated_range.out()) {
|
||||
if (from == kTextureInput || from == kVideoParamsInput) {
|
||||
video_frame_cache_.Invalidate(invalidated_range, job_time);
|
||||
} else {
|
||||
audio_playback_cache_.Invalidate(invalidated_range, job_time);
|
||||
}
|
||||
}
|
||||
|
||||
VerifyLength();
|
||||
}
|
||||
|
||||
VerifyLength();
|
||||
|
||||
super::InvalidateCache(range, from, element, job_time);
|
||||
}
|
||||
|
||||
@@ -298,10 +295,6 @@ void ViewerOutput::Retranslate()
|
||||
|
||||
void ViewerOutput::VerifyLength()
|
||||
{
|
||||
if (operation_stack_ != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeTraverser traverser;
|
||||
|
||||
rational video_length, audio_length, subtitle_length;
|
||||
@@ -364,20 +357,6 @@ rational ViewerOutput::GetCustomLength(Track::Type type) const
|
||||
return rational();
|
||||
}
|
||||
|
||||
void ViewerOutput::BeginOperation()
|
||||
{
|
||||
operation_stack_++;
|
||||
|
||||
super::BeginOperation();
|
||||
}
|
||||
|
||||
void ViewerOutput::EndOperation()
|
||||
{
|
||||
operation_stack_--;
|
||||
|
||||
super::EndOperation();
|
||||
}
|
||||
|
||||
NodeOutput ViewerOutput::GetConnectedTextureOutput()
|
||||
{
|
||||
return GetConnectedOutput(kTextureInput);
|
||||
|
||||
@@ -136,10 +136,6 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual void BeginOperation() override;
|
||||
|
||||
virtual void EndOperation() override;
|
||||
|
||||
virtual NodeOutput GetConnectedTextureOutput();
|
||||
|
||||
virtual NodeOutput GetConnectedSampleOutput();
|
||||
|
||||
@@ -1170,7 +1170,6 @@ private:
|
||||
new_block_length = b->length() + operation_movement;
|
||||
}
|
||||
|
||||
rational earliest_point_of_change;
|
||||
rational pre_shift;
|
||||
rational post_shift;
|
||||
|
||||
@@ -1195,7 +1194,7 @@ private:
|
||||
// As an insertion, we will shift from the gap's in to the gap's out
|
||||
pre_shift = gap->in();
|
||||
post_shift = gap->out();
|
||||
earliest_point_of_change = gap->in();
|
||||
working_data.earliest_point_of_change = gap->in();
|
||||
} else {
|
||||
// As a removal, we will shift from the gap's out to the gap's in
|
||||
pre_shift = gap->out();
|
||||
@@ -1212,7 +1211,7 @@ private:
|
||||
|
||||
if (redo) {
|
||||
// The earliest point changes will happen is at the start of this block
|
||||
earliest_point_of_change = b->in();
|
||||
working_data.earliest_point_of_change = b->in();
|
||||
|
||||
// As a removal, we will be shifting from the out point to the in point
|
||||
pre_shift = b->out();
|
||||
@@ -1228,7 +1227,7 @@ private:
|
||||
track->InsertBlockAfter(b, working_data.removed_gap_after);
|
||||
|
||||
// The earliest point changes will happen is at the start of this block
|
||||
earliest_point_of_change = b->in();
|
||||
working_data.earliest_point_of_change = b->in();
|
||||
|
||||
// As an insert, we will be shifting from the block's in point to its out point
|
||||
pre_shift = b->in();
|
||||
@@ -1242,7 +1241,7 @@ private:
|
||||
|
||||
if (movement_mode_ == Timeline::kTrimIn) {
|
||||
// The earliest point changes will occur is in point of this bloc
|
||||
earliest_point_of_change = b->in();
|
||||
working_data.earliest_point_of_change = b->in();
|
||||
|
||||
// Undo the trim in inversion we do above, this will still be inverted accurately for
|
||||
// undoing where appropriate
|
||||
@@ -1260,7 +1259,7 @@ private:
|
||||
} else {
|
||||
// The earliest point changes will occur is the out point if trimming out or the in point
|
||||
// if trimming in
|
||||
earliest_point_of_change = b->out();
|
||||
working_data.earliest_point_of_change = b->out();
|
||||
|
||||
// The latest out before the ripple is this block's current out point
|
||||
pre_shift = b->out();
|
||||
@@ -1274,17 +1273,10 @@ private:
|
||||
|
||||
}
|
||||
|
||||
track->EndOperation();
|
||||
|
||||
working_data_.insert(it.key(), working_data);
|
||||
|
||||
pre_latest_out = qMax(pre_latest_out, pre_shift);
|
||||
post_latest_out = qMax(post_latest_out, post_shift);
|
||||
|
||||
if (!all_tracks_unlocked_) {
|
||||
// If we're not shifting, the whole track must get invalidated
|
||||
track->Node::InvalidateCache(TimeRange(earliest_point_of_change, RATIONAL_MAX), Track::kBlockInput);
|
||||
}
|
||||
}
|
||||
|
||||
if (all_tracks_unlocked_) {
|
||||
@@ -1295,6 +1287,17 @@ private:
|
||||
track_list_->parent()->ShiftAudioCache(pre_latest_out, post_latest_out);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=working_data_.cbegin(); it!=working_data_.cend(); it++) {
|
||||
Track* track = it.key();
|
||||
|
||||
track->EndOperation();
|
||||
|
||||
if (!all_tracks_unlocked_) {
|
||||
// If we're not shifting, the whole track must get invalidated
|
||||
track->Node::InvalidateCache(TimeRange(it.value().earliest_point_of_change, RATIONAL_MAX), Track::kBlockInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TrackList* track_list_;
|
||||
@@ -1307,6 +1310,7 @@ private:
|
||||
GapBlock* created_gap = nullptr;
|
||||
Block* removed_gap_after;
|
||||
rational old_length;
|
||||
rational earliest_point_of_change;
|
||||
};
|
||||
|
||||
QHash<Track*, WorkingData> working_data_;
|
||||
@@ -1650,12 +1654,12 @@ public:
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
track_->BeginOperation();
|
||||
|
||||
// Invalidate the range inhabited by this block
|
||||
TimeRange invalidate_range(block_->in(), block_->out());
|
||||
|
||||
if (block_->next()) {
|
||||
track_->BeginOperation();
|
||||
|
||||
// Invalidate the range inhabited by this block
|
||||
TimeRange invalidate_range(block_->in(), block_->out());
|
||||
|
||||
// Block has a next, which means it's NOT at the end of the sequence and thus requires a gap
|
||||
rational new_gap_length = block_->length();
|
||||
|
||||
@@ -1704,6 +1708,10 @@ public:
|
||||
position_command_->redo();
|
||||
}
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput);
|
||||
|
||||
} else {
|
||||
// Block is at the end of the track, simply remove it
|
||||
|
||||
@@ -1719,49 +1727,51 @@ public:
|
||||
// Remove block in question
|
||||
track_->RippleRemoveBlock(block_);
|
||||
}
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
track_->BeginOperation();
|
||||
if (our_gap_ || existing_gap_) {
|
||||
track_->BeginOperation();
|
||||
|
||||
if (our_gap_) {
|
||||
if (our_gap_) {
|
||||
|
||||
// We made this gap, simply swap our gap back
|
||||
track_->ReplaceBlock(our_gap_, block_);
|
||||
our_gap_->setParent(&memory_manager_);
|
||||
// We made this gap, simply swap our gap back
|
||||
track_->ReplaceBlock(our_gap_, block_);
|
||||
our_gap_->setParent(&memory_manager_);
|
||||
|
||||
position_command_->undo();
|
||||
position_command_->undo();
|
||||
|
||||
} else if (existing_gap_) {
|
||||
|
||||
// If we're here, assume that we extended an existing gap
|
||||
rational original_gap_length = existing_gap_->length() - block_->length();
|
||||
|
||||
// If we merged two gaps together, restore the second one now
|
||||
if (existing_merged_gap_) {
|
||||
original_gap_length -= existing_merged_gap_->length();
|
||||
existing_merged_gap_->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(existing_merged_gap_, existing_gap_);
|
||||
existing_merged_gap_ = nullptr;
|
||||
}
|
||||
|
||||
// Restore original block
|
||||
if (existing_gap_precedes_) {
|
||||
track_->InsertBlockAfter(block_, existing_gap_);
|
||||
} else {
|
||||
track_->InsertBlockBefore(block_, existing_gap_);
|
||||
|
||||
// If we're here, assume that we extended an existing gap
|
||||
rational original_gap_length = existing_gap_->length() - block_->length();
|
||||
|
||||
// If we merged two gaps together, restore the second one now
|
||||
if (existing_merged_gap_) {
|
||||
original_gap_length -= existing_merged_gap_->length();
|
||||
existing_merged_gap_->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(existing_merged_gap_, existing_gap_);
|
||||
existing_merged_gap_ = nullptr;
|
||||
}
|
||||
|
||||
// Restore original block
|
||||
if (existing_gap_precedes_) {
|
||||
track_->InsertBlockAfter(block_, existing_gap_);
|
||||
} else {
|
||||
track_->InsertBlockBefore(block_, existing_gap_);
|
||||
}
|
||||
|
||||
// Restore gap's original length
|
||||
existing_gap_->set_length_and_media_out(original_gap_length);
|
||||
|
||||
existing_gap_ = nullptr;
|
||||
|
||||
}
|
||||
|
||||
// Restore gap's original length
|
||||
existing_gap_->set_length_and_media_out(original_gap_length);
|
||||
|
||||
existing_gap_ = nullptr;
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput);
|
||||
} else {
|
||||
|
||||
// Our gap and existing gap were both null, our block must have been at the end and thus
|
||||
@@ -1778,10 +1788,6 @@ public:
|
||||
track_->AppendBlock(block_);
|
||||
|
||||
}
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user