From 3aeba28263bab194b2592cf68acce946d5b08fc5 Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Sat, 7 Oct 2023 16:15:22 -0700 Subject: [PATCH] Cache looping fixes --- lib/tlTimeline/PlayerPrivate.cpp | 74 +++++++++++++++---- lib/tlTimeline/PlayerPrivate.h | 8 +- lib/tlTimeline/Util.cpp | 110 +++++++++++++++++++++------- lib/tlTimeline/Util.h | 16 +++- tests/tlTimelineTest/PlayerTest.cpp | 67 +++++++++++++---- 5 files changed, 209 insertions(+), 66 deletions(-) diff --git a/lib/tlTimeline/PlayerPrivate.cpp b/lib/tlTimeline/PlayerPrivate.cpp index 73bb91851..8cb82a2de 100644 --- a/lib/tlTimeline/PlayerPrivate.cpp +++ b/lib/tlTimeline/PlayerPrivate.cpp @@ -131,7 +131,10 @@ namespace tl } //std::cout << "in out range: " << inOutRange << std::endl; //std::cout << "video range: " << videoRange << std::endl; - const auto videoRanges = timeline::loop(videoRange, inOutRange); + const auto videoRanges = timeline::loopCache( + videoRange, + inOutRange, + cacheDirection); //for (const auto& i : videoRanges) //{ // std::cout << "video ranges: " << i << std::endl; @@ -168,7 +171,10 @@ namespace tl inOutRange.end_time_inclusive() + audioOffsetAhead). clamped(timeRange); //std::cout << "in out audio range: " << inOutAudioRange << std::endl; - const auto audioRanges = timeline::loop(audioRange, inOutAudioRange); + const auto audioRanges = timeline::loopCache( + audioRange, + inOutAudioRange, + cacheDirection); // Remove old video from the cache. auto videoCacheIt = thread.videoDataCache.begin(); @@ -227,21 +233,49 @@ namespace tl { for (const auto& range : videoRanges) { - const auto start = range.start_time(); - const auto end = range.end_time_exclusive(); - const auto inc = otime::RationalTime(1.0, range.duration().rate()); - for (auto time = start; time < end; time += inc) + switch (cacheDirection) { - const auto i = thread.videoDataCache.find(time); - if (i == thread.videoDataCache.end()) + case CacheDirection::Forward: + { + const auto start = range.start_time(); + const auto end = range.end_time_inclusive(); + const auto inc = otime::RationalTime(1.0, range.duration().rate()); + for (auto time = start; time <= end; time += inc) + { + const auto i = thread.videoDataCache.find(time); + if (i == thread.videoDataCache.end()) + { + const auto j = thread.videoDataRequests.find(time); + if (j == thread.videoDataRequests.end()) + { + //std::cout << this << " video request: " << time << std::endl; + thread.videoDataRequests[time] = timeline->getVideo(time, videoLayer); + } + } + } + break; + } + case CacheDirection::Reverse: + { + const auto start = range.end_time_inclusive(); + const auto end = range.start_time(); + const auto inc = otime::RationalTime(1.0, range.duration().rate()); + for (auto time = start; time >= end; time -= inc) { - const auto j = thread.videoDataRequests.find(time); - if (j == thread.videoDataRequests.end()) + const auto i = thread.videoDataCache.find(time); + if (i == thread.videoDataCache.end()) { - //std::cout << this << " video request: " << time << std::endl; - thread.videoDataRequests[time] = timeline->getVideo(time, videoLayer); + const auto j = thread.videoDataRequests.find(time); + if (j == thread.videoDataRequests.end()) + { + //std::cout << this << " video request: " << time << std::endl; + thread.videoDataRequests[time] = timeline->getVideo(time, videoLayer); + } } } + break; + } + default: break; } } } @@ -276,9 +310,21 @@ namespace tl } } } - for (auto i : requests) + switch (cacheDirection) { - thread.audioDataRequests[i.first] = timeline->getAudio(i.second); + case CacheDirection::Forward: + for (auto i = requests.begin(); i != requests.end(); ++i) + { + thread.audioDataRequests[i->first] = timeline->getAudio(i->second); + } + break; + case CacheDirection::Reverse: + for (auto i = requests.rbegin(); i != requests.rend(); ++i) + { + thread.audioDataRequests[i->first] = timeline->getAudio(i->second); + } + break; + default: break; } /*if (!requests.empty()) { diff --git a/lib/tlTimeline/PlayerPrivate.h b/lib/tlTimeline/PlayerPrivate.h index 97fb52951..4af39633d 100644 --- a/lib/tlTimeline/PlayerPrivate.h +++ b/lib/tlTimeline/PlayerPrivate.h @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -21,12 +23,6 @@ namespace tl { namespace timeline { - enum class CacheDirection - { - Forward, - Reverse - }; - struct Player::Private { otime::RationalTime loopPlayback(const otime::RationalTime&); diff --git a/lib/tlTimeline/Util.cpp b/lib/tlTimeline/Util.cpp index 46cf4dc60..4625ea247 100644 --- a/lib/tlTimeline/Util.cpp +++ b/lib/tlTimeline/Util.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -76,37 +77,92 @@ namespace tl return out; } - std::vector loop( + std::vector loopCache( const otime::TimeRange& value, - const otime::TimeRange& range) + const otime::TimeRange& range, + CacheDirection direction) { std::vector out; - if (value.duration() >= range.duration()) - { - out.push_back(range); - } - else if (value.start_time() >= range.start_time() && - value.end_time_inclusive() <= range.end_time_inclusive()) - { - out.push_back(value); - } - else if (value.start_time() < range.start_time()) + const otime::RationalTime min = std::min(value.duration(), range.duration()); + switch (direction) { - out.push_back(otime::TimeRange::range_from_start_end_time_inclusive( - range.end_time_exclusive() - (range.start_time() - value.start_time()), - range.end_time_inclusive())); - out.push_back(otime::TimeRange::range_from_start_end_time_inclusive( - range.start_time(), - value.end_time_inclusive())); - } - else if (value.end_time_inclusive() > range.end_time_inclusive()) - { - out.push_back(otime::TimeRange::range_from_start_end_time_inclusive( - value.start_time(), - range.end_time_inclusive())); - out.push_back(otime::TimeRange::range_from_start_end_time_inclusive( - range.start_time(), - range.start_time() + (value.end_time_inclusive() - range.end_time_exclusive()))); + case CacheDirection::Forward: + if (value.start_time() < range.start_time()) + { + const otime::TimeRange a(range.start_time(), min); + TLRENDER_ASSERT(a.duration() == min); + out.push_back(a); + } + else if (value.start_time() > range.end_time_inclusive()) + { + const otime::TimeRange a(range.end_time_exclusive() - min, min); + TLRENDER_ASSERT(a.duration() == min); + out.push_back(a); + } + else if (value.end_time_inclusive() > range.end_time_exclusive()) + { + const otime::TimeRange clamped(value.start_time(), min); + const otime::TimeRange a = otime::TimeRange::range_from_start_end_time_inclusive( + clamped.start_time(), + range.end_time_inclusive()); + const otime::TimeRange b = otime::TimeRange( + range.start_time(), + clamped.duration() - a.duration()); + TLRENDER_ASSERT(a.duration() + b.duration() == min); + if (a.duration().value() > 0.0) + { + out.push_back(a); + } + if (b.duration().value() > 0.0) + { + out.push_back(b); + } + } + else + { + out.push_back(value); + } + break; + case CacheDirection::Reverse: + if (value.end_time_inclusive() > range.end_time_inclusive()) + { + const otime::TimeRange a(range.end_time_exclusive() - min, min); + out.push_back(a); + TLRENDER_ASSERT(a.duration() == min); + } + else if (value.end_time_inclusive() < range.start_time()) + { + const otime::TimeRange a(range.start_time(), min); + out.push_back(a); + TLRENDER_ASSERT(a.duration() == min); + } + else if (value.start_time() < range.start_time()) + { + const otime::TimeRange clamped = otime::TimeRange::range_from_start_end_time_inclusive( + value.end_time_exclusive() - min, + value.end_time_inclusive()); + const otime::TimeRange a = otime::TimeRange::range_from_start_end_time_inclusive( + range.start_time(), + clamped.end_time_inclusive()); + const otime::TimeRange b = otime::TimeRange::range_from_start_end_time_inclusive( + range.end_time_exclusive() - (clamped.duration() - a.duration()), + range.end_time_inclusive()); + TLRENDER_ASSERT(a.duration() + b.duration() == min); + if (a.duration().value() > 0.0) + { + out.push_back(a); + } + if (b.duration().value() > 0.0) + { + out.push_back(b); + } + } + else + { + out.push_back(value); + } + break; + default: break; } return out; } diff --git a/lib/tlTimeline/Util.h b/lib/tlTimeline/Util.h index 5ca537799..b86a5322d 100644 --- a/lib/tlTimeline/Util.h +++ b/lib/tlTimeline/Util.h @@ -4,7 +4,7 @@ #pragma once -#include +#include #include @@ -21,10 +21,18 @@ namespace tl const otime::TimeRange&, bool* looped = nullptr); - //! Loop a range. - std::vector loop( + //! Cache direction. + enum class CacheDirection + { + Forward, + Reverse + }; + + //! Loop the cache time range. + std::vector loopCache( + const otime::TimeRange&, const otime::TimeRange&, - const otime::TimeRange&); + CacheDirection); //! Get the root (highest parent). const otio::Composable* getRoot(const otio::Composable*); diff --git a/tests/tlTimelineTest/PlayerTest.cpp b/tests/tlTimelineTest/PlayerTest.cpp index fe91fea9a..d6c7f06b2 100644 --- a/tests/tlTimelineTest/PlayerTest.cpp +++ b/tests/tlTimelineTest/PlayerTest.cpp @@ -57,27 +57,64 @@ namespace tl TLRENDER_ASSERT(otime::RationalTime(23.0, 24.0) == loop(otime::RationalTime(-1.0, 24.0), timeRange)); } { - const otime::TimeRange timeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)); - auto ranges = loop(otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), timeRange); + auto ranges = loopCache( + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Forward); TLRENDER_ASSERT(1 == ranges.size()); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)) == ranges[0]); - ranges = loop(otime::TimeRange(otime::RationalTime(-10.0, 24.0), otime::RationalTime(34.0, 24.0)), timeRange); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0))); + + ranges = loopCache( + otime::TimeRange(otime::RationalTime(24.0, 24.0), otime::RationalTime(12.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Forward); TLRENDER_ASSERT(1 == ranges.size()); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)) == ranges[0]); - ranges = loop(otime::TimeRange(otime::RationalTime(-10.0, 24.0), otime::RationalTime(20.0, 24.0)), timeRange); - TLRENDER_ASSERT(2 == ranges.size()); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(14.0, 24.0), otime::RationalTime(10.0, 24.0)) == ranges[0]); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(10.0, 24.0)) == ranges[1]); - ranges = loop(otime::TimeRange(otime::RationalTime(10.0, 24.0), otime::RationalTime(20.0, 24.0)), timeRange); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(12.0, 24.0), otime::RationalTime(12.0, 24.0))); + + ranges = loopCache( + otime::TimeRange(otime::RationalTime(-12.0, 24.0), otime::RationalTime(12.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Forward); + TLRENDER_ASSERT(1 == ranges.size()); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(12.0, 24.0))); + + ranges = loopCache( + otime::TimeRange(otime::RationalTime(10.0, 24.0), otime::RationalTime(48.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Forward); TLRENDER_ASSERT(2 == ranges.size()); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(10.0, 24.0), otime::RationalTime(14.0, 24.0)) == ranges[0]); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(6.0, 24.0)) == ranges[1]); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(10.0, 24.0), otime::RationalTime(14.0, 24.0))); + TLRENDER_ASSERT(ranges[1] == otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(10.0, 24.0))); } { - const otime::TimeRange timeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(1.0, 24.0)); - auto ranges = loop(otime::TimeRange(otime::RationalTime(-1.0, 24.0), otime::RationalTime(2.0, 24.0)), timeRange); + auto ranges = loopCache( + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Reverse); TLRENDER_ASSERT(1 == ranges.size()); - TLRENDER_ASSERT(otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(1.0, 24.0)) == ranges[0]); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0))); + + ranges = loopCache( + otime::TimeRange(otime::RationalTime(24.0, 24.0), otime::RationalTime(12.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Reverse); + TLRENDER_ASSERT(1 == ranges.size()); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(12.0, 24.0), otime::RationalTime(12.0, 24.0))); + + ranges = loopCache( + otime::TimeRange(otime::RationalTime(-12.0, 24.0), otime::RationalTime(12.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Reverse); + TLRENDER_ASSERT(1 == ranges.size()); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(12.0, 24.0))); + + ranges = loopCache( + otime::TimeRange(otime::RationalTime(-10.0, 24.0), otime::RationalTime(24.0, 24.0)), + otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), + CacheDirection::Reverse); + TLRENDER_ASSERT(2 == ranges.size()); + TLRENDER_ASSERT(ranges[0] == otime::TimeRange(otime::RationalTime(0.0, 24.0), otime::RationalTime(14.0, 24.0))); + TLRENDER_ASSERT(ranges[1] == otime::TimeRange(otime::RationalTime(14.0, 24.0), otime::RationalTime(10.0, 24.0))); } }