diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 1c175a4d5..46f9441f7 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -62,6 +62,11 @@ void FrameHashCache::ValidateTimestamp(const int64_t &ts) Validate(frame_range); } +void FrameHashCache::ValidateTime(const rational &time) +{ + Validate(TimeRange(time, time + timebase_)); +} + bool FrameHashCache::SaveCacheFrame(const int64_t &time, FramePtr frame) const { return SaveCacheFrame(GetCacheDirectory(), uuid_, time, frame); @@ -190,27 +195,6 @@ FramePtr FrameHashCache::LoadCacheFrame(const QString &fn) return frame; } -struct HashTimePair { - rational time; - QByteArray hash; -}; - -void FrameHashCache::ShiftEvent(const rational &from, const rational &to) -{ -} - -void FrameHashCache::InvalidateEvent(const TimeRange &range) -{ - TimeRangeListFrameIterator iterator({range}, timebase_); - - rational t; - while (iterator.GetNext(&t)) { - int64_t ts = ToTimestamp(t, Timecode::kCeil); - - QFile::remove(CachePathName(ts)); - } -} - 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 f56d3cc7c..014a1af7b 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -39,16 +39,25 @@ class FrameHashCache : public PlaybackCache public: FrameHashCache(QObject* parent = nullptr); + const QUuid &GetUuid() const { return uuid_; } + void SetUuid(const QUuid &u) { uuid_ = u; } + const rational &GetTimebase() const { return timebase_; } void SetTimebase(const rational& tb); void ValidateTimestamp(const int64_t &ts); + void ValidateTime(const rational &time); /** * @brief Return the path of the cached image at this time */ QString CachePathName(const int64_t &time) const; + QString CachePathName(const rational &time) const + { + return CachePathName(ToTimestamp(time, Timecode::kFloor)); + } + static QString CachePathName(const QString& cache_path, const QUuid &cache_id, const int64_t &time); static bool SaveCacheFrame(const QString& filename, FramePtr frame); @@ -58,11 +67,6 @@ public: FramePtr LoadCacheFrame(const int64_t &time) const; static FramePtr LoadCacheFrame(const QString& fn); -protected: - virtual void ShiftEvent(const rational& from, const rational& to) override; - - virtual void InvalidateEvent(const TimeRange& range) 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/previewautocacher.cpp b/app/render/previewautocacher.cpp index 386a8c98c..feada072b 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -67,19 +67,11 @@ RenderTicketPtr PreviewAutoCacher::GetSingleFrame(const rational &t, bool priori // If we have a single frame render queued (but not yet sent to the RenderManager), cancel it now CancelQueuedSingleFrameRender(); - // See if we have a hash, we may or may not retrieve one depending on the state of the video - // frame cache - QByteArray hash; - if (viewer_node_->GetVideoAutoCacheEnabled()) { - hash = viewer_node_->video_frame_cache()->GetHash(t); - } - // Create a new single frame render ticket auto sfr = std::make_shared(); sfr->Start(); sfr->setProperty("time", QVariant::fromValue(t)); sfr->setProperty("prioritize", prioritize); - sfr->setProperty("hash", hash); // Queue it and try to render single_frame_render_ = sfr; @@ -204,25 +196,27 @@ void PreviewAutoCacher::VideoRendered() // If the task list doesn't contain this watcher, presumably it was cleared as a result of a // viewer switch, so we'll completely ignore this watcher - if (video_tasks_.remove(watcher)) { + auto it = video_tasks_.find(watcher); + + if (it != video_tasks_.end()) { // Assume that a "result" is a fully completed image and a non-result is a cancelled ticket if (watcher->HasResult()) { - qDebug() << "FIXME: oops no frame downloading"; - - /* // Download frame in another thread - FramePtr frame = watcher->Get().value(); - RenderTicketWatcher* w = new RenderTicketWatcher(); - w->setProperty("job", QVariant::fromValue(last_update_time_)); - w->setProperty("frame", QVariant::fromValue(frame)); - video_download_tasks_.insert(w, hash); - connect(w, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoDownloaded); - w->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_node_->video_frame_cache(), - frame, - true)); - */ + if (FramePtr frame = watcher->Get().value()) { + RenderTicketWatcher* w = new RenderTicketWatcher(); + w->setProperty("job", QVariant::fromValue(last_update_time_)); + w->setProperty("frame", QVariant::fromValue(frame)); + video_download_tasks_.insert(w, it.value()); + connect(w, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoDownloaded); + w->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_node_->video_frame_cache(), + frame, + it.value(), + true)); + } } + video_tasks_.erase(it); + // Continue rendering TryRender(); } @@ -249,11 +243,11 @@ void PreviewAutoCacher::VideoDownloaded() // viewer switch, so we'll completely ignore this watcher if (video_download_tasks_.contains(watcher)) { // Remove from task list - QByteArray hash = video_download_tasks_.take(watcher); + rational time = video_download_tasks_.take(watcher); // Assume that `true` is a completely successful frame save if (watcher->Get().toBool()) { - viewer_node_->video_frame_cache()->ValidateFramesWithHash(hash); + viewer_node_->video_frame_cache()->ValidateTime(time); } else { qCritical() << "Failed to download video frame"; } @@ -535,11 +529,11 @@ void PreviewAutoCacher::TryRender() // Check for newly invalidated video and hash it if (!invalidated_video_.isEmpty()) { if (!copied_viewer_node_->GetConnectedTextureOutput()) { - hash_iterator_.reset(); - } else if (hash_iterator_.HasNext()) { - hash_iterator_.insert(invalidated_video_); + queued_frame_iterator_.reset(); + } else if (queued_frame_iterator_.HasNext()) { + queued_frame_iterator_.insert(invalidated_video_); } else { - hash_iterator_ = TimeRangeListFrameIterator(invalidated_video_, viewer_node_->GetVideoParams().frame_rate_as_time_base()); + queued_frame_iterator_ = TimeRangeListFrameIterator(invalidated_video_, viewer_node_->GetVideoParams().frame_rate_as_time_base()); } invalidated_video_.clear(); } @@ -552,15 +546,14 @@ void PreviewAutoCacher::TryRender() if (single_frame_render_) { // Check if already caching this - QByteArray hash = single_frame_render_->property("hash").toByteArray(); + rational time = single_frame_render_->property("time").value(); RenderTicketWatcher* watcher; - if (!hash.isEmpty() && (watcher = video_tasks_.key(hash))) { + if ((watcher = video_tasks_.key(time))) { video_immediate_passthroughs_[watcher].append(single_frame_render_); - } else if (!hash.isEmpty() && (watcher = video_download_tasks_.key(hash))) { + } else if ((watcher = video_download_tasks_.key(time))) { single_frame_render_->Finish(watcher->property("frame")); } else { - watcher = RenderFrame(hash, - single_frame_render_->property("time").value(), + watcher = RenderFrame(single_frame_render_->property("time").value(), single_frame_render_->property("prioritize").toBool(), !viewer_node_->GetVideoAutoCacheEnabled()); @@ -576,14 +569,12 @@ void PreviewAutoCacher::TryRender() // Handle video tasks rational t; while (video_tasks_.size() < max_tasks && queued_frame_iterator_.GetNext(&t)) { - const QByteArray& hash = viewer_node_->video_frame_cache()->GetHash(t); - - RenderTicketWatcher* render_task = video_tasks_.key(hash); + RenderTicketWatcher* render_task = video_tasks_.key(t); // We want this hash, if we're not already rendering, start render now - if (!render_task && !video_download_tasks_.key(hash)) { + if (!render_task && !video_download_tasks_.key(t)) { // Don't render any hash more than once - RenderFrame(hash, t, false, false); + RenderFrame(t, false, false); } emit SignalCacheProxyTaskProgress(double(queued_frame_iterator_.frame_index()) / double(queued_frame_iterator_.size())); @@ -609,13 +600,12 @@ void PreviewAutoCacher::TryRender() } } -RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const QByteArray &hash, const rational& time, bool prioritize, bool texture_only) +RenderTicketWatcher* PreviewAutoCacher::RenderFrame(const rational& time, bool prioritize, bool texture_only) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); - watcher->setProperty("hash", hash); watcher->setProperty("job", QVariant::fromValue(last_update_time_)); connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered); - video_tasks_.insert(watcher, hash); + video_tasks_.insert(watcher, time); watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, copied_color_manager_, time, @@ -751,7 +741,6 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) // Clear iterators queued_frame_iterator_.reset(); audio_iterator_.clear(); - hash_iterator_.reset(); // Clear any invalidated ranges invalidated_video_.clear(); diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 39d1db77d..1ad185877 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -100,7 +100,7 @@ signals: private: void TryRender(); - RenderTicketWatcher *RenderFrame(const QByteArray& hash, const rational &time, bool prioritize, bool texture_only); + RenderTicketWatcher *RenderFrame(const rational &time, bool prioritize, bool texture_only); RenderTicketPtr RenderAudio(const TimeRange &range, bool generate_waveforms, bool prioritize); /** @@ -173,8 +173,8 @@ private: RenderTicketPtr single_frame_render_; QMap audio_tasks_; - QMap video_tasks_; - QMap video_download_tasks_; + QMap video_tasks_; + QMap video_download_tasks_; QMap > video_immediate_passthroughs_; JobTime graph_changed_time_; @@ -190,7 +190,6 @@ private: RenderJobTracker audio_job_tracker_; TimeRangeListFrameIterator queued_frame_iterator_; - TimeRangeListFrameIterator hash_iterator_; TimeRangeList audio_iterator_; static const bool kRealTimeWaveformsEnabled; diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index f34ab44a7..49c21e3ba 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -187,13 +187,15 @@ RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange return ticket; } -RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr frame, bool prioritize) +RenderTicketPtr RenderManager::SaveFrameToCache(FrameHashCache *cache, FramePtr frame, const rational &time, bool prioritize) { // Create ticket RenderTicketPtr ticket = std::make_shared(); ticket->setProperty("cache", cache->GetCacheDirectory()); ticket->setProperty("frame", QVariant::fromValue(frame)); + ticket->setProperty("time", QVariant::fromValue(Timecode::time_to_timestamp(time, cache->GetTimebase(), Timecode::kFloor))); + ticket->setProperty("uuid", QVariant::fromValue(cache->GetUuid())); ticket->setProperty("type", kTypeVideoDownload); if (ticket->thread() != this->thread()) { diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 773389e60..c69c22770 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -98,7 +98,7 @@ public: RenderTicketPtr RenderAudio(ViewerOutput* viewer, const TimeRange& r, const AudioParams& params, RenderMode::Mode mode, bool generate_waveforms, bool prioritize = false); RenderTicketPtr RenderAudio(ViewerOutput *viewer, const TimeRange& r, RenderMode::Mode mode, bool generate_waveforms, bool prioritize = false); - RenderTicketPtr SaveFrameToCache(FrameHashCache* cache, FramePtr frame, bool prioritize = false); + RenderTicketPtr SaveFrameToCache(FrameHashCache* cache, FramePtr frame, const rational &time, bool prioritize = false); virtual void RunTicket(RenderTicketPtr ticket) const override; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index eb0ebff20..1a7d692b9 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -219,9 +219,10 @@ void RenderProcessor::Run() { QString cache = ticket_->property("cache").toString(); FramePtr frame = ticket_->property("frame").value(); - QByteArray hash = ticket_->property("hash").toByteArray(); + int64_t time = ticket_->property("time").value(); + QUuid uuid = ticket_->property("uuid").value(); - ticket_->Finish(FrameHashCache::SaveCacheFrame(cache, hash, frame)); + ticket_->Finish(FrameHashCache::SaveCacheFrame(cache, uuid, time, frame)); break; } default: diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index c9b30448a..fb199caad 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -165,7 +165,7 @@ bool RenderTask::Render(ColorManager* manager, } else if (ticket_type == RenderManager::kTypeVideo && TwoStepFrameRendering()) { - if (!DownloadFrame(&watcher_thread, watcher->Get().value())) { + if (!DownloadFrame(&watcher_thread, watcher->Get().value(), watcher->property("time").value())) { result = false; } @@ -237,14 +237,14 @@ bool RenderTask::Render(ColorManager* manager, return result; } -bool RenderTask::DownloadFrame(QThread *thread, FramePtr frame) +bool RenderTask::DownloadFrame(QThread *thread, FramePtr frame, const rational &time) { RenderTicketWatcher* watcher = new RenderTicketWatcher(); PrepareWatcher(watcher, thread); IncrementRunningTickets(); - watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(), frame)); + watcher->SetTicket(RenderManager::instance()->SaveFrameToCache(viewer_->video_frame_cache(), frame, time)); // NOTE: Doesn't reflect the actual return result of SaveFrameToCache return true; diff --git a/app/task/render/render.h b/app/task/render/render.h index ef8f4807a..338cb7df9 100644 --- a/app/task/render/render.h +++ b/app/task/render/render.h @@ -49,7 +49,7 @@ protected: VideoParams::Format force_format = VideoParams::kFormatInvalid, ColorProcessorPtr force_color_output = nullptr); - virtual bool DownloadFrame(QThread* thread, FramePtr frame); + virtual bool DownloadFrame(QThread* thread, FramePtr frame, const rational &time); virtual bool FrameDownloaded(FramePtr frame, const rational &time) = 0; diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 61f95b020..23b349a6a 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -80,17 +80,17 @@ void TimeRuler::SetPlaybackCache(PlaybackCache *cache) } if (playback_cache_) { - disconnect(playback_cache_, &PlaybackCache::Invalidated, this, static_cast(&TimeRuler::update)); - disconnect(playback_cache_, &PlaybackCache::Validated, this, static_cast(&TimeRuler::update)); - disconnect(playback_cache_, &PlaybackCache::Shifted, this, static_cast(&TimeRuler::update)); + disconnect(playback_cache_, &PlaybackCache::Invalidated, viewport(), static_cast(&QWidget::update)); + disconnect(playback_cache_, &PlaybackCache::Validated, viewport(), static_cast(&QWidget::update)); + disconnect(playback_cache_, &PlaybackCache::Shifted, viewport(), static_cast(&QWidget::update)); } playback_cache_ = cache; if (playback_cache_) { - connect(playback_cache_, &PlaybackCache::Invalidated, this, static_cast(&TimeRuler::update)); - connect(playback_cache_, &PlaybackCache::Validated, this, static_cast(&TimeRuler::update)); - connect(playback_cache_, &PlaybackCache::Shifted, this, static_cast(&TimeRuler::update)); + connect(playback_cache_, &PlaybackCache::Invalidated, viewport(), static_cast(&QWidget::update)); + connect(playback_cache_, &PlaybackCache::Validated, viewport(), static_cast(&QWidget::update)); + connect(playback_cache_, &PlaybackCache::Shifted, viewport(), static_cast(&QWidget::update)); } update(); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index f638bc67c..82afc0cc6 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -399,9 +399,9 @@ void ViewerWidget::StartCapture(TimelineWidget *source, const TimeRange &time, c recording_track_ = track; } -FramePtr ViewerWidget::DecodeCachedImage(const QString &cache_path, const QByteArray& hash, const rational& time) +FramePtr ViewerWidget::DecodeCachedImage(const QString &cache_path, const QUuid &cache_id, const int64_t& time) { - FramePtr frame = FrameHashCache::LoadCacheFrame(cache_path, hash); + FramePtr frame = FrameHashCache::LoadCacheFrame(cache_path, cache_id, time); if (frame) { frame->set_timestamp(time); @@ -412,10 +412,10 @@ FramePtr ViewerWidget::DecodeCachedImage(const QString &cache_path, const QByteA return frame; } -void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &cache_path, const QByteArray& hash, const rational& time) +void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &cache_path, const QUuid &cache_id, const int64_t& time) { ticket->Start(); - ticket->Finish(QVariant::fromValue(DecodeCachedImage(cache_path, hash, time))); + ticket->Finish(QVariant::fromValue(DecodeCachedImage(cache_path, cache_id, time))); } bool ViewerWidget::ShouldForceWaveform() const @@ -813,11 +813,7 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp QString ViewerWidget::GetCachedFilenameFromTime(const rational &time) { if (FrameExistsAtTime(time)) { - QByteArray hash = GetConnectedNode()->video_frame_cache()->GetHash(time); - - if (!hash.isEmpty()) { - return GetConnectedNode()->video_frame_cache()->CachePathName(hash); - } + return GetConnectedNode()->video_frame_cache()->CachePathName(time); } return QString(); @@ -860,18 +856,16 @@ void ViewerWidget::RequestNextFrameForQueue(bool prioritize, bool increment) RenderTicketPtr ViewerWidget::GetFrame(const rational &t, bool prioritize) { - QByteArray cached_hash = GetConnectedNode()->video_frame_cache()->GetHash(t); + QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(t); - QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(cached_hash); - - if (cached_hash.isEmpty() || !QFileInfo::exists(cache_fn)) { + if (!QFileInfo::exists(cache_fn)) { // Frame hasn't been cached, start render job return auto_cacher_.GetSingleFrame(t, prioritize); } else { // Frame has been cached, grab the frame RenderTicketPtr ticket = std::make_shared(); ticket->setProperty("time", QVariant::fromValue(t)); - QtConcurrent::run(ViewerWidget::DecodeCachedImage, ticket, GetConnectedNode()->video_frame_cache()->GetCacheDirectory(), cached_hash, t); + QtConcurrent::run(ViewerWidget::DecodeCachedImage, ticket, GetConnectedNode()->video_frame_cache()->GetCacheDirectory(), GetConnectedNode()->video_frame_cache()->GetUuid(), Timecode::time_to_timestamp(t, timebase(), Timecode::kFloor)); return ticket; } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index ba7f7be56..273703f60 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -193,9 +193,9 @@ private: int DeterminePlaybackQueueSize(); - static FramePtr DecodeCachedImage(const QString &cache_path, const QByteArray &hash, const rational& time); + static FramePtr DecodeCachedImage(const QString &cache_path, const QUuid &cache_id, const int64_t& time); - static void DecodeCachedImage(RenderTicketPtr ticket, const QString &cache_path, const QByteArray &hash, const rational& time); + static void DecodeCachedImage(RenderTicketPtr ticket, const QString &cache_path, const QUuid &cache_id, const int64_t &time); bool ShouldForceWaveform() const;