diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 8e15a15e3..e0aaa25bf 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -311,4 +311,23 @@ void ClipBlock::Retranslate() SetInputName(kMaintainAudioPitchInput, tr("Maintain Audio Pitch")); } +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) { + //emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); + } + } + } +} + } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index a725f147f..feff9c4d2 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -158,6 +158,8 @@ public: return TimeRange(0, length()); } + virtual void ConnectedToPreviewEvent() override; + static const QString kBufferIn; static const QString kMediaInInput; static const QString kSpeedInput; diff --git a/app/node/node.h b/app/node/node.h index 324af8089..13beb3d1c 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -991,6 +991,7 @@ public: void SetInputFlags(const QString &input, const InputFlags &f); virtual void LoadFinishedEvent(){} + virtual void ConnectedToPreviewEvent(){} static void SetValueAtTime(const NodeInput &input, const rational &time, const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key); diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 2075b6ce8..00e5dc25b 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -544,6 +544,18 @@ void ProjectSerializer220403::LoadNode(Node *node, XMLNodeData &xml_node_data, Q reader->skipCurrentElement(); } } + } else if (reader->name() == QStringLiteral("caches")) { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("audio")) { + node->audio_playback_cache()->SetUuid(reader->readElementText()); + } else if (reader->name() == QStringLiteral("video")) { + node->video_frame_cache()->SetUuid(reader->readElementText()); + } else if (reader->name() == QStringLiteral("thumb")) { + node->thumbnail_cache()->SetUuid(reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } } else { reader->skipCurrentElement(); } @@ -599,6 +611,14 @@ void ProjectSerializer220403::SaveNode(Node *node, QXmlStreamWriter *writer) con } writer->writeEndElement(); + writer->writeStartElement(QStringLiteral("caches")); + + writer->writeTextElement(QStringLiteral("audio"), node->audio_playback_cache()->GetUuid().toString()); + writer->writeTextElement(QStringLiteral("video"), node->video_frame_cache()->GetUuid().toString()); + writer->writeTextElement(QStringLiteral("thumb"), node->thumbnail_cache()->GetUuid().toString()); + + writer->writeEndElement(); // caches + writer->writeStartElement(QStringLiteral("custom")); SaveNodeCustom(writer, node); diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 86f3dbba1..decdbcc3d 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -193,6 +193,32 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) return frame; } +void FrameHashCache::LoadStateEvent(QDataStream &stream) +{ + uint32_t version; + int num, den; + + stream >> version; + + switch (version) { + case 1: + stream >> num; + stream >> den; + timebase_ = rational(num, den); + break; + } +} + +void FrameHashCache::SaveStateEvent(QDataStream &stream) +{ + uint32_t version = 1; + + stream << version; + + stream << timebase_.numerator(); + stream << timebase_.denominator(); +} + rational FrameHashCache::ToTime(const int64_t &ts) const { return Timecode::timestamp_to_time(ts, timebase_); diff --git a/app/render/framehashcache.h b/app/render/framehashcache.h index 71d53223d..460d33fee 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -65,6 +65,10 @@ public: FramePtr LoadCacheFrame(const int64_t &time) const; static FramePtr LoadCacheFrame(const QString& fn); +protected: + virtual void LoadStateEvent(QDataStream &stream) override; + virtual void SaveStateEvent(QDataStream &stream) override; + private: rational ToTime(const int64_t &ts) const; int64_t ToTimestamp(const rational &ts, Timecode::Rounding rounding = Timecode::kRound) const; diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index 762393223..3d5db2258 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -60,6 +60,77 @@ QDir PlaybackCache::GetThisCacheDirectory(const QString &cache_path, const QUuid return QDir(cache_path).filePath(cache_id.toString()); } +void PlaybackCache::LoadState() +{ + QDir cache_dir = GetThisCacheDirectory(); + QFile f(cache_dir.filePath(QStringLiteral("state"))); + if (f.open(QFile::ReadOnly)) { + QDataStream s(&f); + + uint32_t version; + s >> version; + + LoadStateEvent(s); + + int count; + s >> count; + + switch (version) { + case 1: + validated_.clear(); + + for (int i=0; i> in_num; + s >> in_den; + s >> out_num; + s >> out_den; + + validated_.insert(TimeRange(rational(in_num, in_den), rational(out_num, out_den))); + } + break; + } + + f.close(); + + f.close(); + } +} + +void PlaybackCache::SaveState() +{ + QDir cache_dir = GetThisCacheDirectory(); + QFile f(cache_dir.filePath(QStringLiteral("state"))); + if (validated_.isEmpty()) { + if (f.exists()) { + f.remove(); + } + } else { + if (FileFunctions::DirectoryIsValid(cache_dir)) { + if (f.open(QFile::WriteOnly)) { + QDataStream s(&f); + + uint32_t version = 1; + s << version; + + SaveStateEvent(s); + + s << validated_.size(); + + for (const TimeRange &r : validated_) { + s << r.in().numerator(); + s << r.in().denominator(); + s << r.out().numerator(); + s << r.out().denominator(); + } + + f.close(); + } + } + } +} + void PlaybackCache::InvalidateAll() { Invalidate(TimeRange(0, RATIONAL_MAX)); diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index 64045257b..e5c7db383 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -74,6 +74,9 @@ public: kPreviewsOnly }; + void LoadState(); + void SaveState(); + public slots: void InvalidateAll(); @@ -91,6 +94,10 @@ protected: virtual void InvalidateEvent(const TimeRange& range); + virtual void LoadStateEvent(QDataStream &stream){} + + virtual void SaveStateEvent(QDataStream &stream){} + Project* GetProject() const; private: diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 3e8b35e16..ef48d6284 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -345,6 +345,10 @@ void PreviewAutoCacher::ConnectToNodeCache(Node *node) this, &PreviewAutoCacher::AudioInvalidatedFromCache); + node->video_frame_cache()->LoadState(); + node->audio_playback_cache()->LoadState(); + node->thumbnail_cache()->LoadState(); + // Copy invalidated ranges and start rendering if necessary if (node->video_frame_cache()->IsAutomatic()) { VideoAutoCacheEnableChangedFromNode(node, true); @@ -382,6 +386,10 @@ void PreviewAutoCacher::DisconnectFromNodeCache(Node *node) this, &PreviewAutoCacher::AudioInvalidatedFromCache); + node->video_frame_cache()->SaveState(); + node->audio_playback_cache()->SaveState(); + node->thumbnail_cache()->SaveState(); + if (node->video_frame_cache()->IsAutomatic()) { VideoAutoCacheEnableChangedFromNode(node, false); } @@ -841,6 +849,9 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) for (int i=copied_project_.nodes().size(); inodes().size(); i++) { AddNode(graph->nodes().at(i)); } + for (int i=0; inodes().size(); i++) { + graph->nodes().at(i)->ConnectedToPreviewEvent(); + } // Find copied viewer node copied_viewer_node_ = static_cast(copy_map_.value(viewer_node_)); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 432f3b3ca..d0ad7d6e0 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -837,7 +837,7 @@ void ViewerWidget::PushScrubbedAudio() { if (!IsPlaying() && GetConnectedNode() && OLIVE_CONFIG("AudioScrubbing").toBool() && enable_audio_scrubbing_) { // Get audio src device from renderer - const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters(); + const AudioParams& params = GetConnectedNode()->GetAudioParams(); if (params.is_valid()) { // NOTE: Hardcoded scrubbing interval (20ms)