From d0e12ce60767a6820979df90d34430407ea11867 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 13 Sep 2022 19:19:42 -0700 Subject: [PATCH] timeline: add options for manually caching and discarding parts of clips --- app/node/block/clip/clip.cpp | 31 ++++++++- app/node/block/clip/clip.h | 5 +- app/widget/timelinewidget/timelinewidget.cpp | 70 ++++++++++++++++++-- app/widget/timelinewidget/timelinewidget.h | 4 ++ app/widget/timelinewidget/tool/import.cpp | 3 +- 5 files changed, 101 insertions(+), 12 deletions(-) diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 1b75d7eb0..787cf5442 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -136,6 +136,18 @@ void ClipBlock::SetAutocache(bool e) SetStandardValue(kAutoCacheInput, e); } +void ClipBlock::DiscardCache() +{ + if (Node *connected = GetConnectedOutput(kBufferIn)) { + Track::Type type = GetTrackType(); + if (type == Track::kVideo) { + connected->video_frame_cache()->Invalidate(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); + } else if (type == Track::kAudio) { + connected->audio_playback_cache()->Invalidate(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); + } + } +} + rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, uint64_t flags) const { // These constants are not considered "values" per se, so we don't modify them @@ -237,13 +249,18 @@ void ClipBlock::RequestRangeFromConnected(const TimeRange &range) } } -void ClipBlock::RequestInvalidatedFromConnected() +void ClipBlock::RequestInvalidatedFromConnected(bool force_all, const TimeRange &intersect) { Track::Type type = GetTrackType(); if (type == Track::kVideo || type == Track::kAudio) { if (Node *connected = GetConnectedOutput(kBufferIn)) { TimeRange max_range = media_range(); + + if (!intersect.length().isNull()) { + max_range = max_range.Intersected(intersect); + } + if (type == Track::kVideo) { // Handle thumbnails TimeRange thumb_range = max_range; @@ -252,7 +269,7 @@ void ClipBlock::RequestInvalidatedFromConnected() } // Handle video cache - if (IsAutocaching()) { + if (IsAutocaching() || force_all) { RequestInvalidatedForCache(connected->video_frame_cache(), max_range); } } else if (type == Track::kAudio) { @@ -262,7 +279,7 @@ void ClipBlock::RequestInvalidatedFromConnected() } // Handle audio cache - if (IsAutocaching()) { + if (IsAutocaching() || force_all) { RequestInvalidatedForCache(connected->audio_playback_cache(), max_range); } } @@ -391,6 +408,10 @@ void ClipBlock::InputConnectedEvent(const QString &input, int element, Node *out super::InputConnectedEvent(input, element, output); if (input == kBufferIn) { + connect(output->thumbnail_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); + connect(output->waveform_cache(), &AudioPlaybackCache::Invalidated, this, &Block::PreviewChanged); + connect(output->video_frame_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); + connect(output->audio_playback_cache(), &AudioPlaybackCache::Invalidated, this, &Block::PreviewChanged); connect(output->thumbnail_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged); connect(output->waveform_cache(), &AudioPlaybackCache::Validated, this, &Block::PreviewChanged); connect(output->video_frame_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged); @@ -403,6 +424,10 @@ void ClipBlock::InputDisconnectedEvent(const QString &input, int element, Node * super::InputDisconnectedEvent(input, element, output); if (input == kBufferIn) { + disconnect(output->thumbnail_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); + disconnect(output->waveform_cache(), &AudioPlaybackCache::Invalidated, this, &Block::PreviewChanged); + disconnect(output->video_frame_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); + disconnect(output->audio_playback_cache(), &AudioPlaybackCache::Invalidated, this, &Block::PreviewChanged); disconnect(output->thumbnail_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged); disconnect(output->waveform_cache(), &AudioPlaybackCache::Validated, this, &Block::PreviewChanged); disconnect(output->video_frame_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged); diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 8f98ee80f..5c84a1eca 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -63,6 +63,8 @@ public: bool IsAutocaching() const { return GetStandardValue(kAutoCacheInput).toBool(); } void SetAutocache(bool e); + void DiscardCache(); + 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; @@ -73,7 +75,7 @@ public: virtual void Retranslate() override; - void RerequestCaches() { RequestInvalidatedFromConnected(); } + void RequestInvalidatedFromConnected(bool force_all = false, const TimeRange &intersect = TimeRange()); double speed() const { @@ -227,7 +229,6 @@ private: rational MediaToSequenceTime(const rational& media_time) const; void RequestRangeFromConnected(const TimeRange &range); - void RequestInvalidatedFromConnected(); void RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request); void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 44e63c687..f54792aef 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -1110,10 +1110,26 @@ 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); + { + Menu *cache_menu = new Menu(tr("Cache"), &menu); + menu.addMenu(cache_menu); + + QAction *autocache_action = cache_menu->addAction(tr("Auto-Cache")); + autocache_action->setCheckable(true); + autocache_action->setChecked(clip->IsAutocaching()); + connect(autocache_action, &QAction::triggered, this, &TimelineWidget::SetSelectedClipsAutocaching); + + cache_menu->addSeparator(); + + auto cache_clip = cache_menu->addAction(tr("Cache All")); + connect(cache_clip, &QAction::triggered, this, &TimelineWidget::CacheClips); + + auto cache_inout = cache_menu->addAction(tr("Cache In/Out")); + connect(cache_inout, &QAction::triggered, this, &TimelineWidget::CacheClipsInOut); + + auto cache_discard = cache_menu->addAction(tr("Discard")); + connect(cache_discard, &QAction::triggered, this, &TimelineWidget::CacheDiscard); + } if (clip->connected_viewer()) { QAction *reveal_in_footage_viewer = menu.addAction(tr("Reveal in Footage Viewer")); @@ -1145,7 +1161,7 @@ void TimelineWidget::ShowContextMenu() menu.addMenu(thumbnail_menu); thumbnail_menu->AddActionWithData(tr("Disabled"), Timeline::kThumbnailOff, OLIVE_CONFIG("TimelineThumbnailMode")); - thumbnail_menu->AddActionWithData(tr("Only At In/Out Points"), Timeline::kThumbnailInOut, OLIVE_CONFIG("TimelineThumbnailMode")); + thumbnail_menu->AddActionWithData(tr("Only At In Points"), Timeline::kThumbnailInOut, OLIVE_CONFIG("TimelineThumbnailMode")); thumbnail_menu->AddActionWithData(tr("Enabled"), Timeline::kThumbnailOn, OLIVE_CONFIG("TimelineThumbnailMode")); connect(thumbnail_menu, &Menu::triggered, this, &TimelineWidget::SetViewThumbnailsEnabled); @@ -1311,6 +1327,50 @@ void TimelineWidget::SetSelectedClipsAutocaching(bool e) Core::instance()->undo_stack()->pushIfHasChildren(command); } +void TimelineWidget::CacheClips() +{ + for (Block *b : selected_blocks_) { + if (ClipBlock *clip = dynamic_cast(b)) { + clip->RequestInvalidatedFromConnected(true); + } + } +} + +void TimelineWidget::CacheClipsInOut() +{ + if (!this->sequence() || !this->sequence()->GetWorkArea()->enabled()) { + return; + } + + TimeTargetObject tto; + tto.SetTimeTarget(this->sequence()); + + const TimeRange &r = this->sequence()->GetWorkArea()->range(); + for (Block *b : qAsConst(selected_blocks_)) { + if (ClipBlock *clip = dynamic_cast(b)) { + if (Node *connected = clip->GetConnectedOutput(clip->kBufferIn)) { + TimeRange adjusted = tto.GetAdjustedTime(this->sequence(), connected, r, true); + clip->RequestInvalidatedFromConnected(true, adjusted); + } + } + } +} + +void TimelineWidget::CacheDiscard() +{ + if (QMessageBox::question(this, tr("Discard Cache"), + tr("This will discard all cache for this clip. " + "If the clip has auto-cache enabled, it will be recached immediately. " + "This cannot be undone.\n\n" + "Do you wish to continue?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + for (Block *b : selected_blocks_) { + if (ClipBlock *clip = dynamic_cast(b)) { + clip->DiscardCache(); + } + } + } +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost_items_.append(ghost); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index b2f869443..d952c5419 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -440,6 +440,10 @@ private slots: void SetSelectedClipsAutocaching(bool e); + void CacheClips(); + void CacheClipsInOut(); + void CacheDiscard(); + }; } diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 73a100bff..02dec714d 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -503,7 +503,7 @@ void ImportTool::DropGhosts(bool insert) Core::instance()->undo_stack()->pushIfHasChildren(command); while (!imported_clips.empty()) { - imported_clips.front()->RerequestCaches(); + imported_clips.front()->RequestInvalidatedFromConnected(); imported_clips.pop_front(); } @@ -523,7 +523,6 @@ TimelineViewGhostItem* ImportTool::CreateGhost(const TimeRange &range, const rat snap_points_.push_back(ghost->GetIn()); snap_points_.push_back(ghost->GetOut()); - ghost->SetMode(Timeline::kMove); parent()->AddGhost(ghost);