From 7609754103b45f30da81abfa3e0b5c72ffa1f030 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 5 Jun 2020 15:23:16 +1000 Subject: [PATCH] playbackcache: more intelligent job matching Ensures audio ranges can be anticipated and accurately ignored if they're old or acknowledged if they're current. --- app/render/audioplaybackcache.cpp | 51 +++++++--- app/render/audioplaybackcache.h | 9 ++ app/render/framehashcache.cpp | 14 ++- app/render/playbackcache.cpp | 152 +++++++++++++++--------------- app/render/playbackcache.h | 18 ++-- 5 files changed, 141 insertions(+), 103 deletions(-) diff --git a/app/render/audioplaybackcache.cpp b/app/render/audioplaybackcache.cpp index a825a2b74..f29bf844e 100644 --- a/app/render/audioplaybackcache.cpp +++ b/app/render/audioplaybackcache.cpp @@ -67,29 +67,37 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, SampleBufferPtr sample { QMutexLocker locker(lock()); - if (!JobIsCurrent(range, job_time)) { + QList valid_ranges = NoLockGetValidRanges(range, job_time); + if (valid_ranges.isEmpty()) { return; } QFile f(filename_); if (f.open(QFile::ReadWrite)) { - qint64 start_offset = params_.time_to_bytes(range.in()); - qint64 max_len = params_.time_to_bytes(range.out()); - qint64 write_len = max_len - start_offset; - QByteArray a = samples->toPackedData(); - if (f.size() < max_len) { - f.resize(max_len); - } + foreach (const TimeRange& r, valid_ranges) { + // Calculate destination offsets + qint64 start_offset = params_.time_to_bytes(r.in()); + qint64 max_len = params_.time_to_bytes(r.out()); - f.seek(start_offset); - f.write(a); + if (f.size() < max_len) { + f.resize(max_len); + } - if (write_len > a.size()) { - // Fill remaining space with silence - QByteArray s(write_len - a.size(), 0x00); - f.write(s); + // Calculate source offsets + qint64 sample_start = params_.time_to_bytes(r.in() - range.in()); + qint64 sample_len = params_.time_to_bytes(r.length()); + qint64 actual_write = qMin(sample_len, a.size() - sample_start); + + f.seek(start_offset); + f.write(a.data() + sample_start, actual_write); + + if (actual_write < sample_len) { + // Fill remaining space with silence + QByteArray s(sample_len - actual_write, 0x00); + f.write(s); + } } f.close(); @@ -213,6 +221,21 @@ void AudioPlaybackCache::LengthChangedEvent(const rational& old, const rational& } } +QList AudioPlaybackCache::NoLockGetValidRanges(const TimeRange& range, const qint64& job_time) +{ + QList valid_ranges; + + for (int i=jobs_.size()-1;i>=0;i--) { + const JobIdentifier& job = jobs_.at(i); + + if (job_time >= job.job_time && job.range.OverlapsWith(range)) { + valid_ranges.append(job.range.Intersected(range)); + } + } + + return valid_ranges; +} + const QString &AudioPlaybackCache::GetCacheFilename() const { return filename_; diff --git a/app/render/audioplaybackcache.h b/app/render/audioplaybackcache.h index 32a8f0af0..b14f465d9 100644 --- a/app/render/audioplaybackcache.h +++ b/app/render/audioplaybackcache.h @@ -46,6 +46,13 @@ public: const QString& GetCacheFilename() const; + QList GetValidRanges(const TimeRange &range, const qint64 &job_time) + { + QMutexLocker locker(lock()); + + return NoLockGetValidRanges(range, job_time); + } + signals: void ParametersChanged(); @@ -55,6 +62,8 @@ protected: virtual void LengthChangedEvent(const rational& old, const rational& newlen) override; private: + QList NoLockGetValidRanges(const TimeRange &range, const qint64 &job_time); + QString filename_; AudioRenderingParams params_; diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp index 2ae7e90c3..aa66945e2 100644 --- a/app/render/framehashcache.cpp +++ b/app/render/framehashcache.cpp @@ -45,7 +45,19 @@ void FrameHashCache::SetHash(const rational &time, const QByteArray &hash, const { QMutexLocker locker(lock()); - if (!JobIsCurrent(TimeRange(time, time), job_time)) { + bool is_current = false; + + for (int i=jobs_.size()-1; i>=0; i--) { + const JobIdentifier& job = jobs_.at(i); + + if (job.range.Contains(time) + && job_time >= job.job_time) { + is_current = true; + break; + } + } + + if (!is_current) { return; } diff --git a/app/render/playbackcache.cpp b/app/render/playbackcache.cpp index dd83d8825..cf94b6e47 100644 --- a/app/render/playbackcache.cpp +++ b/app/render/playbackcache.cpp @@ -26,7 +26,7 @@ OLIVE_NAMESPACE_ENTER void PlaybackCache::Invalidate(const TimeRange &r) { - QMutexLocker locker(&lock_); + QMutexLocker locker(lock()); NoLockInvalidate(r); @@ -37,7 +37,7 @@ void PlaybackCache::Invalidate(const TimeRange &r) void PlaybackCache::InvalidateAll() { - QMutexLocker locker(&lock_); + QMutexLocker locker(lock()); TimeRange invalidate_range(0, length_); @@ -50,78 +50,21 @@ void PlaybackCache::InvalidateAll() void PlaybackCache::SetLength(const rational &r) { - QMutexLocker locker(&lock_); + QMutexLocker locker(lock()); - NoLockSetLength(r); -} - -void PlaybackCache::Shift(const rational &from, const rational &to) -{ - if (from == to) { - return; - } - - QMutexLocker locker(&lock_); - - // An region between `from` and `to` will be inserted or spliced out - TimeRangeList ranges_to_shift = invalidated_.Intersects(TimeRange(from, RATIONAL_MAX)); - - // Remove everything from the minimum point - invalidated_.RemoveTimeRange(TimeRange(qMin(from, to), RATIONAL_MAX)); - - // Shift everything in our ranges to shift list - // - // `diff` is POSITIVE when moving forward -> and NEGATIVE when moving backward <- - rational diff = to - from; - foreach (const TimeRange& r, ranges_to_shift) { - invalidated_.InsertTimeRange(r + diff); - } - - ShiftEvent(from, to); - - length_ += diff; - - if (diff > rational()) { - // If shifting forward, add this section to the invalidated region - TimeRange invalidate_range(from, to); - - NoLockInvalidate(invalidate_range); - - locker.unlock(); - - emit Invalidated(invalidate_range); - } else { - locker.unlock(); - } - - emit Shifted(from, to); -} - -void PlaybackCache::NoLockInvalidate(const TimeRange &r) -{ - invalidated_.InsertTimeRange(r); - - RemoveRangeFromJobs(r); - jobs_.append({r, QDateTime::currentMSecsSinceEpoch()}); - - InvalidateEvent(r); -} - -void PlaybackCache::NoLockValidate(const TimeRange &r) -{ - invalidated_.RemoveTimeRange(r); -} - -void PlaybackCache::NoLockSetLength(const rational &r) -{ if (length_ == r) { // Same length - do nothing return; } + LengthChangedEvent(length_, r); + TimeRange range_diff(length_, r); - if (r > length_) { + if (r.isNull()) { + invalidated_.clear(); + jobs_.clear(); + } else if (r > length_) { // If new length is greater, simply extend the invalidated range for now invalidated_.InsertTimeRange(range_diff); } else { @@ -130,23 +73,78 @@ void PlaybackCache::NoLockSetLength(const rational &r) RemoveRangeFromJobs(range_diff); } - LengthChangedEvent(length_, r); - + rational old_length = length_; length_ = r; + + locker.unlock(); + + if (r > old_length) { + emit Invalidated(range_diff); + } else { + emit Validated(range_diff); + } } -bool PlaybackCache::JobIsCurrent(const TimeRange &r, qint64 job_time) +void PlaybackCache::Shift(const rational &from, const rational &to) { - 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; - } + if (from == to) { + return; } - return false; + QMutexLocker locker(lock()); + + // An region between `from` and `to` will be inserted or spliced out + TimeRangeList ranges_to_shift = invalidated_.Intersects(TimeRange(from, RATIONAL_MAX)); + + // Remove everything from the minimum point + TimeRange remove_range = TimeRange(qMin(from, to), RATIONAL_MAX); + NoLockValidate(remove_range); + RemoveRangeFromJobs(remove_range); + + // Shift everything in our ranges to shift list + // (`diff` is POSITIVE when moving forward -> and NEGATIVE when moving backward <-) + rational diff = to - from; + foreach (const TimeRange& r, ranges_to_shift) { + NoLockInvalidate(r + diff); + } + + ShiftEvent(from, to); + + length_ += diff; + + if (diff > rational()) { + // If shifting forward, add this section to the invalidated region + NoLockInvalidate(TimeRange(from, to)); + } + + locker.unlock(); + + // Emit signals + emit Validated(remove_range); + foreach (const TimeRange& r, ranges_to_shift) { + emit Invalidated(r + diff); + } + if (diff > rational()) { + emit Invalidated(TimeRange(from, to)); + } + emit Shifted(from, to); +} + +void PlaybackCache::NoLockInvalidate(const TimeRange &r) +{ + invalidated_.InsertTimeRange(r); + + RemoveRangeFromJobs(r); + qint64 job_time = QDateTime::currentMSecsSinceEpoch(); + jobs_.append({r, job_time}); + qDebug() << "Creating job" << r << job_time; + + InvalidateEvent(r); +} + +void PlaybackCache::NoLockValidate(const TimeRange &r) +{ + invalidated_.RemoveTimeRange(r); } void PlaybackCache::LengthChangedEvent(const rational &, const rational &) diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index 7f186137c..f73cefb9f 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -84,10 +84,6 @@ protected: void NoLockValidate(const TimeRange& r); - void NoLockSetLength(const rational& r); - - bool JobIsCurrent(const TimeRange& r, qint64 job_time); - const rational& NoLockGetLength() const { return length_; @@ -109,13 +105,6 @@ protected: return &lock_; } -private: - void RemoveRangeFromJobs(const TimeRange& remove); - - QMutex lock_; - - TimeRangeList invalidated_; - struct JobIdentifier { TimeRange range; qint64 job_time; @@ -123,6 +112,13 @@ private: QList jobs_; +private: + void RemoveRangeFromJobs(const TimeRange& remove); + + QMutex lock_; + + TimeRangeList invalidated_; + rational length_; };