cache: began base implementation of "shifting" the cache
Should be a fairly important optimization to make.
This commit is contained in:
@@ -87,6 +87,12 @@ QString ViewerOutput::Description() const
|
||||
return tr("Interface between a Viewer panel and the node system.");
|
||||
}
|
||||
|
||||
void ViewerOutput::ShiftCache(const rational &from, const rational &to)
|
||||
{
|
||||
video_frame_cache_.Shift(from, to);
|
||||
audio_playback_cache_.Shift(from, to);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
{
|
||||
emit GraphChangedFrom(source);
|
||||
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
void ShiftCache(const rational& from, const rational& to);
|
||||
|
||||
NodeInput* texture_input() const {
|
||||
return texture_input_;
|
||||
}
|
||||
|
||||
@@ -111,6 +111,74 @@ void AudioPlaybackCache::WriteSilence(const TimeRange &range)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::ShiftEvent(const rational &from, const rational &to)
|
||||
{
|
||||
QFile f(filename_);
|
||||
if (f.open(QFile::ReadWrite)) {
|
||||
qint64 from_offset = params_.time_to_bytes(from);
|
||||
qint64 to_offset = params_.time_to_bytes(to);
|
||||
qint64 chunk = qAbs(to_offset - from_offset);
|
||||
|
||||
QByteArray buf(chunk, Qt::Uninitialized);
|
||||
|
||||
if (to > from) {
|
||||
// Shifting forwards, we must insert a new region and shift all the bytes there
|
||||
|
||||
// For shifting forwards, we copy bytes starting at the back so that bytes we need don't
|
||||
// get overwritten.
|
||||
qint64 read_offset = f.size();
|
||||
|
||||
f.resize(f.size() + chunk);
|
||||
|
||||
qint64 write_offset = f.size();
|
||||
|
||||
do {
|
||||
|
||||
// Calculate how much will be read this time
|
||||
qint64 chunk_sz = qMin(chunk, read_offset - from_offset);
|
||||
read_offset -= chunk_sz;
|
||||
|
||||
// Read that chunk
|
||||
f.seek(read_offset);
|
||||
f.read(buf.data(), chunk_sz);
|
||||
|
||||
// Write it at the destination
|
||||
write_offset -= chunk_sz;
|
||||
f.seek(write_offset);
|
||||
f.write(buf.data(), chunk_sz);
|
||||
|
||||
} while (read_offset != from_offset);
|
||||
|
||||
// Replace remainder with silence
|
||||
f.seek(from_offset);
|
||||
buf.fill(0);
|
||||
f.write(buf);
|
||||
|
||||
} else {
|
||||
// Shifting backwards, we will shift bytes and truncate
|
||||
|
||||
do {
|
||||
// Read region to be shifted
|
||||
f.seek(from_offset);
|
||||
qint64 read_sz = f.read(buf.data(), buf.size());
|
||||
from_offset += read_sz;
|
||||
|
||||
// Write it at the destination
|
||||
f.seek(to_offset);
|
||||
to_offset += f.write(buf, read_sz);
|
||||
} while (from_offset != f.size());
|
||||
|
||||
// Truncate
|
||||
f.resize(f.size() - chunk);
|
||||
|
||||
}
|
||||
|
||||
f.close();
|
||||
} else {
|
||||
qWarning() << "Failed to write PCM data to" << filename_;
|
||||
}
|
||||
}
|
||||
|
||||
const QString &AudioPlaybackCache::GetCacheFilename() const
|
||||
{
|
||||
return filename_;
|
||||
|
||||
@@ -48,6 +48,9 @@ public:
|
||||
signals:
|
||||
void ParametersChanged();
|
||||
|
||||
protected:
|
||||
virtual void ShiftEvent(const rational& from, const rational& to) override;
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
|
||||
@@ -185,6 +185,47 @@ void FrameHashCache::InvalidateEvent(const TimeRange &r)
|
||||
}
|
||||
}
|
||||
|
||||
struct HashTimePair {
|
||||
rational time;
|
||||
QByteArray hash;
|
||||
};
|
||||
|
||||
void FrameHashCache::ShiftEvent(const rational &from, const rational &to)
|
||||
{
|
||||
QMap<rational, QByteArray>::iterator i = time_hash_map_.begin();
|
||||
|
||||
// POSITIVE if moving forward ->
|
||||
// NEGATIVE if moving backward <-
|
||||
rational diff = to - from;
|
||||
bool diff_is_negative = (diff < rational());
|
||||
|
||||
QList<HashTimePair> shifted_times;
|
||||
|
||||
while (i != time_hash_map_.end()) {
|
||||
if (diff_is_negative && i.key() >= to && i.key() < from) {
|
||||
|
||||
// This time will be removed in the shift so we just discard it
|
||||
i = time_hash_map_.erase(i);
|
||||
|
||||
} else if (i.key() >= from) {
|
||||
|
||||
// This time is after the from time and must be shifted
|
||||
shifted_times.append({i.key() + diff, i.value()});
|
||||
i = time_hash_map_.erase(i);
|
||||
|
||||
} else {
|
||||
|
||||
// Do nothing
|
||||
i++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const HashTimePair& p, shifted_times) {
|
||||
time_hash_map_.insert(p.time, p.hash);
|
||||
}
|
||||
}
|
||||
|
||||
QString FrameHashCache::CachePathName(const QByteArray& hash, const PixelFormat::Format& pix_fmt)
|
||||
{
|
||||
QString ext = GetFormatExtension(pix_fmt);
|
||||
|
||||
@@ -75,6 +75,8 @@ protected:
|
||||
|
||||
virtual void InvalidateEvent(const TimeRange& range) override;
|
||||
|
||||
virtual void ShiftEvent(const rational& from, const rational& to) override;
|
||||
|
||||
private:
|
||||
QMap<rational, QByteArray> time_hash_map_;
|
||||
|
||||
|
||||
@@ -61,6 +61,30 @@ bool PlaybackCache::IsFullyValidated() const
|
||||
return invalidated_.isEmpty();
|
||||
}
|
||||
|
||||
void PlaybackCache::Shift(const rational &from, const rational &to)
|
||||
{
|
||||
// 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);
|
||||
|
||||
if (diff > rational()) {
|
||||
// If shifting forward, add this section to the invalidated region
|
||||
Invalidate(TimeRange(from, to));
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackCache::Validate(const TimeRange &r)
|
||||
{
|
||||
invalidated_.RemoveTimeRange(r);
|
||||
@@ -81,4 +105,8 @@ void PlaybackCache::InvalidateEvent(const TimeRange &)
|
||||
{
|
||||
}
|
||||
|
||||
void PlaybackCache::ShiftEvent(const rational &, const rational &)
|
||||
{
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -46,6 +46,8 @@ public:
|
||||
|
||||
bool IsFullyValidated() const;
|
||||
|
||||
void Shift(const rational& from, const rational& to);
|
||||
|
||||
const TimeRangeList& GetInvalidatedRanges() const
|
||||
{
|
||||
return invalidated_;
|
||||
@@ -70,6 +72,8 @@ protected:
|
||||
|
||||
virtual void InvalidateEvent(const TimeRange& range);
|
||||
|
||||
virtual void ShiftEvent(const rational& from, const rational& to);
|
||||
|
||||
private:
|
||||
TimeRangeList invalidated_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user