diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 6a61b73ac..43aff7934 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -34,12 +34,12 @@ const QString ClipBlock::kMediaInInput = QStringLiteral("media_in_in"); const QString ClipBlock::kSpeedInput = QStringLiteral("speed_in"); const QString ClipBlock::kReverseInput = QStringLiteral("reverse_in"); const QString ClipBlock::kMaintainAudioPitchInput = QStringLiteral("maintain_audio_pitch_in"); +const QString ClipBlock::kAutoCacheInput = QStringLiteral("autocache_in"); ClipBlock::ClipBlock() : in_transition_(nullptr), out_transition_(nullptr), - connected_viewer_(nullptr), - autocache_(false) + connected_viewer_(nullptr) { AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); SetInputProperty(kMediaInInput, QStringLiteral("view"), RationalSlider::kTime); @@ -53,6 +53,8 @@ ClipBlock::ClipBlock() : AddInput(kMaintainAudioPitchInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kAutoCacheInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + PrependInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); //SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer)); @@ -122,6 +124,17 @@ void ClipBlock::set_media_in(const rational &media_in) SetStandardValue(kMediaInInput, QVariant::fromValue(media_in)); } +void ClipBlock::SetAutocache(bool e) +{ + SetStandardValue(kAutoCacheInput, e); + + if (e) { + RequestInvalidatedFromConnected(); + } else { + qDebug() << "FIXME: signal that frames for this clip should be unqueued"; + } +} + rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse, bool ignore_speed) const { // These constants are not considered "values" per se, so we don't modify them @@ -176,30 +189,56 @@ rational ClipBlock::MediaToSequenceTime(const rational &media_time) const return sequence_time; } +void ClipBlock::RequestInvalidatedFromConnected() +{ + Track::Type type = GetTrackType(); + + if (type == Track::kVideo || type == Track::kAudio) { + if (Node *connected = GetConnectedOutput(kBufferIn)) { + TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length())); + if (type == Track::kVideo) { + // Handle thumbnails + { + TimeRangeList invalid = connected->thumbnail_cache()->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit connected->thumbnail_cache()->Request(r, PlaybackCache::kPreviewsOnly); + } + } + + // Handle video cache + if (IsAutocaching()) { + TimeRangeList invalid = connected->video_frame_cache()->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit connected->video_frame_cache()->Request(r, PlaybackCache::kPreviewsOnly); + } + } + } else if (type == Track::kAudio) { + { + TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit connected->waveform_cache()->Request(r, PlaybackCache::kPreviewsOnly); + } + } + + if (IsAutocaching()) { + TimeRangeList invalid = connected->audio_playback_cache()->GetInvalidatedRanges(max_range); + for (const TimeRange &r : invalid) { + emit connected->audio_playback_cache()->Request(r, PlaybackCache::kPreviewsOnly); + } + } + } + } + } +} + void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) { Q_UNUSED(element) // If signal is from texture input, transform all times from media time to sequence time if (from == kBufferIn) { - Track::Type type = GetTrackType(); - - if (type == Track::kVideo || type == Track::kAudio) { - if (Node *connected = GetConnectedOutput(from, element)) { - TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, length())); - if (type == Track::kVideo) { - emit connected->thumbnail_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); - if (autocache_) { - emit connected->video_frame_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); - } - } else if (type == Track::kAudio) { - emit connected->waveform_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); - if (autocache_) { - emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); - } - } - } - } + // Render caches where necessary + RequestInvalidatedFromConnected(); // Adjust range from media time to sequence time TimeRange adj; @@ -320,24 +359,7 @@ void ClipBlock::Retranslate() void ClipBlock::ConnectedToPreviewEvent() { - Track::Type type = GetTrackType(); - - if (type == Track::kVideo || type == Track::kAudio) { - if (Node *connected = GetConnectedOutput(kBufferIn)) { - TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length())); - if (type == Track::kVideo) { - TimeRangeList invalid = connected->thumbnail_cache()->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit connected->thumbnail_cache()->Request(r, PlaybackCache::kPreviewsOnly); - } - } else if (type == Track::kAudio) { - TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); - for (const TimeRange &r : invalid) { - emit connected->waveform_cache()->Request(r, PlaybackCache::kPreviewsOnly); - } - } - } - } + RequestInvalidatedFromConnected(); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index b43bfde09..13c5a64ad 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -59,6 +59,9 @@ public: rational media_in() const; void set_media_in(const rational& media_in); + bool IsAutocaching() const { return GetStandardValue(kAutoCacheInput).toBool(); } + void SetAutocache(bool e); + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override; virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; @@ -166,6 +169,8 @@ public: static const QString kReverseInput; static const QString kMaintainAudioPitchInput; + static const QString kAutoCacheInput; + protected: virtual void LinkChangeEvent() override; @@ -178,6 +183,8 @@ private: rational MediaToSequenceTime(const rational& media_time) const; + void RequestInvalidatedFromConnected(); + QVector block_links_; TransitionBlock* in_transition_; @@ -185,8 +192,6 @@ private: ViewerOutput *connected_viewer_; - bool autocache_; - private: rational last_media_in_; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 59a61addc..4bb2de0ee 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -54,6 +54,7 @@ #include "undo/timelineundoworkarea.h" #include "widget/menu/menu.h" #include "widget/menu/menushared.h" +#include "widget/nodeparamview/nodeparamviewundo.h" #include "widget/nodeview/nodeviewundo.h" #include "widget/timeruler/timeruler.h" @@ -1072,6 +1073,11 @@ void TimelineWidget::ShowContextMenu() menu.addSeparator(); if (ClipBlock *clip = dynamic_cast(selected.first())) { + QAction *autocache_action = menu.addAction(tr("Auto-Cache")); + autocache_action->setCheckable(true); + autocache_action->setChecked(clip->IsAutocaching()); + connect(autocache_action, &QAction::triggered, this, &TimelineWidget::SetSelectedClipsAutocaching); + if (clip->connected_viewer()) { QAction *reveal_in_project = menu.addAction(tr("Reveal in Project")); reveal_in_project->setData(reinterpret_cast(clip->connected_viewer())); @@ -1247,6 +1253,19 @@ void TimelineWidget::TrackAboutToBeDeleted(Track *track) } } +void TimelineWidget::SetSelectedClipsAutocaching(bool e) +{ + MultiUndoCommand *command = new MultiUndoCommand(); + + for (Block *b : selected_blocks_) { + if (ClipBlock *clip = dynamic_cast(b)) { + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(clip, ClipBlock::kAutoCacheInput)), e)); + } + } + + Core::instance()->undo_stack()->pushIfHasChildren(command); +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost_items_.append(ghost); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index c1ec0b292..ff431e792 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -433,6 +433,8 @@ private slots: void TrackAboutToBeDeleted(Track *track); + void SetSelectedClipsAutocaching(bool e); + }; }