diff --git a/lib/tlBakeApp/App.cpp b/lib/tlBakeApp/App.cpp index 2e1c9ce94..93e8209a6 100644 --- a/lib/tlBakeApp/App.cpp +++ b/lib/tlBakeApp/App.cpp @@ -364,7 +364,7 @@ namespace tl _render->begin(_renderSize); _render->setOCIOOptions(_options.ocioOptions); _render->setLUTOptions(_options.lutOptions); - const auto videoData = _timeline->getVideo(_inputTime).get(); + const auto videoData = _timeline->getVideo(_inputTime).future.get(); _render->drawVideo( { videoData }, { math::Box2i(0, 0, _renderSize.w, _renderSize.h) }); diff --git a/lib/tlTimeline/Player.cpp b/lib/tlTimeline/Player.cpp index dd4418341..df6ae7cb5 100644 --- a/lib/tlTimeline/Player.cpp +++ b/lib/tlTimeline/Player.cpp @@ -220,10 +220,23 @@ namespace tl // Clear requests. if (clearRequests) { - p.timeline->cancelRequests(); - for (const auto& i : p.thread.compare) + std::vector > ids; + ids.resize(1 + p.thread.compare.size()); + for (const auto& i : p.thread.videoDataRequests) { - i->cancelRequests(); + for (size_t j = 0; j < i.second.size() && j < ids.size(); ++j) + { + ids[j].push_back(i.second[j].id); + } + } + for (const auto& i : p.thread.audioDataRequests) + { + ids[0].push_back(i.second.id); + } + p.timeline->cancelRequests(ids[0]); + for (size_t i = 0; i < p.thread.compare.size(); ++i) + { + p.thread.compare[i]->cancelRequests(ids[i + 1]); } p.thread.videoDataRequests.clear(); p.thread.audioDataRequests.clear(); diff --git a/lib/tlTimeline/PlayerPrivate.cpp b/lib/tlTimeline/PlayerPrivate.cpp index 5c2e390ea..4d3092786 100644 --- a/lib/tlTimeline/PlayerPrivate.cpp +++ b/lib/tlTimeline/PlayerPrivate.cpp @@ -386,8 +386,8 @@ namespace tl videoDataRequestIt != videoDataRequestsIt->second.end(); ++videoDataRequestIt) { - ready &= videoDataRequestIt->valid() && - videoDataRequestIt->wait_for(std::chrono::seconds(0)) == std::future_status::ready; + ready &= videoDataRequestIt->future.valid() && + videoDataRequestIt->future.wait_for(std::chrono::seconds(0)) == std::future_status::ready; } if (ready) { @@ -397,7 +397,7 @@ namespace tl videoDataRequestIt != videoDataRequestsIt->second.end(); ++videoDataRequestIt) { - auto data = videoDataRequestIt->get(); + auto data = videoDataRequestIt->future.get(); data.time = time; thread.videoDataCache[data.time].push_back(data); } @@ -413,10 +413,10 @@ namespace tl auto audioDataRequestsIt = thread.audioDataRequests.begin(); while (audioDataRequestsIt != thread.audioDataRequests.end()) { - if (audioDataRequestsIt->second.valid() && - audioDataRequestsIt->second.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + if (audioDataRequestsIt->second.future.valid() && + audioDataRequestsIt->second.future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - auto audioData = audioDataRequestsIt->second.get(); + auto audioData = audioDataRequestsIt->second.future.get(); audioData.seconds = audioDataRequestsIt->first; { std::unique_lock lock(audioMutex.mutex); diff --git a/lib/tlTimeline/PlayerPrivate.h b/lib/tlTimeline/PlayerPrivate.h index 4a16f98e4..85153909d 100644 --- a/lib/tlTimeline/PlayerPrivate.h +++ b/lib/tlTimeline/PlayerPrivate.h @@ -118,12 +118,12 @@ namespace tl CacheDirection cacheDirection = CacheDirection::Forward; PlayerCacheOptions cacheOptions; - std::map > > videoDataRequests; + std::map > videoDataRequests; std::map > videoDataCache; #if defined(TLRENDER_AUDIO) std::unique_ptr rtAudio; #endif // TLRENDER_AUDIO - std::map > audioDataRequests; + std::map audioDataRequests; std::chrono::steady_clock::time_point cacheTimer; std::chrono::steady_clock::time_point logTimer; std::atomic running; diff --git a/lib/tlTimeline/Timeline.cpp b/lib/tlTimeline/Timeline.cpp index dacf67f6d..1591a49a7 100644 --- a/lib/tlTimeline/Timeline.cpp +++ b/lib/tlTimeline/Timeline.cpp @@ -251,15 +251,19 @@ namespace tl return _p->ioInfo; } - std::future Timeline::getVideo( + VideoRequest Timeline::getVideo( const otime::RationalTime& time, const io::Options& options) { TLRENDER_P(); + (p.requestId)++; auto request = std::make_shared(); + request->id = p.requestId; request->time = time; request->options = options; - auto future = request->promise.get_future(); + VideoRequest out; + out.id = p.requestId; + out.future = request->promise.get_future(); bool valid = false; { std::unique_lock lock(p.mutex.mutex); @@ -277,18 +281,22 @@ namespace tl { request->promise.set_value(VideoData()); } - return future; + return out; } - std::future Timeline::getAudio( + AudioRequest Timeline::getAudio( double seconds, const io::Options& options) { TLRENDER_P(); + (p.requestId)++; auto request = std::make_shared(); + request->id = p.requestId; request->seconds = seconds; request->options = options; - auto future = request->promise.get_future(); + AudioRequest out; + out.id = p.requestId; + out.future = request->promise.get_future(); bool valid = false; { std::unique_lock lock(p.mutex.mutex); @@ -306,26 +314,42 @@ namespace tl { request->promise.set_value(AudioData()); } - return future; + return out; } - void Timeline::cancelRequests() + void Timeline::cancelRequests(const std::vector& ids) { TLRENDER_P(); - std::list > videoRequests; - std::list > audioRequests; - { - std::unique_lock lock(p.mutex.mutex); - videoRequests = std::move(p.mutex.videoRequests); - audioRequests = std::move(p.mutex.audioRequests); - } - for (auto& request : videoRequests) + std::unique_lock lock(p.mutex.mutex); { - request->promise.set_value(VideoData()); + auto i = p.mutex.videoRequests.begin(); + while (i != p.mutex.videoRequests.end()) + { + const auto j = std::find(ids.begin(), ids.end(), (*i)->id); + if (j != ids.end()) + { + i = p.mutex.videoRequests.erase(i); + } + else + { + ++i; + } + } } - for (auto& request : audioRequests) { - request->promise.set_value(AudioData()); + auto i = p.mutex.audioRequests.begin(); + while (i != p.mutex.audioRequests.end()) + { + const auto j = std::find(ids.begin(), ids.end(), (*i)->id); + if (j != ids.end()) + { + i = p.mutex.audioRequests.erase(i); + } + else + { + ++i; + } + } } } diff --git a/lib/tlTimeline/Timeline.h b/lib/tlTimeline/Timeline.h index 918b33791..3dc977f46 100644 --- a/lib/tlTimeline/Timeline.h +++ b/lib/tlTimeline/Timeline.h @@ -69,6 +69,20 @@ namespace tl const std::shared_ptr&, const Options& = Options()); + //! Video request. + struct VideoRequest + { + uint64_t id = 0; + std::future future; + }; + + //! Audio request. + struct AudioRequest + { + uint64_t id = 0; + std::future future; + }; + //! Timeline. class Timeline : public std::enable_shared_from_this { @@ -159,19 +173,17 @@ namespace tl ///@{ //! Get video data. - std::future getVideo( + VideoRequest getVideo( const otime::RationalTime&, const io::Options& = io::Options()); //! Get audio data. - std::future getAudio( + AudioRequest getAudio( double seconds, const io::Options& = io::Options()); //! Cancel requests. - //! - //! \todo Change this to cancel only specific requests. - void cancelRequests(); + void cancelRequests(const std::vector&); ///@} diff --git a/lib/tlTimeline/TimelinePrivate.h b/lib/tlTimeline/TimelinePrivate.h index 1ee3353f7..bec70dd85 100644 --- a/lib/tlTimeline/TimelinePrivate.h +++ b/lib/tlTimeline/TimelinePrivate.h @@ -58,6 +58,7 @@ namespace tl memory::LRUCache > readCache; otime::TimeRange timeRange = time::invalidTimeRange; io::Info ioInfo; + uint64_t requestId = 0; struct VideoLayerData { @@ -74,6 +75,7 @@ namespace tl VideoRequest() {}; VideoRequest(VideoRequest&&) = default; + uint64_t id = 0; otime::RationalTime time = time::invalidTime; io::Options options; std::promise promise; @@ -95,6 +97,7 @@ namespace tl AudioRequest() {}; AudioRequest(AudioRequest&&) = default; + uint64_t id = 0; double seconds = -1.0; io::Options options; std::promise promise; diff --git a/tests/tlTimelineTest/TimelineTest.cpp b/tests/tlTimelineTest/TimelineTest.cpp index 9d5788dac..8c7ff6754 100644 --- a/tests/tlTimelineTest/TimelineTest.cpp +++ b/tests/tlTimelineTest/TimelineTest.cpp @@ -134,27 +134,27 @@ namespace tl // Get video from the timeline. const otime::TimeRange& timeRange = timeline->getTimeRange(); std::vector videoData; - std::vector > videoFutures; + std::vector videoRequests; for (size_t i = 0; i < static_cast(timeRange.duration().value()); ++i) { - videoFutures.push_back(timeline->getVideo(otime::RationalTime(i, 24.0))); + videoRequests.push_back(timeline->getVideo(otime::RationalTime(i, 24.0))); } io::Options ioOptions; ioOptions["Layer"] = "1"; for (size_t i = 0; i < static_cast(timeRange.duration().value()); ++i) { - videoFutures.push_back(timeline->getVideo(otime::RationalTime(i, 24.0), ioOptions)); + videoRequests.push_back(timeline->getVideo(otime::RationalTime(i, 24.0), ioOptions)); } while (videoData.size() < static_cast(timeRange.duration().value()) * 2) { - auto i = videoFutures.begin(); - while (i != videoFutures.end()) + auto i = videoRequests.begin(); + while (i != videoRequests.end()) { - if (i->valid() && - i->wait_for(std::chrono::seconds(0)) == std::future_status::ready) + if (i->future.valid() && + i->future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - videoData.push_back(i->get()); - i = videoFutures.erase(i); + videoData.push_back(i->future.get()); + i = videoRequests.erase(i); } else { @@ -162,25 +162,25 @@ namespace tl } } } - TLRENDER_ASSERT(videoFutures.empty()); + TLRENDER_ASSERT(videoRequests.empty()); // Get audio from the timeline. std::vector audioData; - std::vector > audioFutures; + std::vector audioRequests; for (size_t i = 0; i < static_cast(timeRange.duration().rescaled_to(1.0).value()); ++i) { - audioFutures.push_back(timeline->getAudio(i)); + audioRequests.push_back(timeline->getAudio(i)); } while (audioData.size() < static_cast(timeRange.duration().rescaled_to(1.0).value())) { - auto i = audioFutures.begin(); - while (i != audioFutures.end()) + auto i = audioRequests.begin(); + while (i != audioRequests.end()) { - if (i->valid() && - i->wait_for(std::chrono::seconds(0)) == std::future_status::ready) + if (i->future.valid() && + i->future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - audioData.push_back(i->get()); - i = audioFutures.erase(i); + audioData.push_back(i->future.get()); + i = audioRequests.erase(i); } else { @@ -188,26 +188,35 @@ namespace tl } } } - TLRENDER_ASSERT(audioFutures.empty()); + TLRENDER_ASSERT(audioRequests.empty()); // Cancel requests. videoData.clear(); - videoFutures.clear(); + videoRequests.clear(); audioData.clear(); - audioFutures.clear(); + audioRequests.clear(); for (size_t i = 0; i < static_cast(timeRange.duration().value()); ++i) { - videoFutures.push_back(timeline->getVideo(otime::RationalTime(i, 24.0))); + videoRequests.push_back(timeline->getVideo(otime::RationalTime(i, 24.0))); } for (size_t i = 0; i < static_cast(timeRange.duration().value()); ++i) { - videoFutures.push_back(timeline->getVideo(otime::RationalTime(i, 24.0), ioOptions)); + videoRequests.push_back(timeline->getVideo(otime::RationalTime(i, 24.0), ioOptions)); } for (size_t i = 0; i < static_cast(timeRange.duration().rescaled_to(1.0).value()); ++i) { - audioFutures.push_back(timeline->getAudio(i)); + audioRequests.push_back(timeline->getAudio(i)); } - timeline->cancelRequests(); + std::vector ids; + for (const auto& i : videoRequests) + { + ids.push_back(i.id); + } + for (const auto& i : audioRequests) + { + ids.push_back(i.id); + } + timeline->cancelRequests(ids); } void TimelineTest::_separateAudio()