cache: use render job times to synchronize cache
Ensure old inaccurate jobs are never prioritized over current jobs.
This commit is contained in:
+16
-18
@@ -162,30 +162,28 @@ void TimeRangeList::InsertTimeRange(const TimeRange &range)
|
||||
|
||||
void TimeRangeList::RemoveTimeRange(const TimeRange &range)
|
||||
{
|
||||
for (int i=0;i<size();i++) {
|
||||
const TimeRange& compare = at(i);
|
||||
RemoveTimeRange(this, range);
|
||||
}
|
||||
|
||||
if (range.Contains(compare)) {
|
||||
void TimeRangeList::RemoveTimeRange(QList<TimeRange> *list, const TimeRange &remove)
|
||||
{
|
||||
for (int i=0;i<list->size();i++) {
|
||||
TimeRange& compare = (*list)[i];
|
||||
|
||||
if (remove.Contains(compare)) {
|
||||
// This element is entirely encompassed in this range, remove it
|
||||
removeAt(i);
|
||||
list->removeAt(i);
|
||||
i--;
|
||||
} else if (compare.Contains(range, false, false)) {
|
||||
} else if (compare.Contains(remove, false, false)) {
|
||||
// The remove range is within this element, only choice is to split the element into two
|
||||
TimeRange first(compare.in(), range.in());
|
||||
TimeRange last(range.out(), compare.out());
|
||||
|
||||
replace(i, first);
|
||||
append(last);
|
||||
} else if (compare.in() < range.in() && compare.out() > range.in()) {
|
||||
list->append(TimeRange(remove.out(), compare.out()));
|
||||
compare.set_out(remove.in());
|
||||
} else if (compare.in() < remove.in() && compare.out() > remove.in()) {
|
||||
// This element's out point overlaps the range's in, we'll trim it
|
||||
TimeRange trimmed = compare;
|
||||
trimmed.set_out(range.in());
|
||||
replace(i, trimmed);
|
||||
} else if (compare.in() < range.out() && compare.out() > range.out()) {
|
||||
compare.set_out(remove.in());
|
||||
} else if (compare.in() < remove.out() && compare.out() > remove.out()) {
|
||||
// This element's in point overlaps the range's out, we'll trim it
|
||||
TimeRange trimmed = compare;
|
||||
trimmed.set_in(range.out());
|
||||
replace(i, trimmed);
|
||||
compare.set_in(remove.out());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,8 @@ public:
|
||||
|
||||
void RemoveTimeRange(const TimeRange& range);
|
||||
|
||||
static void RemoveTimeRange(QList<TimeRange>* list, const TimeRange& remove);
|
||||
|
||||
bool ContainsTimeRange(const TimeRange& range, bool in_inclusive = true, bool out_inclusive = true) const;
|
||||
|
||||
TimeRangeList Intersects(const TimeRange& range) const;
|
||||
|
||||
@@ -63,10 +63,14 @@ void AudioPlaybackCache::SetParameters(const AudioRenderingParams ¶ms)
|
||||
emit Invalidated(invalidate_range);
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples)
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr samples, const qint64 &job_time)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
if (!JobIsCurrent(range, job_time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QFile f(filename_);
|
||||
if (f.open(QFile::ReadWrite)) {
|
||||
qint64 start_offset = params_.time_to_bytes(range.in());
|
||||
@@ -193,6 +197,13 @@ void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::LengthChangedEvent(const rational& old, const rational& newlen)
|
||||
{
|
||||
if (newlen < old) {
|
||||
QFile(filename_).resize(params_.time_to_bytes(newlen));
|
||||
}
|
||||
}
|
||||
|
||||
const QString &AudioPlaybackCache::GetCacheFilename() const
|
||||
{
|
||||
return filename_;
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
void SetParameters(const AudioRenderingParams& params);
|
||||
|
||||
void WritePCM(const TimeRange &range, SampleBufferPtr samples);
|
||||
void WritePCM(const TimeRange &range, SampleBufferPtr samples, const qint64& job_time);
|
||||
|
||||
void WriteSilence(const TimeRange &range);
|
||||
|
||||
@@ -52,6 +52,8 @@ signals:
|
||||
protected:
|
||||
virtual void ShiftEvent(const rational& from, const rational& to) override;
|
||||
|
||||
virtual void LengthChangedEvent(const rational& old, const rational& newlen) override;
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
|
||||
@@ -351,8 +351,6 @@ void RenderBackend::WorkerFinished()
|
||||
|
||||
if (viewer_node_) {
|
||||
RunNextJob();
|
||||
} else {
|
||||
qDebug() << "Ignored job finish because no viewer";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,14 @@ QByteArray FrameHashCache::GetHash(const rational &time)
|
||||
return time_hash_map_.value(time);
|
||||
}
|
||||
|
||||
void FrameHashCache::SetHash(const rational &time, const QByteArray &hash)
|
||||
void FrameHashCache::SetHash(const rational &time, const QByteArray &hash, const qint64& job_time)
|
||||
{
|
||||
QMutexLocker locker(lock());
|
||||
|
||||
if (!JobIsCurrent(TimeRange(time, time), job_time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_hash_map_.insert(time, hash);
|
||||
|
||||
TimeRange validated_range(time, time + timebase_);
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
|
||||
QByteArray GetHash(const rational& time);
|
||||
|
||||
void SetHash(const rational& time, const QByteArray& hash);
|
||||
void SetHash(const rational& time, const QByteArray& hash, const qint64 &job_time);
|
||||
|
||||
void SetTimebase(const rational& tb);
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "playbackcache.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
void PlaybackCache::Invalidate(const TimeRange &r)
|
||||
@@ -89,21 +91,13 @@ void PlaybackCache::Shift(const rational &from, const rational &to)
|
||||
emit Shifted(from, to);
|
||||
}
|
||||
|
||||
void PlaybackCache::Validate(const TimeRange &r)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
NoLockValidate(r);
|
||||
|
||||
locker.unlock();
|
||||
|
||||
emit Validated(r);
|
||||
}
|
||||
|
||||
void PlaybackCache::NoLockInvalidate(const TimeRange &r)
|
||||
{
|
||||
invalidated_.InsertTimeRange(r);
|
||||
|
||||
RemoveRangeFromJobs(r);
|
||||
jobs_.append({r, QDateTime::currentMSecsSinceEpoch()});
|
||||
|
||||
InvalidateEvent(r);
|
||||
}
|
||||
|
||||
@@ -132,6 +126,20 @@ void PlaybackCache::NoLockSetLength(const rational &r)
|
||||
length_ = r;
|
||||
}
|
||||
|
||||
bool PlaybackCache::JobIsCurrent(const TimeRange &r, qint64 job_time)
|
||||
{
|
||||
for (int i=jobs_.size()-1; i>=0; i--) {
|
||||
const JobIdentifier& job = jobs_.at(i);
|
||||
|
||||
if (job.range.Contains(r, true, r.in() != r.out())
|
||||
&& job_time >= job.job_time) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PlaybackCache::LengthChangedEvent(const rational &, const rational &)
|
||||
{
|
||||
}
|
||||
@@ -144,4 +152,29 @@ void PlaybackCache::ShiftEvent(const rational &, const rational &)
|
||||
{
|
||||
}
|
||||
|
||||
void PlaybackCache::RemoveRangeFromJobs(const TimeRange &remove)
|
||||
{
|
||||
// Code shamelessly copied from TimeRangeList::RemoveTimeRange
|
||||
for (int i=0;i<jobs_.size();i++) {
|
||||
JobIdentifier& job = jobs_[i];
|
||||
TimeRange& compare = job.range;
|
||||
|
||||
if (remove.Contains(compare)) {
|
||||
// This element is entirely encompassed in this range, remove it
|
||||
jobs_.removeAt(i);
|
||||
i--;
|
||||
} else if (compare.Contains(remove, false, false)) {
|
||||
// The remove range is within this element, only choice is to split the element into two
|
||||
jobs_.append({TimeRange(remove.out(), compare.out()), job.job_time});
|
||||
compare.set_out(remove.in());
|
||||
} else if (compare.in() < remove.in() && compare.out() > remove.in()) {
|
||||
// This element's out point overlaps the range's in, we'll trim it
|
||||
compare.set_out(remove.in());
|
||||
} else if (compare.in() < remove.out() && compare.out() > remove.out()) {
|
||||
// This element's in point overlaps the range's out, we'll trim it
|
||||
compare.set_in(remove.out());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -80,14 +80,14 @@ signals:
|
||||
void LengthChanged(const OLIVE_NAMESPACE::rational& r);
|
||||
|
||||
protected:
|
||||
void Validate(const TimeRange& r);
|
||||
|
||||
void NoLockInvalidate(const TimeRange& r);
|
||||
|
||||
void NoLockValidate(const TimeRange& r);
|
||||
|
||||
void NoLockSetLength(const rational& r);
|
||||
|
||||
bool JobIsCurrent(const TimeRange& r, qint64 job_time);
|
||||
|
||||
const rational& NoLockGetLength() const
|
||||
{
|
||||
return length_;
|
||||
@@ -110,10 +110,19 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
void RemoveRangeFromJobs(const TimeRange& remove);
|
||||
|
||||
QMutex lock_;
|
||||
|
||||
TimeRangeList invalidated_;
|
||||
|
||||
struct JobIdentifier {
|
||||
TimeRange range;
|
||||
qint64 job_time;
|
||||
};
|
||||
|
||||
QList<JobIdentifier> jobs_;
|
||||
|
||||
rational length_;
|
||||
|
||||
};
|
||||
|
||||
Vendored
+2
-2
@@ -65,14 +65,14 @@ QFuture<void> CacheTask::DownloadFrame(FramePtr frame, const QByteArray &hash)
|
||||
void CacheTask::FrameDownloaded(const QByteArray &hash, const std::list<rational> ×)
|
||||
{
|
||||
foreach (const rational& t, times) {
|
||||
viewer()->video_frame_cache()->SetHash(t, hash);
|
||||
viewer()->video_frame_cache()->SetHash(t, hash, job_time());
|
||||
}
|
||||
}
|
||||
|
||||
void CacheTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples)
|
||||
{
|
||||
if (samples) {
|
||||
viewer()->audio_playback_cache()->WritePCM(range, samples);
|
||||
viewer()->audio_playback_cache()->WritePCM(range, samples, job_time());
|
||||
} else {
|
||||
viewer()->audio_playback_cache()->WriteSilence(range);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples
|
||||
adjusted_range -= params_.custom_range().in();
|
||||
}
|
||||
|
||||
audio_data_.WritePCM(adjusted_range, samples);
|
||||
audio_data_.WritePCM(adjusted_range, samples, job_time());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -29,6 +29,8 @@ RenderTask::RenderTask(ViewerOutput* viewer, const VideoRenderingParams &vparams
|
||||
video_params_(vparams),
|
||||
audio_params_(aparams)
|
||||
{
|
||||
job_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// FIXME: This makes a full copy of the node graph every time it starts, there must be a better
|
||||
// way.
|
||||
backend_.SetViewerNode(viewer_);
|
||||
|
||||
@@ -63,6 +63,11 @@ protected:
|
||||
|
||||
void SetAnchorPoint(const rational& r);
|
||||
|
||||
const qint64& job_time() const
|
||||
{
|
||||
return job_time_;
|
||||
}
|
||||
|
||||
private:
|
||||
ViewerOutput* viewer_;
|
||||
|
||||
@@ -74,6 +79,8 @@ private:
|
||||
|
||||
OpenGLBackend backend_;
|
||||
|
||||
qint64 job_time_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
Reference in New Issue
Block a user