Skip to content

Commit

Permalink
WIP A/B playback sync
Browse files Browse the repository at this point in the history
  • Loading branch information
darbyjohnston committed Feb 28, 2024
1 parent 10c5c75 commit bbaab59
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 123 deletions.
153 changes: 102 additions & 51 deletions lib/tlPlayApp/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ namespace tl
std::string settingsFileName;
std::shared_ptr<play::Settings> settings;
std::shared_ptr<play::FilesModel> filesModel;
std::vector<std::shared_ptr<play::FilesModelItem> > files;
std::vector<std::shared_ptr<play::FilesModelItem> > activeFiles;
std::vector<std::shared_ptr<timeline::Timeline> > timelines;
std::shared_ptr<observer::Value<std::shared_ptr<timeline::Player> > > player;
std::shared_ptr<play::ViewportModel> viewportModel;
std::shared_ptr<play::ColorModel> colorModel;
Expand All @@ -65,6 +68,7 @@ namespace tl
#endif // TLRENDER_BMD

std::shared_ptr<observer::ValueObserver<std::string> > settingsObserver;
std::shared_ptr<observer::ListObserver<std::shared_ptr<play::FilesModelItem> > > filesObserver;
std::shared_ptr<observer::ListObserver<std::shared_ptr<play::FilesModelItem> > > activeObserver;
std::shared_ptr<observer::ListObserver<int> > layersObserver;
std::shared_ptr<observer::ValueObserver<size_t> > recentFilesMaxObserver;
Expand Down Expand Up @@ -468,6 +472,12 @@ namespace tl
_settingsUpdate(name);
});

p.filesObserver = observer::ListObserver<std::shared_ptr<play::FilesModelItem> >::create(
p.filesModel->observeFiles(),
[this](const std::vector<std::shared_ptr<play::FilesModelItem> >& value)
{
_filesCallback(value);
});
p.activeObserver = observer::ListObserver<std::shared_ptr<play::FilesModelItem> >::create(
p.filesModel->observeActive(),
[this](const std::vector<std::shared_ptr<play::FilesModelItem> >& value)
Expand All @@ -479,13 +489,13 @@ namespace tl
[this](const std::vector<int>& 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);
}
}*/
});
Expand Down Expand Up @@ -785,68 +795,109 @@ namespace tl
1.0);
}

void App::_activeCallback(const std::vector<std::shared_ptr<play::FilesModelItem> >& items)
void App::_filesCallback(const std::vector<std::shared_ptr<play::FilesModelItem> >& files)
{
TLRENDER_P();

std::shared_ptr<timeline::Player> player;
if (!items.empty())
{
try
{
timeline::Options options;
options.fileSequenceAudio =
p.settings->getValue<timeline::FileSequenceAudio>("FileSequence/Audio");
options.fileSequenceAudioFileName =
p.settings->getValue<std::string>("FileSequence/AudioFileName");
options.fileSequenceAudioDirectory =
p.settings->getValue<std::string>("FileSequence/AudioDirectory");
options.videoRequestCount =
p.settings->getValue<size_t>("Performance/VideoRequestCount");
options.audioRequestCount =
p.settings->getValue<size_t>("Performance/AudioRequestCount");
options.ioOptions = _getIOOptions();
options.pathOptions.maxNumberDigits =
p.settings->getValue<size_t>("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<std::shared_ptr<timeline::Timeline> > 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<std::shared_ptr<timeline::Timeline> > 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<timeline::FileSequenceAudio>("FileSequence/Audio");
options.fileSequenceAudioFileName =
p.settings->getValue<std::string>("FileSequence/AudioFileName");
options.fileSequenceAudioDirectory =
p.settings->getValue<std::string>("FileSequence/AudioDirectory");
options.videoRequestCount =
p.settings->getValue<size_t>("Performance/VideoRequestCount");
options.audioRequestCount =
p.settings->getValue<size_t>("Performance/AudioRequestCount");
options.ioOptions = _getIOOptions();
options.pathOptions.maxNumberDigits =
p.settings->getValue<size_t>("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<timeline::TimerMode>("Performance/TimerMode");
playerOptions.audioBufferFrameCount =
p.settings->getValue<size_t>("Performance/AudioBufferFrameCount");
player = timeline::Player::create(timeline, compare, _context, playerOptions);
void App::_activeCallback(const std::vector<std::shared_ptr<play::FilesModelItem> >& files)
{
TLRENDER_P();
std::shared_ptr<timeline::Player> 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<timeline::TimerMode>("Performance/TimerMode");
playerOptions.audioBufferFrameCount =
p.settings->getValue<size_t>("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<std::shared_ptr<timeline::Timeline> > 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
Expand Down
1 change: 1 addition & 0 deletions lib/tlPlayApp/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ namespace tl
otime::RationalTime _getCacheReadAhead() const;
otime::RationalTime _getCacheReadBehind() const;

void _filesCallback(const std::vector<std::shared_ptr<play::FilesModelItem> >&);
void _activeCallback(const std::vector<std::shared_ptr<play::FilesModelItem> >&);

void _settingsUpdate(const std::string&);
Expand Down
71 changes: 38 additions & 33 deletions lib/tlTimeline/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ namespace tl

void Player::_init(
const std::shared_ptr<Timeline>& timeline,
const std::vector<std::shared_ptr<Timeline> >& compare,
const std::shared_ptr<system::Context>& context,
const PlayerOptions& playerOptions)
{
Expand Down Expand Up @@ -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<double>::create(p.timeline->getTimeRange().duration().rate());
Expand Down Expand Up @@ -209,6 +195,7 @@ namespace tl
Playback playback = Playback::Stop;
otime::RationalTime currentTime = time::invalidTime;
otime::TimeRange inOutRange = time::invalidTimeRange;
std::vector<std::shared_ptr<Timeline> > compare;
io::Options ioOptions;
double audioOffset = 0.0;
bool clearRequests = false;
Expand All @@ -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;
Expand All @@ -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();
}
Expand All @@ -253,6 +245,7 @@ namespace tl
}

// Update the cache.
p.thread.compare = compare;
p.cacheUpdate(
currentTime,
inOutRange,
Expand Down Expand Up @@ -374,18 +367,7 @@ namespace tl
const PlayerOptions& playerOptions)
{
auto out = std::shared_ptr<Player>(new Player);
out->_init(timeline, {}, context, playerOptions);
return out;
}

std::shared_ptr<Player> Player::create(
const std::shared_ptr<Timeline>& timeline,
const std::vector<std::shared_ptr<Timeline> >& compare,
const std::shared_ptr<system::Context>& context,
const PlayerOptions& playerOptions)
{
auto out = std::shared_ptr<Player>(new Player);
out->_init(timeline, compare, context, playerOptions);
out->_init(timeline, context, playerOptions);
return out;
}

Expand All @@ -399,11 +381,6 @@ namespace tl
return _p->timeline;
}

const std::vector<std::shared_ptr<Timeline> >& Player::getCompare() const
{
return _p->compare;
}

const file::Path& Player::getPath() const
{
return _p->timeline->getPath();
Expand Down Expand Up @@ -434,9 +411,20 @@ namespace tl
return _p->ioInfo;
}

const std::vector<image::Size>& Player::getSizes() const
std::vector<image::Size> Player::getSizes() const
{
return _p->sizes;
TLRENDER_P();
std::vector<image::Size> 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
Expand Down Expand Up @@ -739,6 +727,23 @@ namespace tl
p.timeline->getTimeRange().end_time_inclusive()));
}

const std::vector<std::shared_ptr<Timeline> >& Player::getCompare() const
{
return _p->compare;
}

void Player::setCompare(const std::vector<std::shared_ptr<Timeline> >& value)
{
TLRENDER_P();
if (value == p.compare)
return;
p.compare = value;
std::unique_lock<std::mutex> 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();
Expand Down
Loading

0 comments on commit bbaab59

Please sign in to comment.