only disk cache around the playhead
This commit is contained in:
@@ -33,6 +33,7 @@ extern "C" {
|
||||
#include <QtMath>
|
||||
|
||||
#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<AVPixelFormat>(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<AVPixelFormat>(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<AV_NUM_DATA_POINTERS;i++) {
|
||||
size_t plane_size = static_cast<size_t>(frame->linesize[i] * CalculatePlaneHeight(frame->height, static_cast<AVPixelFormat>(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<PixelFormat::Format>(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<uint8_t*>(frame_container->data());
|
||||
int dst_linesize = frame_container->width() * PixelService::BytesPerPixel(static_cast<PixelFormat::Format>(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<AVPixelFormat>(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<AV_NUM_DATA_POINTERS;i++) {
|
||||
frame_save.append(reinterpret_cast<const char*>(frame->data[i]),
|
||||
frame->linesize[i] * CalculatePlaneHeight(frame->height, static_cast<AVPixelFormat>(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<uint8_t**>(&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
|
||||
|
||||
@@ -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<int64_t> frame_index_;
|
||||
|
||||
|
||||
@@ -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<size();i++) {
|
||||
const TimeRange& compare = at(i);
|
||||
|
||||
if (compare.out() <= range.in() || compare.in() >= 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;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public:
|
||||
|
||||
bool ContainsTimeRange(const TimeRange& range) const;
|
||||
|
||||
TimeRangeList Intersects(const TimeRange& range);
|
||||
|
||||
};
|
||||
|
||||
#endif // TIMERANGE_H
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,50 +3,76 @@
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
#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<rational>().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<rational>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ private:
|
||||
|
||||
FloatSlider* maximum_cache_slider_;
|
||||
|
||||
FloatSlider* cache_ahead_slider_;
|
||||
|
||||
FloatSlider* cache_behind_slider_;
|
||||
|
||||
QCheckBox* clear_disk_cache_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -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<QThread *> &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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <QtMath>
|
||||
|
||||
#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<rational> 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<rational>(),
|
||||
last_time_requested_ + Config::Current()["DiskCacheAhead"].value<rational>());
|
||||
|
||||
cache_queue_ = missing_cache_.Intersects(queueable_range);
|
||||
|
||||
CacheNext();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
#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();
|
||||
|
||||
@@ -17,6 +17,8 @@ public:
|
||||
|
||||
void CreatedFile(const QString& file_name, const QByteArray& hash);
|
||||
|
||||
bool ClearDiskCache();
|
||||
|
||||
signals:
|
||||
void DeletedFrame(const QByteArray& hash);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user