From bbaab5995125a8dc9a9837d4ce38be5cf7ddec0e Mon Sep 17 00:00:00 2001 From: Darby Johnston Date: Tue, 27 Feb 2024 16:59:44 -0800 Subject: [PATCH] WIP A/B playback sync --- lib/tlPlayApp/App.cpp | 153 ++++++++++++++------- lib/tlPlayApp/App.h | 1 + lib/tlTimeline/Player.cpp | 71 +++++----- lib/tlTimeline/Player.h | 24 ++-- lib/tlTimeline/PlayerOptions.cpp | 20 +-- lib/tlTimeline/PlayerOptions.h | 20 +-- lib/tlTimeline/PlayerOptionsInline.h | 2 +- lib/tlTimeline/PlayerPrivate.h | 3 +- tests/tlTimelineTest/PlayerOptionsTest.cpp | 10 +- 9 files changed, 181 insertions(+), 123 deletions(-) diff --git a/lib/tlPlayApp/App.cpp b/lib/tlPlayApp/App.cpp index d2e5b40a7..558e63eaf 100644 --- a/lib/tlPlayApp/App.cpp +++ b/lib/tlPlayApp/App.cpp @@ -46,6 +46,9 @@ namespace tl std::string settingsFileName; std::shared_ptr settings; std::shared_ptr filesModel; + std::vector > files; + std::vector > activeFiles; + std::vector > timelines; std::shared_ptr > > player; std::shared_ptr viewportModel; std::shared_ptr colorModel; @@ -65,6 +68,7 @@ namespace tl #endif // TLRENDER_BMD std::shared_ptr > settingsObserver; + std::shared_ptr > > filesObserver; std::shared_ptr > > activeObserver; std::shared_ptr > layersObserver; std::shared_ptr > recentFilesMaxObserver; @@ -468,6 +472,12 @@ namespace tl _settingsUpdate(name); }); + p.filesObserver = observer::ListObserver >::create( + p.filesModel->observeFiles(), + [this](const std::vector >& value) + { + _filesCallback(value); + }); p.activeObserver = observer::ListObserver >::create( p.filesModel->observeActive(), [this](const std::vector >& value) @@ -479,13 +489,13 @@ namespace tl [this](const std::vector& value) { //! \todo AB - /*for (size_t i = 0; i < value.size() && i < _p->players.size(); ++i) + /*for (size_t i = 0; i < value.size() && i < _p->timelines.size(); ++i) { - if (auto player = _p->players[i]) + if (auto timeline = _p->timelines[i]) { auto ioOptions = _getIOOptions(); ioOptions["Layer"] = string::Format("{0}").arg(value[i]); - player->setIOOptions(ioOptions); + timeline->setIOOptions(ioOptions); } }*/ }); @@ -785,68 +795,109 @@ namespace tl 1.0); } - void App::_activeCallback(const std::vector >& items) + void App::_filesCallback(const std::vector >& files) { TLRENDER_P(); - std::shared_ptr player; - if (!items.empty()) - { - try - { - timeline::Options options; - options.fileSequenceAudio = - p.settings->getValue("FileSequence/Audio"); - options.fileSequenceAudioFileName = - p.settings->getValue("FileSequence/AudioFileName"); - options.fileSequenceAudioDirectory = - p.settings->getValue("FileSequence/AudioDirectory"); - options.videoRequestCount = - p.settings->getValue("Performance/VideoRequestCount"); - options.audioRequestCount = - p.settings->getValue("Performance/AudioRequestCount"); - options.ioOptions = _getIOOptions(); - options.pathOptions.maxNumberDigits = - p.settings->getValue("FileSequence/MaxDigits"); - auto otioTimeline = items[0]->audioPath.isEmpty() ? - timeline::create(items[0]->path, _context, options) : - timeline::create(items[0]->path, items[0]->audioPath, _context, options); - auto timeline = timeline::Timeline::create(otioTimeline, _context, options); - for (const auto& video : timeline->getIOInfo().video) - { - items[0]->videoLayers.push_back(video.name); - } + std::vector > timelines(files.size()); + for (size_t i = 0; i < files.size(); ++i) + { + const auto j = std::find(p.files.begin(), p.files.end(), files[i]); + if (j != p.files.end()) + { + timelines[i] = p.timelines[j - p.files.begin()]; + } + } - std::vector > compare; - for (size_t i = 1; i < items.size(); ++i) + for (size_t i = 0; i < files.size(); ++i) + { + if (!timelines[i]) + { + try { - auto otioTimelineB = items[i]->audioPath.isEmpty() ? - timeline::create(items[i]->path, _context, options) : - timeline::create(items[i]->path, items[i]->audioPath, _context, options); - auto timelineB = timeline::Timeline::create(otioTimelineB, _context, options); - compare.push_back(timelineB); - for (const auto& video : timelineB->getIOInfo().video) + timeline::Options options; + options.fileSequenceAudio = + p.settings->getValue("FileSequence/Audio"); + options.fileSequenceAudioFileName = + p.settings->getValue("FileSequence/AudioFileName"); + options.fileSequenceAudioDirectory = + p.settings->getValue("FileSequence/AudioDirectory"); + options.videoRequestCount = + p.settings->getValue("Performance/VideoRequestCount"); + options.audioRequestCount = + p.settings->getValue("Performance/AudioRequestCount"); + options.ioOptions = _getIOOptions(); + options.pathOptions.maxNumberDigits = + p.settings->getValue("FileSequence/MaxDigits"); + auto otioTimeline = files[i]->audioPath.isEmpty() ? + timeline::create(files[i]->path, _context, options) : + timeline::create(files[i]->path, files[i]->audioPath, _context, options); + timelines[i] = timeline::Timeline::create(otioTimeline, _context, options); + for (const auto& video : timelines[i]->getIOInfo().video) { - items[i]->videoLayers.push_back(video.name); + files[i]->videoLayers.push_back(video.name); } } + catch (const std::exception& e) + { + _log(e.what(), log::Type::Error); + } + } + } + + p.files = files; + p.timelines = timelines; + } - timeline::PlayerOptions playerOptions; - playerOptions.cache.readAhead = time::invalidTime; - playerOptions.cache.readBehind = time::invalidTime; - playerOptions.timerMode = - p.settings->getValue("Performance/TimerMode"); - playerOptions.audioBufferFrameCount = - p.settings->getValue("Performance/AudioBufferFrameCount"); - player = timeline::Player::create(timeline, compare, _context, playerOptions); + void App::_activeCallback(const std::vector >& files) + { + TLRENDER_P(); + std::shared_ptr player; + if ((!files.empty() && !p.activeFiles.empty() && files[0] != p.activeFiles[0]) || + (!files.empty() && p.activeFiles.empty())) + { + auto i = std::find(p.files.begin(), p.files.end(), files[0]); + if (i != p.files.end()) + { + try + { + timeline::PlayerOptions playerOptions; + playerOptions.cache.readAhead = time::invalidTime; + playerOptions.cache.readBehind = time::invalidTime; + playerOptions.timerMode = + p.settings->getValue("Performance/TimerMode"); + playerOptions.audioBufferFrameCount = + p.settings->getValue("Performance/AudioBufferFrameCount"); + auto timeline = p.timelines[i - p.files.begin()]; + player = timeline::Player::create(timeline, _context, playerOptions); + } + catch (const std::exception& e) + { + _log(e.what(), log::Type::Error); + } } - catch (const std::exception& e) + } + else + { + player = p.player->get(); + } + if (player) + { + std::vector > compare; + for (size_t i = 1; i < files.size(); ++i) { - _log(e.what(), log::Type::Error); + auto j = std::find(p.files.begin(), p.files.end(), files[i]); + if (j != p.files.end()) + { + auto timeline = p.timelines[j - p.files.begin()]; + compare.push_back(timeline); + } } + player->setCompare(compare); } - p.player->setIfChanged(player); + p.activeFiles = files; + p.player->setIfChanged(player); #if defined(TLRENDER_BMD) p.bmdOutputDevice->setPlayer(player); #endif // TLRENDER_BMD diff --git a/lib/tlPlayApp/App.h b/lib/tlPlayApp/App.h index 4cd1cc4ed..bc6868957 100644 --- a/lib/tlPlayApp/App.h +++ b/lib/tlPlayApp/App.h @@ -125,6 +125,7 @@ namespace tl otime::RationalTime _getCacheReadAhead() const; otime::RationalTime _getCacheReadBehind() const; + void _filesCallback(const std::vector >&); void _activeCallback(const std::vector >&); void _settingsUpdate(const std::string&); diff --git a/lib/tlTimeline/Player.cpp b/lib/tlTimeline/Player.cpp index b28d3eeba..83c55c5cf 100644 --- a/lib/tlTimeline/Player.cpp +++ b/lib/tlTimeline/Player.cpp @@ -58,7 +58,6 @@ namespace tl void Player::_init( const std::shared_ptr& timeline, - const std::vector >& compare, const std::shared_ptr& context, const PlayerOptions& playerOptions) { @@ -88,19 +87,6 @@ namespace tl p.playerOptions = playerOptions; p.timeline = timeline; p.ioInfo = p.timeline->getIOInfo(); - p.compare = compare; - if (!p.ioInfo.video.empty()) - { - p.sizes.push_back(p.ioInfo.video.front().size); - } - for (const auto& timeline : compare) - { - io::Info ioInfo = timeline->getIOInfo(); - if (!ioInfo.video.empty()) - { - p.sizes.push_back(ioInfo.video.front().size); - } - } // Create observers. p.speed = observer::Value::create(p.timeline->getTimeRange().duration().rate()); @@ -209,6 +195,7 @@ namespace tl Playback playback = Playback::Stop; otime::RationalTime currentTime = time::invalidTime; otime::TimeRange inOutRange = time::invalidTimeRange; + std::vector > compare; io::Options ioOptions; double audioOffset = 0.0; bool clearRequests = false; @@ -220,6 +207,7 @@ namespace tl playback = p.mutex.playback; currentTime = p.mutex.currentTime; inOutRange = p.mutex.inOutRange; + compare = p.mutex.compare; ioOptions = p.mutex.ioOptions; audioOffset = p.mutex.audioOffset; clearRequests = p.mutex.clearRequests; @@ -234,6 +222,10 @@ namespace tl if (clearRequests) { p.timeline->cancelRequests(); + for (const auto& i : p.thread.compare) + { + i->cancelRequests(); + } p.thread.videoDataRequests.clear(); p.thread.audioDataRequests.clear(); } @@ -253,6 +245,7 @@ namespace tl } // Update the cache. + p.thread.compare = compare; p.cacheUpdate( currentTime, inOutRange, @@ -374,18 +367,7 @@ namespace tl const PlayerOptions& playerOptions) { auto out = std::shared_ptr(new Player); - out->_init(timeline, {}, context, playerOptions); - return out; - } - - std::shared_ptr Player::create( - const std::shared_ptr& timeline, - const std::vector >& compare, - const std::shared_ptr& context, - const PlayerOptions& playerOptions) - { - auto out = std::shared_ptr(new Player); - out->_init(timeline, compare, context, playerOptions); + out->_init(timeline, context, playerOptions); return out; } @@ -399,11 +381,6 @@ namespace tl return _p->timeline; } - const std::vector >& Player::getCompare() const - { - return _p->compare; - } - const file::Path& Player::getPath() const { return _p->timeline->getPath(); @@ -434,9 +411,20 @@ namespace tl return _p->ioInfo; } - const std::vector& Player::getSizes() const + std::vector Player::getSizes() const { - return _p->sizes; + TLRENDER_P(); + std::vector out; + out.push_back(!_p->ioInfo.video.empty() ? + _p->ioInfo.video.front().size : + image::Size()); + for (const auto& compare : p.compare) + { + out.push_back(compare && !compare->getIOInfo().video.empty() ? + compare->getIOInfo().video.front().size : + image::Size()); + } + return out; } double Player::getDefaultSpeed() const @@ -739,6 +727,23 @@ namespace tl p.timeline->getTimeRange().end_time_inclusive())); } + const std::vector >& Player::getCompare() const + { + return _p->compare; + } + + void Player::setCompare(const std::vector >& value) + { + TLRENDER_P(); + if (value == p.compare) + return; + p.compare = value; + std::unique_lock lock(p.mutex.mutex); + p.mutex.compare = value; + p.mutex.clearRequests = true; + p.mutex.clearCache = true; + } + const io::Options& Player::getIOOptions() const { return _p->ioOptions->get(); diff --git a/lib/tlTimeline/Player.h b/lib/tlTimeline/Player.h index 61d908766..27eab3bbe 100644 --- a/lib/tlTimeline/Player.h +++ b/lib/tlTimeline/Player.h @@ -85,7 +85,6 @@ namespace tl protected: void _init( const std::shared_ptr&, - const std::vector >& compare, const std::shared_ptr&, const PlayerOptions&); @@ -100,22 +99,12 @@ namespace tl const std::shared_ptr&, const PlayerOptions & = PlayerOptions()); - //! Create a new timeline player. - static std::shared_ptr create( - const std::shared_ptr&, - const std::vector >& compare, - const std::shared_ptr&, - const PlayerOptions & = PlayerOptions()); - //! Get the context. const std::weak_ptr& getContext() const; //! Get the timeline. const std::shared_ptr& getTimeline() const; - //! Get the timelines for comparison. - const std::vector >& getCompare() const; - //! Get the path. const file::Path& getPath() const; @@ -139,7 +128,7 @@ namespace tl const io::Info& getIOInfo() const; //! Get the timeline sizes. - const std::vector& getSizes() const; + std::vector getSizes() const; ///@} @@ -233,6 +222,17 @@ namespace tl ///@} + //! \name Comparison + ///@{ + + //! Get the timelines for comparison. + const std::vector >& getCompare() const; + + //! Set the timelines for comparison. + void setCompare(const std::vector >&); + + ///@} + //! \name I/O ///@{ diff --git a/lib/tlTimeline/PlayerOptions.cpp b/lib/tlTimeline/PlayerOptions.cpp index 7d2c2df98..f52edf352 100644 --- a/lib/tlTimeline/PlayerOptions.cpp +++ b/lib/tlTimeline/PlayerOptions.cpp @@ -14,30 +14,30 @@ namespace tl TLRENDER_ENUM_IMPL(TimerMode, "System", "Audio"); TLRENDER_ENUM_SERIALIZE_IMPL(TimerMode); - TLRENDER_ENUM_IMPL(ExternalTimeMode, "Relative", "Absolute"); - TLRENDER_ENUM_SERIALIZE_IMPL(ExternalTimeMode); + TLRENDER_ENUM_IMPL(CompareTimeMode, "Relative", "Absolute"); + TLRENDER_ENUM_SERIALIZE_IMPL(CompareTimeMode); - otime::RationalTime getExternalTime( + otime::RationalTime getCompareTime( const otime::RationalTime& sourceTime, const otime::TimeRange& sourceTimeRange, - const otime::TimeRange& externalTimeRange, - ExternalTimeMode mode) + const otime::TimeRange& compareTimeRange, + CompareTimeMode mode) { otime::RationalTime out; switch (mode) { - case ExternalTimeMode::Relative: + case CompareTimeMode::Relative: { const otime::RationalTime relativeTime = sourceTime - sourceTimeRange.start_time(); const otime::RationalTime relativeTimeRescaled = time::floor( - relativeTime.rescaled_to(externalTimeRange.duration().rate())); - out = externalTimeRange.start_time() + relativeTimeRescaled; + relativeTime.rescaled_to(compareTimeRange.duration().rate())); + out = compareTimeRange.start_time() + relativeTimeRescaled; break; } - case ExternalTimeMode::Absolute: + case CompareTimeMode::Absolute: out = time::floor(sourceTime.rescaled_to( - externalTimeRange.duration().rate())); + compareTimeRange.duration().rate())); break; default: break; } diff --git a/lib/tlTimeline/PlayerOptions.h b/lib/tlTimeline/PlayerOptions.h index 75407ba07..f27730cf8 100644 --- a/lib/tlTimeline/PlayerOptions.h +++ b/lib/tlTimeline/PlayerOptions.h @@ -22,8 +22,8 @@ namespace tl TLRENDER_ENUM(TimerMode); TLRENDER_ENUM_SERIALIZE(TimerMode); - //! External time mode. - enum class ExternalTimeMode + //! Compare time mode. + enum class CompareTimeMode { Relative, Absolute, @@ -31,8 +31,8 @@ namespace tl Count, First = Relative }; - TLRENDER_ENUM(ExternalTimeMode); - TLRENDER_ENUM_SERIALIZE(ExternalTimeMode); + TLRENDER_ENUM(CompareTimeMode); + TLRENDER_ENUM_SERIALIZE(CompareTimeMode); //! Timeline player cache options. struct PlayerCacheOptions @@ -68,19 +68,19 @@ namespace tl //! Current time. otime::RationalTime currentTime = time::invalidTime; - //! External time mode. - ExternalTimeMode externalTimeMode = ExternalTimeMode::Relative; + //! Compare time mode. + CompareTimeMode compareTimeMode = CompareTimeMode::Relative; bool operator == (const PlayerOptions&) const; bool operator != (const PlayerOptions&) const; }; - //! Get an external time from a source time. - otime::RationalTime getExternalTime( + //! Get a compare time. + otime::RationalTime getCompareTime( const otime::RationalTime& sourceTime, const otime::TimeRange& sourceTimeRange, - const otime::TimeRange& externalTimeRange, - ExternalTimeMode); + const otime::TimeRange& compareTimeRange, + CompareTimeMode); } } diff --git a/lib/tlTimeline/PlayerOptionsInline.h b/lib/tlTimeline/PlayerOptionsInline.h index d52f12d5e..b3d59e620 100644 --- a/lib/tlTimeline/PlayerOptionsInline.h +++ b/lib/tlTimeline/PlayerOptionsInline.h @@ -27,7 +27,7 @@ namespace tl muteTimeout == other.muteTimeout && sleepTimeout == other.sleepTimeout && currentTime == other.currentTime && - externalTimeMode == other.externalTimeMode; + compareTimeMode == other.compareTimeMode; } inline bool PlayerOptions::operator != (const PlayerOptions& other) const diff --git a/lib/tlTimeline/PlayerPrivate.h b/lib/tlTimeline/PlayerPrivate.h index 581f7e6e0..638b08264 100644 --- a/lib/tlTimeline/PlayerPrivate.h +++ b/lib/tlTimeline/PlayerPrivate.h @@ -55,7 +55,6 @@ namespace tl std::shared_ptr timeline; io::Info ioInfo; std::vector > compare; - std::vector sizes; std::shared_ptr > speed; std::shared_ptr > playback; @@ -79,6 +78,7 @@ namespace tl std::chrono::steady_clock::time_point playbackStartTimer; otime::RationalTime currentTime = time::invalidTime; otime::TimeRange inOutRange = time::invalidTimeRange; + std::vector > compare; io::Options ioOptions; std::vector currentVideoData; double audioOffset = 0.0; @@ -106,6 +106,7 @@ namespace tl struct Thread { + std::vector > compare; std::map > videoDataRequests; std::map > videoDataCache; #if defined(TLRENDER_AUDIO) diff --git a/tests/tlTimelineTest/PlayerOptionsTest.cpp b/tests/tlTimelineTest/PlayerOptionsTest.cpp index 4342a0c95..f7ef95367 100644 --- a/tests/tlTimelineTest/PlayerOptionsTest.cpp +++ b/tests/tlTimelineTest/PlayerOptionsTest.cpp @@ -28,7 +28,7 @@ namespace tl { { _enum("TimerMode", getTimerModeEnums); - _enum("ExternalTimeMode", getExternalTimeModeEnums); + _enum("CompareTimeMode", getCompareTimeModeEnums); } { PlayerCacheOptions v; @@ -43,7 +43,7 @@ namespace tl TLRENDER_ASSERT(v != PlayerOptions()); } { - const auto time = getExternalTime( + const auto time = getCompareTime( otime::RationalTime(0.0, 24.0), otime::TimeRange( otime::RationalTime(0.0, 24.0), @@ -51,11 +51,11 @@ namespace tl otime::TimeRange( otime::RationalTime(0.0, 24.0), otime::RationalTime(24.0, 24.0)), - ExternalTimeMode::Absolute); + CompareTimeMode::Absolute); TLRENDER_ASSERT(time == otime::RationalTime(0.0, 24.0)); } { - const auto time = getExternalTime( + const auto time = getCompareTime( otime::RationalTime(0.0, 24.0), otime::TimeRange( otime::RationalTime(0.0, 24.0), @@ -63,7 +63,7 @@ namespace tl otime::TimeRange( otime::RationalTime(24.0, 24.0), otime::RationalTime(24.0, 24.0)), - ExternalTimeMode::Relative); + CompareTimeMode::Relative); TLRENDER_ASSERT(time == otime::RationalTime(24.0, 24.0)); } }