cache: heavily reworked exporting for new system

Also cleans up cache in general and moves it to the Task infrastructure.
This commit is contained in:
itsmattkc
2020-05-19 23:35:17 +10:00
parent cd847a89ce
commit 87e7564222
18 changed files with 414 additions and 263 deletions
+14 -1
View File
@@ -21,6 +21,7 @@
#include "cache.h"
#include <QLinkedList>
#include <QMatrix4x4>
#include "project/item/sequence/sequence.h"
@@ -48,9 +49,21 @@ bool CacheTask::Run()
}
}
Render(range_to_cache, 2);
Render(range_to_cache, RenderMode::kOffline, QMatrix4x4(), false, divider_);
return true;
}
QFuture<void> CacheTask::DownloadFrame(FramePtr frame, const QByteArray &hash)
{
return QtConcurrent::run(FrameHashCache::SaveCacheFrame, hash, frame);
}
void CacheTask::FrameDownloaded(const QByteArray &hash, const QLinkedList<rational> &times)
{
foreach (const rational& t, times) {
viewer()->video_frame_cache()->SetHash(t, hash);
}
}
OLIVE_NAMESPACE_EXIT
+5
View File
@@ -36,6 +36,11 @@ public:
public slots:
virtual bool Run() override;
protected:
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) override;
virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList<rational>& times) override;
private:
bool in_out_only_;
+118 -1
View File
@@ -20,6 +20,9 @@
#include "export.h"
#include "common/timecodefunctions.h"
#include "render/colormanager.h"
OLIVE_NAMESPACE_ENTER
ExportTask::ExportTask(ViewerOutput* viewer_node,
@@ -29,11 +32,125 @@ ExportTask::ExportTask(ViewerOutput* viewer_node,
color_manager_(color_manager),
params_(params)
{
SetTitle(tr("Exporting \"%1\"").arg(viewer_node->media_name()));
}
bool ExportTask::Run()
{
return true;
TimeRange range;
encoder_ = Encoder::CreateFromID(params_.encoder(), params_);
if (!encoder_) {
SetError(tr("Failed to create encoder"));
return false;
}
if (!encoder_->Open()) {
SetError(tr("Failed to open file"));
encoder_->deleteLater();
return false;
}
if (params_.has_custom_range()) {
// Render custom range only
range = params_.custom_range();
} else {
// Render entire sequence
range = TimeRange(0, viewer()->GetLength());
}
frame_time_ = Timecode::time_to_timestamp(range.in(), viewer()->video_params().time_base());
QMatrix4x4 mat;
if (params_.video_enabled()) {
// If a transformation matrix is applied to this video, create it here
if (params_.video_scaling_method() != ExportParams::kStretch) {
mat = ExportParams::GenerateMatrix(params_.video_scaling_method(),
viewer()->video_params().width(),
viewer()->video_params().height(),
params_.video_params().width(),
params_.video_params().height());
}
// Create color processor
color_processor_ = ColorProcessor::Create(color_manager_,
color_manager_->GetReferenceColorSpace(),
params_.color_transform());
}
TimeRangeList ranges;
ranges.InsertTimeRange(range);
Render(ranges, RenderMode::kOnline, mat, true, 1);
bool success = true;
foreach (QFuture<bool> f, write_frame_futures_) {
f.waitForFinished();
if (!f.result()) {
SetError(tr("Failed to write AVFrame"));
success = false;
}
}
encoder_->Close();
encoder_->deleteLater();
return success;
}
void FrameColorConvert(ColorProcessorPtr processor, FramePtr frame)
{
qDebug() << "Converting" << frame->timestamp() << "from" << frame->format();
// OCIO conversion requires a frame in 32F format
if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) {
frame = PixelFormat::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F);
}
// Color conversion must be done with unassociated alpha, and the pipeline is always associated
ColorManager::DisassociateAlpha(frame);
// Convert color space
processor->ConvertFrame(frame);
// Re-associate alpha
ColorManager::ReassociateAlpha(frame);
}
QFuture<void> ExportTask::DownloadFrame(FramePtr frame, const QByteArray &hash)
{
rendered_frame_.insert(hash, frame);
return QtConcurrent::run(FrameColorConvert, color_processor_, frame);
}
void ExportTask::FrameDownloaded(const QByteArray &hash, const QLinkedList<rational> &times)
{
FramePtr f = rendered_frame_.value(hash);
foreach (const rational& t, times) {
time_map_.insert(t, f);
}
forever {
rational real_time = Timecode::timestamp_to_time(frame_time_,
viewer()->video_params().time_base());
if (!time_map_.contains(real_time)) {
break;
}
// Unfortunately this can't be done in another thread since the frames need to be sent
// one after the other chronologically.
encoder_->WriteFrame(time_map_.value(real_time), real_time);
frame_time_++;
}
}
OLIVE_NAMESPACE_EXIT
+17
View File
@@ -38,11 +38,28 @@ public:
public slots:
virtual bool Run() override;
protected:
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) override;
virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList<rational>& times) override;
private:
QHash<QByteArray, FramePtr> rendered_frame_;
QHash<rational, FramePtr> time_map_;
QList< QFuture<bool> > write_frame_futures_;
ColorManager* color_manager_;
ExportParams params_;
Encoder* encoder_;
ColorProcessorPtr color_processor_;
int64_t frame_time_;
};
OLIVE_NAMESPACE_EXIT
+43 -45
View File
@@ -36,6 +36,11 @@ struct TimeHashFuturePair {
QFuture<QByteArray> hash_future;
};
struct HashTimePair {
rational time;
QByteArray hash;
};
struct HashFrameFuturePair {
QByteArray hash;
QFuture<FramePtr> frame_future;
@@ -46,24 +51,23 @@ struct HashDownloadFuturePair {
QFuture<void> download_future;
};
struct HashTimePair {
rational time;
QByteArray hash;
};
void RenderTask::Render(TimeRangeList range_to_cache, int divider)
void RenderTask::Render(TimeRangeList range_to_cache,
RenderMode::Mode mode,
const QMatrix4x4& mat,
bool audio_enabled,
int divider)
{
OpenGLBackend backend;
RenderMode::Mode mode = RenderMode::kOffline;
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
backend.SetAudioEnabled(false);
backend.SetAudioMode(audio_enabled ? RenderBackend::kAudioRender : RenderBackend::kAudioDisabled);
backend.SetViewerNode(viewer_);
backend.SetPixelFormat(format);
backend.SetMode(mode);
backend.SetDivider(divider);
backend.SetSampleFormat(SampleFormat::kInternalFormat);
backend.SetVideoDownloadMatrix(mat);
// Get hashes for each frame
QLinkedList<TimeHashFuturePair> hash_list;
@@ -126,58 +130,52 @@ void RenderTask::Render(TimeRangeList range_to_cache, int divider)
}
}
// Start downloading frames that have finished
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
OIIO::ImageSpec output_spec(viewer_->video_params().width() / divider,
viewer_->video_params().height() / divider,
OIIO::ImageSpec output_spec(viewer()->video_params().width() / divider,
viewer()->video_params().height() / divider,
PixelFormat::ChannelCount(format),
output_desc);
// Start downloading frames that have finished
{
int counter = 0;
int nb_frames = render_lookup_table.size();
int counter = 0;
int nb_frames = render_lookup_table.size();
QLinkedList<HashDownloadFuturePair> download_futures;
QLinkedList<HashDownloadFuturePair> download_futures;
// Iterators
QLinkedList<HashFrameFuturePair>::iterator i;
QLinkedList<HashDownloadFuturePair>::iterator j;
// Iterators
QLinkedList<HashFrameFuturePair>::iterator i;
QLinkedList<HashDownloadFuturePair>::iterator j;
while (!render_lookup_table.isEmpty() || !download_futures.isEmpty()) {
i = render_lookup_table.begin();
while (!render_lookup_table.isEmpty() || !download_futures.isEmpty()) {
i = render_lookup_table.begin();
while (i != render_lookup_table.end()) {
if (i->frame_future.isFinished()) {
FramePtr f = i->frame_future.result();
while (i != render_lookup_table.end()) {
if (i->frame_future.isFinished()) {
FramePtr f = i->frame_future.result();
// Start multithreaded download here
download_futures.append({i->hash,
QtConcurrent::run(FrameHashCache::SaveCacheFrame, i->hash, f)});
// Start multithreaded download here
download_futures.append({i->hash, DownloadFrame(f, i->hash)});
i = render_lookup_table.erase(i);
} else {
i++;
}
i = render_lookup_table.erase(i);
} else {
i++;
}
}
j = download_futures.begin();
j = download_futures.begin();
while (j != download_futures.end()) {
if (j->download_future.isFinished()) {
// Place it in the cache
const QLinkedList<rational>& times_with_hash = times_to_render.value(j->hash);
foreach (const rational& t, times_with_hash) {
viewer_->video_frame_cache()->SetHash(t, j->hash);
}
while (j != download_futures.end()) {
if (j->download_future.isFinished()) {
// Place it in the cache
FrameDownloaded(j->hash, times_to_render.value(j->hash));
// Signal process
counter++;
emit ProgressChanged(static_cast<double>(counter) / static_cast<double>(nb_frames));
// Signal process
counter++;
emit ProgressChanged(static_cast<double>(counter) / static_cast<double>(nb_frames));
j = download_futures.erase(j);
} else {
j++;
}
j = download_futures.erase(j);
} else {
j++;
}
}
}
+7 -1
View File
@@ -21,6 +21,8 @@
#ifndef RENDERTASK_H
#define RENDERTASK_H
#include <QtConcurrent/QtConcurrent>
#include "node/output/viewer/viewer.h"
#include "task/task.h"
@@ -32,13 +34,17 @@ public:
RenderTask(ViewerOutput* viewer);
protected:
void Render(TimeRangeList range_to_cache, int divider = 1);
void Render(TimeRangeList range_to_cache, RenderMode::Mode mode, const QMatrix4x4 &mat, bool audio_enabled, int divider);
ViewerOutput* viewer() const
{
return viewer_;
}
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) = 0;
virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList<rational>& times) = 0;
private:
ViewerOutput* viewer_;