exporter: updated exporter to function with new render system

This commit is contained in:
itsmattkc
2020-02-25 20:03:15 +11:00
parent ab19e445bd
commit 78daf9e28a
15 changed files with 153 additions and 249 deletions
+25 -14
View File
@@ -1,6 +1,5 @@
#include "export.h"
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
@@ -12,7 +11,7 @@
#include "project/item/sequence/sequence.h"
#include "project/project.h"
#include "render/backend/opengl/openglexporter.h"
#include "render/backend/exporter.h"
#include "render/pixelservice.h"
#include "ui/icons/icons.h"
@@ -27,8 +26,8 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
splitter->setChildrenCollapsible(false);
layout->addWidget(splitter);
QWidget* preferences_area = new QWidget();
QGridLayout* preferences_layout = new QGridLayout(preferences_area);
preferences_area_ = new QWidget();
QGridLayout* preferences_layout = new QGridLayout(preferences_area_);
int row = 0;
@@ -120,15 +119,15 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
row++;
QDialogButtonBox* buttons = new QDialogButtonBox();
buttons->setCenterButtons(true);
buttons->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
buttons->addButton(QDialogButtonBox::Cancel);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
preferences_layout->addWidget(buttons, row, 0, 1, 4);
buttons_ = new QDialogButtonBox();
buttons_->setCenterButtons(true);
buttons_->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
buttons_->addButton(QDialogButtonBox::Cancel);
connect(buttons_, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons_, SIGNAL(rejected()), this, SLOT(reject()));
preferences_layout->addWidget(buttons_, row, 0, 1, 4);
splitter->addWidget(preferences_area);
splitter->addWidget(preferences_area_);
QWidget* preview_area = new QWidget();
QVBoxLayout* preview_layout = new QVBoxLayout(preview_area);
@@ -242,7 +241,7 @@ void ExportDialog::accept()
Encoder* encoder = Encoder::CreateFromID("ffmpeg", encoding_params);
OpenGLExporter* exporter = new OpenGLExporter(viewer_node_, encoder);
Exporter* exporter = new Exporter(viewer_node_, encoder);
if (video_enabled_->isChecked()) {
exporter->EnableVideo(video_render_params, transform, color_processor);
@@ -256,6 +255,8 @@ void ExportDialog::accept()
connect(exporter, &Exporter::ProgressChanged, progress_bar_, &QProgressBar::setValue);
QMetaObject::invokeMethod(exporter, "StartExporting", Qt::QueuedConnection);
SetUIElementsEnabled(false);
}
void ExportDialog::closeEvent(QCloseEvent *e)
@@ -442,6 +443,12 @@ QMatrix4x4 ExportDialog::GenerateMatrix(ExportVideoTab::ScalingMethod method, in
return preview_matrix;
}
void ExportDialog::SetUIElementsEnabled(bool enabled)
{
preferences_area_->setEnabled(enabled);
//buttons_->setEnabled(false);
}
void ExportDialog::UpdateViewerDimensions()
{
preview_viewer_->SetOverrideSize(static_cast<int>(video_tab_->width_slider()->GetValue()),
@@ -461,12 +468,16 @@ void ExportDialog::ExporterIsDone()
if (exporter->GetExportStatus()) {
QMessageBox::information(this,
tr("Export Status"),
tr("Export completed successfully!"),
tr("Export completed successfully."),
QMessageBox::Ok);
QDialog::accept();
} else {
QMessageBox::critical(this,
tr("Export Status"),
tr("Export failed: %1").arg(exporter->GetExportError()),
QMessageBox::Ok);
SetUIElementsEnabled(true);
}
}
+6
View File
@@ -3,6 +3,7 @@
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
#include <QProgressBar>
@@ -31,6 +32,8 @@ private:
QMatrix4x4 GenerateMatrix(ExportVideoTab::ScalingMethod method, int source_width, int source_height, int dest_width, int height);
void SetUIElementsEnabled(bool enabled);
ViewerOutput* viewer_node_;
QList<ExportFormat> formats_;
@@ -54,6 +57,9 @@ private:
QProgressBar* progress_bar_;
QWidget* preferences_area_;
QDialogButtonBox* buttons_;
enum Format {
kFormatDNxHD,
kFormatMatroska,
+53 -65
View File
@@ -1,5 +1,7 @@
#include "exporter.h"
#include "render/backend/audio/audiobackend.h"
#include "render/backend/opengl/openglbackend.h"
#include "render/colormanager.h"
#include "render/pixelservice.h"
@@ -50,15 +52,11 @@ void Exporter::StartExporting()
// Default to error state until ExportEnd is called
export_status_ = false;
// Create renderers
if (!Initialize()) {
SetExportMessage("Failed to initialize exporter");
ExportFailed();
return;
}
// Create renderers
if (!video_done_) {
video_backend_ = new OpenGLBackend();
video_backend_->SetLimitCaching(false);
video_backend_->SetViewerNode(viewer_node_);
video_backend_->SetParameters(VideoRenderingParams(viewer_node_->video_params().width(),
viewer_node_->video_params().height(),
@@ -70,6 +68,8 @@ void Exporter::StartExporting()
}
if (!audio_done_) {
audio_backend_ = new AudioBackend();
audio_backend_->SetViewerNode(viewer_node_);
audio_backend_->SetParameters(audio_params_);
}
@@ -96,8 +96,6 @@ void Exporter::ExportSucceeded()
return;
}
Cleanup();
if (video_backend_) {
video_backend_->deleteLater();
}
@@ -116,74 +114,64 @@ void Exporter::ExportFailed()
emit ExportEnded();
}
void Exporter::EncodeFrame(const rational &time, QVariant value)
void Exporter::EncodeFrame()
{
if (time == waiting_for_frame_) {
bool get_cached = false;
while (cached_frames_.contains(waiting_for_frame_)) {
FramePtr frame = cached_frames_.take(waiting_for_frame_);
do {
if (get_cached) {
value = cached_frames_.take(waiting_for_frame_);
} else {
get_cached = true;
}
// Convert texture to frame
FramePtr frame = TextureToFrame(value);
// OCIO conversion requires a frame in 32F format
if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) {
frame = PixelService::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
color_processor_->ConvertFrame(frame);
// Set frame timestamp
frame->set_timestamp(waiting_for_frame_);
// Encode (may require re-associating alpha?)
QMetaObject::invokeMethod(encoder_,
"WriteFrame",
Qt::QueuedConnection,
Q_ARG(FramePtr, frame));
waiting_for_frame_ += video_params_.time_base();
// Calculate progress
int progress = qRound(100.0 * (waiting_for_frame_.toDouble() / viewer_node_->Length().toDouble()));
emit ProgressChanged(progress);
} while (cached_frames_.contains(waiting_for_frame_));
if (waiting_for_frame_ >= viewer_node_->Length()) {
video_done_ = true;
ExportSucceeded();
// OCIO conversion requires a frame in 32F format
if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) {
frame = PixelService::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F);
}
} else {
cached_frames_.insert(time, value);
// Color conversion must be done with unassociated alpha, and the pipeline is always associated
ColorManager::DisassociateAlpha(frame);
// Convert color space
color_processor_->ConvertFrame(frame);
// Set frame timestamp
frame->set_timestamp(waiting_for_frame_);
// Encode (may require re-associating alpha?)
QMetaObject::invokeMethod(encoder_,
"WriteFrame",
Qt::QueuedConnection,
Q_ARG(FramePtr, frame));
waiting_for_frame_ += video_params_.time_base();
// Calculate progress
int progress = qRound(100.0 * (waiting_for_frame_.toDouble() / viewer_node_->Length().toDouble()));
emit ProgressChanged(progress);
}
if (waiting_for_frame_ >= viewer_node_->Length()) {
video_done_ = true;
ExportSucceeded();
}
}
void Exporter::FrameRendered(const rational &time, QVariant value)
void Exporter::FrameRendered(const rational &time, FramePtr value)
{
qDebug() << "Received" << time.toDouble() << "- waiting for" << waiting_for_frame_.toDouble();
const QMap<rational, QByteArray>& time_hash_map = video_backend_->frame_cache()->time_hash_map();
QByteArray map_hash = time_hash_map.value(time);
QByteArray this_hash = time_hash_map.value(time);
EncodeFrame(time, value);
qDebug() << "Received" << this_hash.toHex();
QList<rational> matching_times = time_hash_map.keys(this_hash);
QList<rational> matching_times = matched_frames_.value(map_hash);
foreach (const rational& t, matching_times) {
qDebug() << " Also matches" << t;
EncodeFrame(t, value);
qDebug() << " Matches" << t.toDouble();
cached_frames_.insert(t, value);
}
qDebug() << " Waiting for" << waiting_for_frame_.toDouble();
EncodeFrame();
}
void Exporter::AudioRendered()
@@ -279,7 +267,7 @@ void Exporter::VideoHashesComplete()
video_backend_->SetOnlySignalLastFrameRequested(false);
// FIXME: Exporting is now broken because of this
//connect(video_backend_, &VideoRenderBackend::CachedFrameReady, this, &Exporter::FrameRendered);
connect(video_backend_, &VideoRenderBackend::GeneratedFrame, this, &Exporter::FrameRendered);
foreach (const TimeRange& range, ranges) {
video_backend_->InvalidateCache(range);
+3 -8
View File
@@ -34,11 +34,6 @@ signals:
void ExportEnded();
protected:
virtual bool Initialize() = 0;
virtual void Cleanup() = 0;
virtual FramePtr TextureToFrame(const QVariant &texture) = 0;
void SetExportMessage(const QString& s);
// Renderers
@@ -64,7 +59,7 @@ private:
void ExportFailed();
void EncodeFrame(const rational &time, QVariant value);
void EncodeFrame();
ColorProcessorPtr color_processor_;
@@ -76,12 +71,12 @@ private:
rational waiting_for_frame_;
QHash<rational, QVariant> cached_frames_;
QHash<rational, FramePtr> cached_frames_;
QHash< QByteArray, QList<rational> > matched_frames_;
private slots:
void FrameRendered(const rational& time, QVariant value);
void FrameRendered(const rational &time, FramePtr value);
void AudioRendered();
-2
View File
@@ -20,8 +20,6 @@ set(OLIVE_SOURCES
render/backend/opengl/openglbackend.cpp
render/backend/opengl/openglcolorprocessor.h
render/backend/opengl/openglcolorprocessor.cpp
render/backend/opengl/openglexporter.h
render/backend/opengl/openglexporter.cpp
render/backend/opengl/openglframebuffer.h
render/backend/opengl/openglframebuffer.cpp
render/backend/opengl/openglproxy.h
@@ -1,96 +0,0 @@
#include "openglexporter.h"
#include "render/backend/opengl/openglrenderfunctions.h"
#include "render/pixelservice.h"
OpenGLExporter::OpenGLExporter(ViewerOutput* viewer, Encoder *encoder, QObject* parent) :
Exporter(viewer, encoder, parent),
texture_(nullptr),
pipeline_(nullptr)
{
}
OpenGLExporter::~OpenGLExporter()
{
}
bool OpenGLExporter::Initialize()
{
QOpenGLContext* ctx = QOpenGLContext::currentContext();
// Create rendering backends
if (!video_done_) {
video_backend_ = new OpenGLBackend();
// Create blitting framebuffer and texture
buffer_.Create(ctx);
texture_ = std::make_shared<OpenGLTexture>();
texture_->Create(ctx, video_params_.effective_width(), video_params_.effective_height(), video_params_.format());
pipeline_ = OpenGLShader::CreateDefault();
}
if (!audio_done_) {
audio_backend_ = new AudioBackend();
}
return true;
}
void OpenGLExporter::Cleanup()
{
if (texture_) {
texture_->Destroy();
texture_ = nullptr;
}
pipeline_ = nullptr;
buffer_.Destroy();
}
FramePtr OpenGLExporter::TextureToFrame(const QVariant& texture)
{
FramePtr frame = Frame::Create();
frame->set_width(video_params_.width());
frame->set_height(video_params_.height());
frame->set_format(video_params_.format());
frame->allocate();
// Blit for transform if the width/height are different
OpenGLTexturePtr input_tex = texture.value<OpenGLTexturePtr>();
if (input_tex) {
QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions();
f->glViewport(0, 0, texture_->width(), texture_->height());
buffer_.Attach(texture_);
buffer_.Bind();
input_tex->Bind();
OpenGLRenderFunctions::Blit(pipeline_, false, transform_);
input_tex->Release();
buffer_.Release();
buffer_.Detach();
// Perform OpenGL read
buffer_.Attach(texture_);
buffer_.Bind();
PixelFormat::Info format_info = PixelService::GetPixelFormatInfo(video_params_.format());
f->glReadPixels(0,
0,
texture_->width(),
texture_->height(),
format_info.pixel_format,
format_info.gl_pixel_type,
frame->data());
buffer_.Release();
buffer_.Detach();
}
return frame;
}
@@ -1,35 +0,0 @@
#ifndef OPENGLEXPORTER_H
#define OPENGLEXPORTER_H
#include "render/backend/exporter.h"
#include "render/backend/opengl/openglbackend.h"
#include "render/backend/audio/audiobackend.h"
#include "render/backend/opengl/openglframebuffer.h"
#include "render/backend/opengl/opengltexture.h"
class OpenGLExporter : public Exporter
{
public:
OpenGLExporter(ViewerOutput* viewer,
Encoder* encoder,
QObject* parent = nullptr);
virtual ~OpenGLExporter() override;
protected:
virtual bool Initialize() override;
virtual void Cleanup() override;
virtual FramePtr TextureToFrame(const QVariant &texture) override;
private:
OpenGLFramebuffer buffer_;
OpenGLTexturePtr texture_;
OpenGLShaderPtr pipeline_;
};
#endif // OPENGLEXPORTER_H
+2 -2
View File
@@ -380,7 +380,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, c
output_params->Push(NodeParam::kTexture, QVariant::fromValue(output_tex));
}
void OpenGLProxy::TextureToBuffer(const QVariant &tex_in, QByteArray &buffer)
void OpenGLProxy::TextureToBuffer(const QVariant &tex_in, void *buffer)
{
OpenGLTextureCache::ReferencePtr texture = tex_in.value<OpenGLTextureCache::ReferencePtr>();
@@ -396,7 +396,7 @@ void OpenGLProxy::TextureToBuffer(const QVariant &tex_in, QByteArray &buffer)
video_params_.effective_height(),
format_info.pixel_format,
format_info.gl_pixel_type,
buffer.data());
buffer);
buffer_.Release();
buffer_.Detach();
+1 -1
View File
@@ -45,7 +45,7 @@ public:
void RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
void TextureToBuffer(const QVariant& texture, QByteArray& buffer);
void TextureToBuffer(const QVariant& texture, void *buffer);
void SetParameters(const VideoRenderingParams& params);
+1 -1
View File
@@ -24,7 +24,7 @@ void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range,
emit RequestRunNodeAccelerated(node, range, input_params, output_params);
}
void OpenGLWorker::TextureToBuffer(const QVariant &tex_in, QByteArray &buffer)
void OpenGLWorker::TextureToBuffer(const QVariant &tex_in, void *buffer)
{
emit RequestTextureToBuffer(tex_in, buffer);
}
+2 -2
View File
@@ -20,14 +20,14 @@ signals:
void RequestRunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
void RequestTextureToBuffer(const QVariant& texture, QByteArray& buffer);
void RequestTextureToBuffer(const QVariant& texture, void *buffer);
protected:
virtual void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table) override;
virtual void RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params) override;
virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) override;
virtual void TextureToBuffer(const QVariant& texture, void *buffer) override;
};
+19 -6
View File
@@ -37,7 +37,8 @@
VideoRenderBackend::VideoRenderBackend(QObject *parent) :
RenderBackend(parent),
operating_mode_(VideoRenderWorker::kHashRenderCache),
only_signal_last_frame_requested_(true)
only_signal_last_frame_requested_(true),
limit_caching_(true)
{
connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &VideoRenderBackend::FrameRemovedFromDiskCache);
}
@@ -108,6 +109,11 @@ bool VideoRenderBackend::IsRendered(const rational &time) const
return !TimeIsQueued(range) && !render_job_info_.contains(range);
}
void VideoRenderBackend::SetLimitCaching(bool limit)
{
limit_caching_ = limit;
}
bool VideoRenderBackend::GenerateCacheIDInternal(QCryptographicHash& hash)
{
if (!params_.is_valid()) {
@@ -138,6 +144,7 @@ void VideoRenderBackend::ConnectWorkerToThis(RenderWorker *processor)
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);
connect(video_processor, &VideoRenderWorker::GeneratedFrame, this, &VideoRenderBackend::GeneratedFrame, Qt::QueuedConnection);
}
void VideoRenderBackend::InvalidateCacheInternal(const rational &start_range, const rational &end_range)
@@ -371,13 +378,19 @@ bool VideoRenderBackend::SetFrameHash(const NodeDependency &dep, const QByteArra
void VideoRenderBackend::Requeue()
{
cache_queue_.clear();
if (limit_caching_) {
// 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>());
// 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_ = invalidated_.Intersects(queueable_range);
cache_queue_ = invalidated_.Intersects(queueable_range);
} else {
cache_queue_ = invalidated_;
}
CacheNext();
}
+6
View File
@@ -60,6 +60,8 @@ public:
bool IsRendered(const rational& time) const;
void SetLimitCaching(bool limit);
QString GetCachedFrame(const rational& time);
VideoRenderFrameCache* frame_cache();
@@ -102,6 +104,8 @@ signals:
void RangeInvalidated(const TimeRange& range);
void GeneratedFrame(const rational &time, FramePtr frame);
private:
bool TimeIsQueued(const TimeRange &time) const;
@@ -121,6 +125,8 @@ private:
bool only_signal_last_frame_requested_;
bool limit_caching_;
private slots:
void ThreadCompletedFrame(NodeDependency path, qint64 job_time, QByteArray hash, QVariant value);
void ThreadCompletedDownload(NodeDependency dep, qint64 job_time, QByteArray hash, bool texture_existed);
+31 -15
View File
@@ -66,14 +66,14 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path, con
emit CompletedFrame(path, job_time, hash, texture);
// If we actually have a texture, download it into the disk cache
if ((operating_mode_ & kDownloadOnly) && !texture.isNull()) {
Download(texture, frame_cache_->CachePathName(hash, video_params_.format()));
if (!texture.isNull()) {
Download(path.in(), texture, frame_cache_->CachePathName(hash, video_params_.format()));
}
frame_cache_->RemoveHashFromCurrentlyCaching(hash);
// Signal that this job is complete
if (operating_mode_ & kDownloadOnly) {
// Signal that this job is complete
emit CompletedDownload(path, job_time, hash, !texture.isNull());
}
@@ -223,7 +223,7 @@ void VideoRenderWorker::CloseInternal()
download_buffer_.clear();
}
void VideoRenderWorker::Download(QVariant texture, QString filename)
void VideoRenderWorker::Download(const rational& time, QVariant texture, QString filename)
{
PixelFormat::Info format_info = PixelService::GetPixelFormatInfo(video_params().format());
@@ -236,25 +236,41 @@ void VideoRenderWorker::Download(QVariant texture, QString filename)
spec.attribute("compression", "dwaa:200");
}
TextureToBuffer(texture, download_buffer_);
if (operating_mode_ & kDownloadOnly) {
std::string working_fn_std = filename.toStdString();
TextureToBuffer(texture, download_buffer_.data());
auto out = OIIO::ImageOutput::create(working_fn_std);
std::string working_fn_std = filename.toStdString();
// Keep export to this thread only
out->threads(1);
auto out = OIIO::ImageOutput::create(working_fn_std);
if (out) {
out->open(working_fn_std, spec);
out->write_image(format_info.oiio_desc, download_buffer_.data());
out->close();
// Keep export to this thread only
out->threads(1);
if (out) {
out->open(working_fn_std, spec);
out->write_image(format_info.oiio_desc, download_buffer_.data());
out->close();
#if OIIO_VERSION < 10903
OIIO::ImageOutput::destroy(out);
OIIO::ImageOutput::destroy(out);
#endif
} else {
qWarning() << "Failed to open output file:" << filename;
}
} else {
qWarning() << "Failed to open output file:" << filename;
FramePtr frame = Frame::Create();
frame->set_width(video_params().width());
frame->set_height(video_params().height());
frame->set_format(video_params().format());
frame->allocate();
TextureToBuffer(texture, frame->data());
emit GeneratedFrame(time, frame);
}
}
+4 -2
View File
@@ -56,6 +56,8 @@ signals:
void HashAlreadyExists(NodeDependency CurrentPath, qint64 job_time, QByteArray hash);
void GeneratedFrame(const rational &time, FramePtr frame);
void Aborted();
protected:
@@ -67,7 +69,7 @@ protected:
virtual void ParametersChangedEvent(){}
virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) = 0;
virtual void TextureToBuffer(const QVariant& texture, void *buffer) = 0;
virtual NodeValueTable RenderInternal(const NodeDependency& CurrentPath, const qint64& job_time) override;
@@ -80,7 +82,7 @@ protected:
private:
void HashNodeRecursively(QCryptographicHash* hash, const Node *n, const rational &time);
void Download(QVariant texture, QString filename);
void Download(const rational &time, QVariant texture, QString filename);
void ResizeDownloadBuffer();