From d32306f88b164d50aaeac7c7c0658c23be797ab6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 10 Jan 2020 20:35:48 +1100 Subject: [PATCH] only disk cache around the playhead --- app/codec/ffmpeg/ffmpegdecoder.cpp | 143 ++++++------------ app/codec/ffmpeg/ffmpegdecoder.h | 6 +- app/common/timerange.cpp | 22 +++ app/common/timerange.h | 2 + app/config/config.cpp | 4 +- .../preferences/tabs/preferencesdisktab.cpp | 63 ++++++-- .../preferences/tabs/preferencesdisktab.h | 4 + app/render/backend/renderbackend.cpp | 13 +- app/render/backend/renderbackend.h | 2 + app/render/backend/videorenderbackend.cpp | 57 ++++++- app/render/backend/videorenderbackend.h | 10 ++ app/render/diskmanager.cpp | 21 ++- app/render/diskmanager.h | 2 + app/widget/timeruler/timeruler.cpp | 4 +- app/widget/timeruler/timeruler.h | 2 +- app/widget/viewer/viewer.cpp | 3 +- 16 files changed, 222 insertions(+), 136 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index e9a0bb671..68e84b207 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -33,6 +33,7 @@ extern "C" { #include #include "codec/waveinput.h" +#include "common/define.h" #include "common/filefunctions.h" #include "common/timecodefunctions.h" #include "ffmpegcommon.h" @@ -41,8 +42,7 @@ extern "C" { FFmpegDecoder::FFmpegDecoder() : fmt_ctx_(nullptr), codec_ctx_(nullptr), - opts_(nullptr), - scale_ctx_(nullptr) + opts_(nullptr) { } @@ -129,41 +129,20 @@ bool FFmpegDecoder::Open() return false; } - // Set up - if (codec_ctx_->codec_type == AVMEDIA_TYPE_VIDEO) { - // Set up pixel format conversion for video - AVPixelFormat pix_fmt = static_cast(avstream_->codecpar->format); - + if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { // Get an Olive compatible AVPixelFormat - AVPixelFormat ideal_pix_fmt = FFmpegCommon::GetCompatiblePixelFormat(pix_fmt); + ideal_pix_fmt_ = FFmpegCommon::GetCompatiblePixelFormat(static_cast(avstream_->codecpar->format)); // Determine which Olive native pixel format we retrieved // Note that FFmpeg doesn't support float formats - switch (ideal_pix_fmt) { - case AV_PIX_FMT_RGBA: - output_fmt_ = PixelFormat::PIX_FMT_RGBA8; - break; - case AV_PIX_FMT_RGBA64: - output_fmt_ = PixelFormat::PIX_FMT_RGBA16U; - break; - default: - // We should never get here, but if we do there's nothing we can do with this format - return false; + if (ideal_pix_fmt_ == AV_PIX_FMT_RGBA) { + native_pix_fmt_ = PixelFormat::PIX_FMT_RGBA8; + } else if (ideal_pix_fmt_ == AV_PIX_FMT_RGBA64) { + native_pix_fmt_ = PixelFormat::PIX_FMT_RGBA16U; + } else { + // We should never get here, but just in case... + qFatal("Invalid output format"); } - - scale_ctx_ = sws_getContext(avstream_->codecpar->width, - avstream_->codecpar->height, - pix_fmt, - avstream_->codecpar->width, - avstream_->codecpar->height, - ideal_pix_fmt, - 0, - nullptr, - nullptr, - nullptr); - - } else if (codec_ctx_->codec_type == AVMEDIA_TYPE_AUDIO) { - // FIXME: Fill this in } // All allocation succeeded so we set the state to open @@ -192,56 +171,18 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode) QFile compressed_frame(GetIndexFilename().append(QString::number(target_ts))); if (compressed_frame.open(QFile::ReadOnly)) { - QByteArray frame_loader = qUncompress(compressed_frame.readAll()); - AVFrame* frame = av_frame_alloc(); - - if (frame == nullptr) { - qWarning() << "Failed to create AVFrame for swscale"; - return nullptr; - } - - frame->width = avstream_->codecpar->width; - frame->height = avstream_->codecpar->height; - - QDataStream ds(&frame_loader, QIODevice::ReadOnly); - ds >> frame->format; - - if (av_frame_get_buffer(frame, 0) != 0) { - qWarning() << "Failed to get AVFrame buffer"; - av_frame_free(&frame); - return nullptr; - } - // Read data - size_t pos = sizeof(int); - for (int i=0;i(frame->linesize[i] * CalculatePlaneHeight(frame->height, static_cast(frame->format), i)); - memcpy(frame->data[i], frame_loader.data() + pos, plane_size); - pos += plane_size; - } + QByteArray frame_loader = qUncompress(compressed_frame.readAll()); // Frame was valid, now we convert it to a native Olive frame FramePtr frame_container = Frame::Create(); - frame_container->set_width(frame->width); - frame_container->set_height(frame->height); - frame_container->set_format(static_cast(output_fmt_)); + frame_container->set_width(avstream_->codecpar->width); + frame_container->set_height(avstream_->codecpar->height); + frame_container->set_format(native_pix_fmt_); frame_container->set_timestamp(Timecode::timestamp_to_time(target_ts, avstream_->time_base)); frame_container->allocate(); - // Convert pixel format/linesize if necessary - uint8_t* dst_data = reinterpret_cast(frame_container->data()); - int dst_linesize = frame_container->width() * PixelService::BytesPerPixel(static_cast(output_fmt_)); - - // Perform pixel conversion - sws_scale(scale_ctx_, - frame->data, - frame->linesize, - 0, - frame->height, - &dst_data, - &dst_linesize); - - av_frame_free(&frame); + memcpy(frame_container->data(), frame_loader.constData(), frame_loader.size()); return frame_container; } @@ -291,11 +232,6 @@ void FFmpegDecoder::Close() { frame_index_.clear(); - if (scale_ctx_ != nullptr) { - sws_freeContext(scale_ctx_); - scale_ctx_ = nullptr; - } - if (opts_ != nullptr) { av_dict_free(&opts_); opts_ = nullptr; @@ -839,6 +775,17 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) // Iterate through every single frame and get each timestamp // NOTE: Expects no frames to have been read so far + SwsContext* scale_ctx = sws_getContext(avstream_->codecpar->width, + avstream_->codecpar->height, + static_cast(avstream_->codecpar->format), + avstream_->codecpar->width, + avstream_->codecpar->height, + ideal_pix_fmt_, + 0, + nullptr, + nullptr, + nullptr); + int ret; while (true) { @@ -846,20 +793,25 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) if (ret >= 0) { // Save frame - QByteArray frame_save; - QDataStream ds(&frame_save, QIODevice::WriteOnly); + int buffer_size = PixelService::GetBufferSize(native_pix_fmt_, avstream_->codecpar->width, avstream_->codecpar->height); - ds << frame->format; + QByteArray frame_save(buffer_size, Qt::Uninitialized); - // Save data - for (int i=0;i(frame->data[i]), - frame->linesize[i] * CalculatePlaneHeight(frame->height, static_cast(frame->format), i)); - } + char* data = frame_save.data(); + int line_size = avstream_->codecpar->width * kRGBAChannels; + + // Perform pixel conversion + sws_scale(scale_ctx, + frame->data, + frame->linesize, + 0, + avstream_->codecpar->height, + reinterpret_cast(&data), + &line_size); QFile compressed_frame(GetIndexFilename().append(QString::number(frame->pts))); if (compressed_frame.open(QFile::WriteOnly)) { - compressed_frame.write(qCompress(frame_save)); + compressed_frame.write(qCompress(frame_save, 9)); compressed_frame.close(); } @@ -870,6 +822,8 @@ void FFmpegDecoder::IndexVideo(AVPacket* pkt, AVFrame* frame) } } + sws_freeContext(scale_ctx); + // Save index to file SaveIndex(); } @@ -919,17 +873,6 @@ int FFmpegDecoder::GetFrame(AVPacket *pkt, AVFrame *frame) return ret; } -int FFmpegDecoder::CalculatePlaneHeight(int frame_height, const AVPixelFormat &format, int plane) -{ - // FIXME: This seems dumb, but I can't find any FFmpeg function that returns this information - - if ((plane == 1 || plane == 2) - && format == AV_PIX_FMT_YUV420P) { - return frame_height/2; - } - return frame_height; -} - int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts) { // Index now if we haven't already diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 9ca114ec5..8520062f8 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -139,15 +139,13 @@ private: void Seek(int64_t timestamp); - int CalculatePlaneHeight(int frame_height, const AVPixelFormat& format, int plane); - AVFormatContext* fmt_ctx_; AVCodecContext* codec_ctx_; AVStream* avstream_; AVDictionary* opts_; - SwsContext* scale_ctx_; - int output_fmt_; + AVPixelFormat ideal_pix_fmt_; + PixelFormat::Format native_pix_fmt_; QVector frame_index_; diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index 3a2564057..9ea543e88 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -149,3 +149,25 @@ bool TimeRangeList::ContainsTimeRange(const TimeRange &range) const return false; } + +TimeRangeList TimeRangeList::Intersects(const TimeRange &range) +{ + TimeRangeList intersect_list; + + for (int i=0;i= range.out()) { + // No intersect + continue; + } else { + // Crop the time range to the range and add it to the list + TimeRange cropped(qMax(range.in(), compare.in()), + qMin(range.out(), compare.out())); + + intersect_list.append(cropped); + } + } + + return intersect_list; +} diff --git a/app/common/timerange.h b/app/common/timerange.h index 192aa220b..bb2621c75 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -45,6 +45,8 @@ public: bool ContainsTimeRange(const TimeRange& range) const; + TimeRangeList Intersects(const TimeRange& range); + }; #endif // TIMERANGE_H diff --git a/app/config/config.cpp b/app/config/config.cpp index 474c08160..78fe1939b 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -75,7 +75,9 @@ void Config::SetDefaults() config_map_["Autoscroll"] = AutoScroll::kPage; config_map_["DiskCachePath"] = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); - config_map_["DiskCacheSize"] = 20.0; + config_map_["DiskCacheSize"] = 0.025; + config_map_["DiskCacheBehind"] = QVariant::fromValue(rational(5)); + config_map_["DiskCacheAhead"] = QVariant::fromValue(rational(30)); config_map_["ClearDiskCacheOnClose"] = false; config_map_["DefaultSequenceWidth"] = 1920; diff --git a/app/dialog/preferences/tabs/preferencesdisktab.cpp b/app/dialog/preferences/tabs/preferencesdisktab.cpp index ff8d9474b..bf43fdcf2 100644 --- a/app/dialog/preferences/tabs/preferencesdisktab.cpp +++ b/app/dialog/preferences/tabs/preferencesdisktab.cpp @@ -3,50 +3,76 @@ #include #include #include +#include #include +#include #include +#include "render/diskmanager.h" + PreferencesDiskTab::PreferencesDiskTab() { QVBoxLayout* outer_layout = new QVBoxLayout(this); - QGridLayout* layout = new QGridLayout(); - outer_layout->addLayout(layout); + QGroupBox* disk_management_group = new QGroupBox(tr("Disk Management")); + outer_layout->addWidget(disk_management_group); + + QGridLayout* disk_management_layout = new QGridLayout(disk_management_group); int row = 0; - layout->addWidget(new QLabel(tr("Disk Cache Location:")), row, 0); + disk_management_layout->addWidget(new QLabel(tr("Disk Cache Location:")), row, 0); disk_cache_location_ = new QLineEdit(); disk_cache_location_->setText(Config::Current()["DiskCachePath"].toString()); connect(disk_cache_location_, &QLineEdit::textChanged, this, &PreferencesDiskTab::DiskCacheLineEditChanged); - layout->addWidget(disk_cache_location_, row, 1); + disk_management_layout->addWidget(disk_cache_location_, row, 1); QPushButton* browse_btn = new QPushButton(tr("Browse")); connect(browse_btn, &QPushButton::clicked, this, &PreferencesDiskTab::BrowseDiskCachePath); - layout->addWidget(browse_btn, row, 2); + disk_management_layout->addWidget(browse_btn, row, 2); row++; - layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0); + disk_management_layout->addWidget(new QLabel(tr("Maximum Disk Cache:")), row, 0); maximum_cache_slider_ = new FloatSlider(); maximum_cache_slider_->SetSuffix(QStringLiteral(" GB")); maximum_cache_slider_->SetMinimum(1.0); maximum_cache_slider_->SetValue(Config::Current()["DiskCacheSize"].toDouble()); - layout->addWidget(maximum_cache_slider_, row, 1, 1, 2); + disk_management_layout->addWidget(maximum_cache_slider_, row, 1, 1, 2); row++; QPushButton* clear_cache_btn = new QPushButton(tr("Clear Disk Cache")); connect(clear_cache_btn, &QPushButton::clicked, this, &PreferencesDiskTab::ClearDiskCache); - layout->addWidget(clear_cache_btn, row, 1, 1, 2); + disk_management_layout->addWidget(clear_cache_btn, row, 1, 1, 2); row++; clear_disk_cache_ = new QCheckBox(tr("Automatically clear disk cache on close")); clear_disk_cache_->setChecked(Config::Current()["ClearDiskCacheOnClose"].toBool()); - layout->addWidget(clear_disk_cache_, row, 1, 1, 2); + disk_management_layout->addWidget(clear_disk_cache_, row, 1, 1, 2); + + QGroupBox* cache_behavior = new QGroupBox(tr("Cache Behavior")); + outer_layout->addWidget(cache_behavior); + QGridLayout* cache_behavior_layout = new QGridLayout(cache_behavior); + + row = 0; + + cache_behavior_layout->addWidget(new QLabel(tr("Cache Ahead:")), row, 0); + + cache_ahead_slider_ = new FloatSlider(); + cache_ahead_slider_->SetSuffix(QStringLiteral(" seconds")); + cache_ahead_slider_->SetValue(Config::Current()["DiskCacheAhead"].value().toDouble()); + cache_behavior_layout->addWidget(cache_ahead_slider_, row, 1); + + cache_behavior_layout->addWidget(new QLabel(tr("Cache Behind:")), row, 2); + + cache_behind_slider_ = new FloatSlider(); + cache_behind_slider_->SetSuffix(QStringLiteral(" seconds")); + cache_behind_slider_->SetValue(Config::Current()["DiskCacheBehind"].value().toDouble()); + cache_behavior_layout->addWidget(cache_behind_slider_, row, 3); outer_layout->addStretch(); } @@ -56,6 +82,8 @@ void PreferencesDiskTab::Accept() Config::Current()["DiskCachePath"] = disk_cache_location_->text(); Config::Current()["DiskCacheSize"] = maximum_cache_slider_->GetValue(); Config::Current()["ClearDiskCacheOnClose"] = clear_disk_cache_->isChecked(); + Config::Current()["DiskCacheBehind"] = QVariant::fromValue(rational::fromDouble(cache_behind_slider_->GetValue())); + Config::Current()["DiskCacheAhead"] = QVariant::fromValue(rational::fromDouble(cache_ahead_slider_->GetValue())); } void PreferencesDiskTab::DiskCacheLineEditChanged() @@ -80,5 +108,20 @@ void PreferencesDiskTab::BrowseDiskCachePath() void PreferencesDiskTab::ClearDiskCache() { - qDebug() << "Clear" << Config::Current()["DiskCachePath"]; + if (QMessageBox::question(this, + tr("Clear Disk Cache"), + tr("Are you sure you want to clear the disk cache in '%1'?").arg(Config::Current()["DiskCachePath"].toString()), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + if (DiskManager::instance()->ClearDiskCache()) { + QMessageBox::information(this, + tr("Clear Disk Cache"), + tr("Disk cache cleared successfully"), + QMessageBox::Ok); + } else { + QMessageBox::information(this, + tr("Clear Disk Cache"), + tr("Disk cache failed to fully clear. You may have to delete the cache files manually."), + QMessageBox::Ok); + } + } } diff --git a/app/dialog/preferences/tabs/preferencesdisktab.h b/app/dialog/preferences/tabs/preferencesdisktab.h index fc2862511..5da7a33f0 100644 --- a/app/dialog/preferences/tabs/preferencesdisktab.h +++ b/app/dialog/preferences/tabs/preferencesdisktab.h @@ -20,6 +20,10 @@ private: FloatSlider* maximum_cache_slider_; + FloatSlider* cache_ahead_slider_; + + FloatSlider* cache_behind_slider_; + QCheckBox* clear_disk_cache_; private slots: diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 70469dc1f..5d277b707 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -115,13 +115,10 @@ void RenderBackend::InvalidateCache(const rational &start_range, const rational << "and" << end_range_adj.toDouble(); - // Add the range to the list - cache_queue_.InsertTimeRange(TimeRange(start_range_adj, end_range_adj)); - // Queue value update QueueValueUpdate(); - CacheNext(); + InvalidateCacheInternal(start_range_adj, end_range_adj); } bool RenderBackend::Compile() @@ -356,6 +353,14 @@ const QVector &RenderBackend::threads() return threads_; } +void RenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range) +{ + // Add the range to the list + cache_queue_.InsertTimeRange(TimeRange(start_range, end_range)); + + CacheNext(); +} + void RenderBackend::CacheIDChangedEvent(const QString &id) { Q_UNUSED(id) diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 769a0a1c0..01cd5d5a7 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -63,6 +63,8 @@ protected: */ virtual bool GenerateCacheIDInternal(QCryptographicHash& hash) = 0; + virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range); + virtual void CacheIDChangedEvent(const QString& id); void SetError(const QString& error); diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index fc8f43341..91b9d4543 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -28,6 +28,8 @@ #include #include "common/timecodefunctions.h" +#include "config/config.h" +#include "render/diskmanager.h" #include "render/diskmanager.h" #include "render/pixelservice.h" #include "videorenderworker.h" @@ -37,6 +39,7 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : operating_mode_(VideoRenderWorker::kHashRenderCache), only_signal_last_frame_requested_(true) { + connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &VideoRenderBackend::FrameRemovedFromDiskCache); } bool VideoRenderBackend::InitInternal() @@ -142,10 +145,21 @@ void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor) video_processor->SetOperatingMode(operating_mode_); - connect(video_processor, &VideoRenderWorker::CompletedFrame, this, &VideoRenderBackend::ThreadCompletedFrame); - connect(video_processor, &VideoRenderWorker::HashAlreadyBeingCached, this, &VideoRenderBackend::ThreadSkippedFrame); - connect(video_processor, &VideoRenderWorker::CompletedDownload, this, &VideoRenderBackend::ThreadCompletedDownload); - connect(video_processor, &VideoRenderWorker::HashAlreadyExists, this, &VideoRenderBackend::ThreadHashAlreadyExists); + connect(video_processor, &VideoRenderWorker::CompletedFrame, this, &VideoRenderBackend::ThreadCompletedFrame, Qt::QueuedConnection); + connect(video_processor, &VideoRenderWorker::HashAlreadyBeingCached, this, &VideoRenderBackend::ThreadSkippedFrame, Qt::QueuedConnection); + connect(video_processor, &VideoRenderWorker::CompletedDownload, this, &VideoRenderBackend::ThreadCompletedDownload, Qt::QueuedConnection); + connect(video_processor, &VideoRenderWorker::HashAlreadyExists, this, &VideoRenderBackend::ThreadHashAlreadyExists, Qt::QueuedConnection); +} + +void VideoRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range) +{ + TimeRange invalidated(start_range, end_range); + + missing_cache_.InsertTimeRange(invalidated); + + emit RangeInvalidated(invalidated); + + Requeue(); } VideoRenderFrameCache *VideoRenderBackend::frame_cache() @@ -172,6 +186,8 @@ const char *VideoRenderBackend::GetCachedFrame(const rational &time) return nullptr; } + Requeue(); + // Find frame in map QByteArray frame_hash = frame_cache_.TimeToHash(time); @@ -220,8 +236,13 @@ TimeRange VideoRenderBackend::PopNextFrameFromQueue() snapped_in -= params_.time_base(); } + TimeRange frame_range(snapped_in, snapped_in + params_.time_base()); + // Remove this particular frame from the queue - cache_queue_.RemoveTimeRange(TimeRange(snapped_in, snapped_in + params_.time_base())); + cache_queue_.RemoveTimeRange(frame_range); + + // Remove this particular frame from missing frames + missing_cache_.RemoveTimeRange(frame_range); // Return the snapped frame return TimeRange(snapped_in, snapped_in); @@ -293,6 +314,19 @@ void VideoRenderBackend::TruncateFrameCacheLength(const rational &length) frame_cache_.Truncate(length); } +void VideoRenderBackend::FrameRemovedFromDiskCache(const QByteArray &hash) +{ + QList deleted_frames = frame_cache()->FramesWithHash(hash); + + foreach (const rational& frame, deleted_frames) { + TimeRange invalidated(frame, frame+params_.time_base()); + + missing_cache_.InsertTimeRange(invalidated); + + emit RangeInvalidated(invalidated); + } +} + bool VideoRenderBackend::TimeIsQueued(const TimeRange &time) const { return cache_queue_.ContainsTimeRange(time); @@ -314,3 +348,16 @@ bool VideoRenderBackend::SetFrameHash(const NodeDependency &dep, const QByteArra return false; } + +void VideoRenderBackend::Requeue() +{ + cache_queue_.clear(); + + // Reset queue around the last time requested + TimeRange queueable_range(last_time_requested_ - Config::Current()["DiskCacheBehind"].value(), + last_time_requested_ + Config::Current()["DiskCacheAhead"].value()); + + cache_queue_ = missing_cache_.Intersects(queueable_range); + + CacheNext(); +} diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index afe233b9e..743661b53 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -103,12 +103,16 @@ protected: virtual void EmitCachedFrameReady(const rational &time, const QVariant& value, qint64 job_time) = 0; + virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override; + VideoRenderWorker::OperatingMode operating_mode_; signals: void CachedFrameReady(const rational& time, QVariant value, qint64 job_time); void CachedTimeReady(const rational& time, qint64 job_time); + void RangeInvalidated(const TimeRange& range); + private: bool TimeIsQueued(const TimeRange &time) const; @@ -116,12 +120,16 @@ private: bool SetFrameHash(const NodeDependency& dep, const QByteArray& hash, const qint64& job_time); + void Requeue(); + VideoRenderingParams params_; QByteArray cache_frame_load_buffer_; VideoRenderFrameCache frame_cache_; + TimeRangeList missing_cache_; + rational last_time_requested_; bool only_signal_last_frame_requested_; @@ -134,6 +142,8 @@ private slots: void TruncateFrameCacheLength(const rational& length); + void FrameRemovedFromDiskCache(const QByteArray& hash); + }; #endif // VIDEORENDERERBACKEND_H diff --git a/app/render/diskmanager.cpp b/app/render/diskmanager.cpp index f06f3631d..fbead0e6a 100644 --- a/app/render/diskmanager.cpp +++ b/app/render/diskmanager.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "common/filefunctions.h" #include "config/config.h" @@ -19,8 +20,6 @@ DiskManager::DiskManager() : if (cache_index_file.open(QFile::ReadOnly)) { QDataStream ds(&cache_index_file); - ds >> consumption_; - while (!cache_index_file.atEnd()) { HashTime h; @@ -29,7 +28,10 @@ DiskManager::DiskManager() : ds >> h.access_time; ds >> h.file_size; - disk_data_.append(h); + if (QFileInfo::exists(h.file_name)) { + consumption_ += h.file_size; + disk_data_.append(h); + } } } } @@ -38,7 +40,7 @@ DiskManager::~DiskManager() { if (Config::Current()["ClearDiskCacheOnClose"].toBool()) { // Clear all cache data - QDir(GetMediaCacheLocation()).removeRecursively(); + ClearDiskCache(); } else { // Save current cache index QFile cache_index_file(QDir(GetMediaCacheLocation()).filePath("index")); @@ -46,8 +48,6 @@ DiskManager::~DiskManager() if (cache_index_file.open(QFile::WriteOnly)) { QDataStream ds(&cache_index_file); - ds << consumption_; - foreach (const HashTime& h, disk_data_) { ds << h.file_name; ds << h.hash; @@ -97,7 +97,7 @@ void DiskManager::CreatedFile(const QString &file_name, const QByteArray &hash) { qint64 file_size = QFile(file_name).size(); - disk_data_.append({file_name, hash, QDateTime::currentMSecsSinceEpoch(), }); + disk_data_.append({file_name, hash, QDateTime::currentMSecsSinceEpoch(), file_size}); consumption_ += file_size; @@ -106,6 +106,13 @@ void DiskManager::CreatedFile(const QString &file_name, const QByteArray &hash) } } +bool DiskManager::ClearDiskCache() +{ + disk_data_.clear(); + + return QDir(GetMediaCacheLocation()).removeRecursively(); +} + void DiskManager::DeleteLeastRecent() { HashTime h = disk_data_.takeFirst(); diff --git a/app/render/diskmanager.h b/app/render/diskmanager.h index bc7e7f7ed..370fa29cc 100644 --- a/app/render/diskmanager.h +++ b/app/render/diskmanager.h @@ -17,6 +17,8 @@ public: void CreatedFile(const QString& file_name, const QByteArray& hash); + bool ClearDiskCache(); + signals: void DeletedFrame(const QByteArray& hash); diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index c39eac222..cfdcb23f8 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -109,9 +109,9 @@ void TimeRuler::SetScroll(int s) update(); } -void TimeRuler::CacheInvalidatedRange(const rational& in, const rational& out) +void TimeRuler::CacheInvalidatedRange(const TimeRange& range) { - dirty_cache_ranges_.InsertTimeRange(TimeRange(in, out)); + dirty_cache_ranges_.InsertTimeRange(range); update(); } diff --git a/app/widget/timeruler/timeruler.h b/app/widget/timeruler/timeruler.h index ca3a35f96..65c723576 100644 --- a/app/widget/timeruler/timeruler.h +++ b/app/widget/timeruler/timeruler.h @@ -48,7 +48,7 @@ public slots: void SetScroll(int s); - void CacheInvalidatedRange(const rational& in, const rational& out); + void CacheInvalidatedRange(const TimeRange &range); void CacheTimeReady(const rational& time); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 8955f3447..9de60620a 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -82,6 +82,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(video_renderer_, &VideoRenderBackend::CachedFrameReady, this, &ViewerWidget::RendererCachedFrame); connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, this, &ViewerWidget::RendererCachedTime); connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, ruler_, &TimeRuler::CacheTimeReady); + connect(video_renderer_, &VideoRenderBackend::RangeInvalidated, ruler_, &TimeRuler::CacheInvalidatedRange); audio_renderer_ = new AudioBackend(this); } @@ -142,7 +143,6 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_man disconnect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &ViewerWidget::SetTimebase); disconnect(viewer_node_, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot); disconnect(viewer_node_, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); - disconnect(viewer_node_, &ViewerOutput::VideoChangedBetween, ruler_, &TimeRuler::CacheInvalidatedRange); // Effectively disables the viewer and clears the state SizeChangedSlot(0, 0); @@ -164,7 +164,6 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_man connect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &ViewerWidget::SetTimebase); connect(viewer_node_, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot); connect(viewer_node_, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); - connect(viewer_node_, &ViewerOutput::VideoChangedBetween, ruler_, &TimeRuler::CacheInvalidatedRange); SizeChangedSlot(viewer_node_->video_params().width(), viewer_node_->video_params().height()); LengthChangedSlot(viewer_node_->Length());