diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index 83d65e9f7..a9cbad326 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -296,6 +296,68 @@ uint qHash(const TimeRange &r, uint seed) return qHash(r.in(), seed) ^ qHash(r.out(), seed); } +TimeRangeListFrameIterator::TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase) : + list_(list), + timebase_(timebase), + index_(-1), + size_(-1) +{ + UpdateIndexIfNecessary(); +} + +bool TimeRangeListFrameIterator::GetNext(rational *out) +{ + if (index_ == list_.size()) { + return false; + } + + // Output current value + *out = current_; + + // Determine next value by adding timebase + current_ += timebase_; + + // If this time is outside the current range, jump to the next one + UpdateIndexIfNecessary(); + + return true; +} + +int TimeRangeListFrameIterator::size() +{ + if (size_ == -1) { + // Size isn't calculated automatically for optimization, so we'll calculate it now + size_ = 0; + + foreach (const TimeRange &range, list_) { + rational start = Timecode::snap_time_to_timebase(range.in(), timebase_, Timecode::kCeil); + rational end = Timecode::snap_time_to_timebase(range.out(), timebase_, Timecode::kFloor); + + if (end == range.out()) { + end -= timebase_; + } + + int64_t start_ts = Timecode::time_to_timestamp(start, timebase_); + int64_t end_ts = Timecode::time_to_timestamp(end, timebase_); + + size_ += 1 + (end_ts - start_ts); + } + } + + return size_; +} + +void TimeRangeListFrameIterator::UpdateIndexIfNecessary() +{ + while (index_ < list_.size() && (index_ == -1 || current_ >= list_.at(index_).out())) { + index_++; + + if (index_ < list_.size()) { + current_ = Timecode::snap_time_to_timebase(list_.at(index_).in(), timebase_, Timecode::kCeil); + } + } +} + } QDebug operator<<(QDebug debug, const olive::TimeRange &r) diff --git a/app/common/timerange.h b/app/common/timerange.h index 28913f6df..78c95d8db 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -22,6 +22,7 @@ #define TIMERANGE_H #include "rational.h" +#include "timecodefunctions.h" namespace olive { @@ -127,16 +128,61 @@ public: return array_.last(); } + const TimeRange& at(int index) const + { + return array_.at(index); + } + const QVector& internal_array() const { return array_; } + bool operator==(const TimeRangeList &rhs) const + { + return array_ == rhs.array_; + } + private: QVector array_; }; +class TimeRangeListFrameIterator +{ +public: + TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase); + + bool GetNext(rational *out); + + QVector ToVector() const + { + TimeRangeListFrameIterator copy(list_, timebase_); + QVector times; + rational r; + while (copy.GetNext(&r)) { + times.append(r); + } + return times; + } + + int size(); + +private: + void UpdateIndexIfNecessary(); + + TimeRangeList list_; + + rational timebase_; + + rational current_; + + int index_; + + int size_; + +}; + uint qHash(const TimeRange& r, uint seed = 0); } diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 84857614d..52e6028f2 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -114,77 +114,6 @@ void FrameHashCache::ValidateFramesWithHash(const QByteArray &hash) } } -QList FrameHashCache::GetFramesWithHash(const QByteArray &hash) -{ - QList times; - - for (int64_t i=0; i FrameHashCache::TakeFramesWithHash(const QByteArray &hash) -{ - TimeRangeList range_to_invalidate; - QList times; - - for (int64_t i=0; i FrameHashCache::GetFrameListFromTimeRange(TimeRangeList range_list, const rational &timebase) -{ - // If timebase is null, this will be an infinite loop - Q_ASSERT(!timebase.isNull()); - - QVector times; - - foreach (const TimeRange &range, range_list) { - rational frame = Timecode::snap_time_to_timebase(range.in(), timebase, Timecode::kCeil); - - while (frame < range.out()) { - times.append(frame); - frame += timebase; - } - } - - return times; -} - -QVector FrameHashCache::GetFrameListFromTimeRange(const TimeRangeList &range) -{ - return GetFrameListFromTimeRange(range, timebase_); -} - -QVector FrameHashCache::GetInvalidatedFrames() -{ - return GetFrameListFromTimeRange(GetInvalidatedRanges()); -} - -QVector FrameHashCache::GetInvalidatedFrames(const TimeRange &intersecting) -{ - return GetFrameListFromTimeRange(GetInvalidatedRanges().Intersects(intersecting)); -} - bool FrameHashCache::SaveCacheFrame(const QByteArray& hash, char* data, const VideoParams& vparam, diff --git a/app/render/framehashcache.h b/app/render/framehashcache.h index 56753c199..b6904ec51 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -50,16 +50,6 @@ public: void ValidateFramesWithHash(const QByteArray& hash); - /** - * @brief Returns a list of frames that use a particular hash - */ - QList GetFramesWithHash(const QByteArray& hash); - - /** - * @brief Same as FramesWithHash() but also removes these frames from the map - */ - QList TakeFramesWithHash(const QByteArray& hash); - QMap time_hash_map(); /** @@ -77,11 +67,6 @@ public: FramePtr LoadCacheFrame(const QByteArray& hash) const; static FramePtr LoadCacheFrame(const QString& fn); - static QVector GetFrameListFromTimeRange(TimeRangeList range_list, const rational& timebase); - QVector GetFrameListFromTimeRange(const TimeRangeList &range); - QVector GetInvalidatedFrames(); - QVector GetInvalidatedFrames(const TimeRange& intersecting); - public slots: void SetHash(const olive::rational &time, const QByteArray& hash, const qint64 &job_time, bool frame_exists); diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 916be3318..5c2f70ac2 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -91,8 +91,10 @@ void GenerateHashesInternal(ViewerOutput *viewer, FrameHashCache* cache, const Q } } -void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cache, const QVector ×, qint64 job_time) +void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cache, TimeRangeListFrameIterator iterator, qint64 job_time) { + QVector times = iterator.ToVector(); + // Ensure number of threads doesn't exceed idealThreadCount for maximum concurrency int hashes_per_thread = times.size() / qMax(1, QThread::idealThreadCount()-1); @@ -493,7 +495,7 @@ void PreviewAutoCacher::TryRender() // If we're here, we must be able to render if (!invalidated_video_.isEmpty()) { - QVector frames = viewer_node_->video_frame_cache()->GetFrameListFromTimeRange(invalidated_video_); + TimeRangeListFrameIterator frames(invalidated_video_, viewer_node_->video_frame_cache()->GetTimebase()); QFutureWatcher* watcher = new QFutureWatcher(); hash_tasks_.append(watcher); @@ -578,9 +580,11 @@ void PreviewAutoCacher::RequeueFrames() using_range = cache_range_; } - QVector invalidated_ranges = viewer_node_->video_frame_cache()->GetInvalidatedFrames(using_range); + TimeRangeList invalidated = viewer_node_->video_frame_cache()->GetInvalidatedRanges().Intersects(using_range); + TimeRangeListFrameIterator invalidated_ranges(invalidated, viewer_node_->video_frame_cache()->GetTimebase()); - foreach (const rational& t, invalidated_ranges) { + rational t; + while (invalidated_ranges.GetNext(&t)) { const QByteArray& hash = viewer_node_->video_frame_cache()->GetHash(t); RenderTicketWatcher* render_task = video_tasks_.key(hash); diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 6d0bd4f44..2c40ba7dc 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -80,7 +80,7 @@ public: void ClearVideoDownloadQueue(bool wait = false); private: - static void GenerateHashes(ViewerOutput *viewer, FrameHashCache *cache, const QVector& times, qint64 job_time); + static void GenerateHashes(ViewerOutput *viewer, FrameHashCache *cache, TimeRangeListFrameIterator times, qint64 job_time); void TryRender(); diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index ee559fe2c..2254477a5 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -84,16 +84,19 @@ bool RenderTask::Render(ColorManager* manager, if (!video_range.isEmpty()) { // Get list of discrete frames from range - QVector times = FrameHashCache::GetFrameListFromTimeRange(video_range, video_params().frame_rate_as_time_base()); - QVector hashes(times.size()); + TimeRangeListFrameIterator iterator(video_range, video_params().frame_rate_as_time_base()); + QVector times(iterator.size()); + QVector hashes(iterator.size()); // Generate hashes - for (int i=0; iHash(viewer()->GetConnectedTextureOutput(), video_params_, times.at(i)); + times[i] = r; + hashes[i] = RenderManager::instance()->Hash(viewer()->GetConnectedTextureOutput(), video_params_, r); } // Filter out duplicates diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt index 11f3472ed..7f87c6302 100644 --- a/tests/general/CMakeLists.txt +++ b/tests/general/CMakeLists.txt @@ -16,3 +16,4 @@ olive_add_test(General common-tests common-tests.cpp) olive_add_test(General rational-tests rational-tests.cpp) +olive_add_test(General timerange-tests timerange-tests.cpp) diff --git a/tests/testutil.h b/tests/testutil.h index 9f916c2d0..3cfd6845d 100644 --- a/tests/testutil.h +++ b/tests/testutil.h @@ -21,6 +21,7 @@ #include #define OLIVE_ASSERT(x) if (!(x)) return false +#define OLIVE_ASSERT_EQUAL(x, y) if (x != y) {std::cout << " - Equal assert failed on line " << __LINE__ << ": " << x << " != " << y; return false;}void() #define OLIVE_TEST_END return true #define OLIVE_ADD_TEST(x) bool Test##x()