exporter: add ability to cancel exports (including a confirm to cancel if the

user closes the dialog mid-export)
This commit is contained in:
itsmattkc
2020-03-03 02:37:18 +11:00
parent 02909c6ba9
commit 0f5cd40c45
7 changed files with 112 additions and 30 deletions
+60 -21
View File
@@ -1,5 +1,6 @@
#include "export.h" #include "export.h"
#include <QCloseEvent>
#include <QFileDialog> #include <QFileDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@@ -11,14 +12,15 @@
#include "project/item/sequence/sequence.h" #include "project/item/sequence/sequence.h"
#include "project/project.h" #include "project/project.h"
#include "render/backend/exporter.h"
#include "render/pixelformat.h" #include "render/pixelformat.h"
#include "ui/icons/icons.h" #include "ui/icons/icons.h"
ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
QDialog(parent), QDialog(parent),
viewer_node_(viewer_node), viewer_node_(viewer_node),
previously_selected_format_(0) previously_selected_format_(0),
exporter_(nullptr),
cancelled_(false)
{ {
QHBoxLayout* layout = new QHBoxLayout(this); QHBoxLayout* layout = new QHBoxLayout(this);
@@ -26,8 +28,13 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
splitter->setChildrenCollapsible(false); splitter->setChildrenCollapsible(false);
layout->addWidget(splitter); layout->addWidget(splitter);
QWidget* outer_preferences_area = new QWidget();
QVBoxLayout* outer_preferences_layout = new QVBoxLayout(outer_preferences_area);
preferences_area_ = new QWidget(); preferences_area_ = new QWidget();
outer_preferences_layout->addWidget(preferences_area_);
QGridLayout* preferences_layout = new QGridLayout(preferences_area_); QGridLayout* preferences_layout = new QGridLayout(preferences_area_);
preferences_layout->setMargin(0);
int row = 0; int row = 0;
@@ -110,14 +117,20 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
preferences_tabs->addTab(audio_area, tr("Audio")); preferences_tabs->addTab(audio_area, tr("Audio"));
preferences_layout->addWidget(preferences_tabs, row, 0, 1, 4); preferences_layout->addWidget(preferences_tabs, row, 0, 1, 4);
row++; QHBoxLayout* progress_bar_layout = new QHBoxLayout();
progress_bar_layout->setMargin(0);
outer_preferences_layout->addLayout(progress_bar_layout);
progress_bar_ = new QProgressBar(); progress_bar_ = new QProgressBar();
progress_bar_->setEnabled(false); progress_bar_->setEnabled(false);
progress_bar_->setValue(0); progress_bar_->setValue(0);
preferences_layout->addWidget(progress_bar_, row, 0, 1, 4); progress_bar_layout->addWidget(progress_bar_);
row++; export_cancel_btn_ = new QPushButton();
export_cancel_btn_->setIcon(icon::Error);
export_cancel_btn_->setEnabled(false);
connect(export_cancel_btn_, &QPushButton::clicked, this, &ExportDialog::CancelExport);
progress_bar_layout->addWidget(export_cancel_btn_);
buttons_ = new QDialogButtonBox(); buttons_ = new QDialogButtonBox();
buttons_->setCenterButtons(true); buttons_->setCenterButtons(true);
@@ -125,9 +138,9 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
buttons_->addButton(QDialogButtonBox::Cancel); buttons_->addButton(QDialogButtonBox::Cancel);
connect(buttons_, SIGNAL(accepted()), this, SLOT(accept())); connect(buttons_, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons_, SIGNAL(rejected()), this, SLOT(reject())); connect(buttons_, SIGNAL(rejected()), this, SLOT(reject()));
preferences_layout->addWidget(buttons_, row, 0, 1, 4); outer_preferences_layout->addWidget(buttons_);
splitter->addWidget(preferences_area_); splitter->addWidget(outer_preferences_area);
QWidget* preview_area = new QWidget(); QWidget* preview_area = new QWidget();
QVBoxLayout* preview_layout = new QVBoxLayout(preview_area); QVBoxLayout* preview_layout = new QVBoxLayout(preview_area);
@@ -244,26 +257,38 @@ void ExportDialog::accept()
Encoder* encoder = Encoder::CreateFromID("ffmpeg", encoding_params); Encoder* encoder = Encoder::CreateFromID("ffmpeg", encoding_params);
Exporter* exporter = new Exporter(viewer_node_, encoder); exporter_ = new Exporter(viewer_node_, encoder);
if (video_enabled_->isChecked()) { if (video_enabled_->isChecked()) {
exporter->EnableVideo(video_render_params, transform, color_processor); exporter_->EnableVideo(video_render_params, transform, color_processor);
} }
if (audio_enabled_->isChecked()) { if (audio_enabled_->isChecked()) {
exporter->EnableAudio(audio_render_params); exporter_->EnableAudio(audio_render_params);
} }
connect(exporter, &Exporter::ExportEnded, this, &ExportDialog::ExporterIsDone); connect(exporter_, &Exporter::ExportEnded, this, &ExportDialog::ExporterIsDone);
connect(exporter, &Exporter::ProgressChanged, progress_bar_, &QProgressBar::setValue); connect(exporter_, &Exporter::ProgressChanged, progress_bar_, &QProgressBar::setValue);
QMetaObject::invokeMethod(exporter, "StartExporting", Qt::QueuedConnection); QMetaObject::invokeMethod(exporter_, "StartExporting", Qt::QueuedConnection);
SetUIElementsEnabled(false); SetUIElementsEnabled(false);
} }
void ExportDialog::closeEvent(QCloseEvent *e) void ExportDialog::closeEvent(QCloseEvent *e)
{ {
if (exporter_) {
if (QMessageBox::question(this,
tr("Still Exporting"),
tr("This sequence is still being exported. Do you wish to cancel it?"),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
CancelExport();
} else {
e->ignore();
return;
}
}
preview_viewer_->ConnectViewerNode(nullptr); preview_viewer_->ConnectViewerNode(nullptr);
QDialog::closeEvent(e); QDialog::closeEvent(e);
@@ -464,7 +489,9 @@ QMatrix4x4 ExportDialog::GenerateMatrix(ExportVideoTab::ScalingMethod method, in
void ExportDialog::SetUIElementsEnabled(bool enabled) void ExportDialog::SetUIElementsEnabled(bool enabled)
{ {
preferences_area_->setEnabled(enabled); preferences_area_->setEnabled(enabled);
//buttons_->setEnabled(false); buttons_->setEnabled(enabled);
export_cancel_btn_->setEnabled(!enabled);
} }
void ExportDialog::UpdateViewerDimensions() void ExportDialog::UpdateViewerDimensions()
@@ -481,9 +508,7 @@ void ExportDialog::UpdateViewerDimensions()
void ExportDialog::ExporterIsDone() void ExportDialog::ExporterIsDone()
{ {
Exporter* exporter = static_cast<Exporter*>(sender()); if (exporter_->GetExportStatus()) {
if (exporter->GetExportStatus()) {
QMessageBox::information(this, QMessageBox::information(this,
tr("Export Status"), tr("Export Status"),
tr("Export completed successfully."), tr("Export completed successfully."),
@@ -491,11 +516,25 @@ void ExportDialog::ExporterIsDone()
QDialog::accept(); QDialog::accept();
} else { } else {
QMessageBox::critical(this, if (!cancelled_) {
tr("Export Status"), QMessageBox::critical(this,
tr("Export failed: %1").arg(exporter->GetExportError()), tr("Export Status"),
QMessageBox::Ok); tr("Export failed: %1").arg(exporter_->GetExportError()),
QMessageBox::Ok);
}
SetUIElementsEnabled(true); SetUIElementsEnabled(true);
} }
exporter_->deleteLater();
exporter_ = nullptr;
cancelled_ = false;
}
void ExportDialog::CancelExport()
{
if (exporter_) {
cancelled_ = true;
exporter_->Cancel();
}
} }
+8
View File
@@ -11,6 +11,7 @@
#include "exportcodec.h" #include "exportcodec.h"
#include "exportformat.h" #include "exportformat.h"
#include "exportvideotab.h" #include "exportvideotab.h"
#include "render/backend/exporter.h"
#include "widget/viewer/viewer.h" #include "widget/viewer/viewer.h"
class ExportDialog : public QDialog class ExportDialog : public QDialog
@@ -48,6 +49,8 @@ private:
QLineEdit* filename_edit_; QLineEdit* filename_edit_;
QComboBox* format_combobox_; QComboBox* format_combobox_;
Exporter* exporter_;
ExportVideoTab* video_tab_; ExportVideoTab* video_tab_;
ExportAudioTab* audio_tab_; ExportAudioTab* audio_tab_;
@@ -59,6 +62,9 @@ private:
QWidget* preferences_area_; QWidget* preferences_area_;
QDialogButtonBox* buttons_; QDialogButtonBox* buttons_;
QPushButton* export_cancel_btn_;
bool cancelled_;
enum Format { enum Format {
kFormatDNxHD, kFormatDNxHD,
@@ -102,6 +108,8 @@ private slots:
void ExporterIsDone(); void ExporterIsDone();
void CancelExport();
}; };
#endif // EXPORTDIALOG_H #endif // EXPORTDIALOG_H
+25 -4
View File
@@ -50,6 +50,24 @@ const QString &Exporter::GetExportError() const
return export_msg_; return export_msg_;
} }
void Exporter::Cancel()
{
if (video_backend_) {
video_backend_->CancelQueue();
video_backend_->deleteLater();
video_backend_ = nullptr;
}
if (audio_backend_) {
audio_backend_->CancelQueue();
audio_backend_->deleteLater();
audio_backend_ = nullptr;
}
SetExportMessage(tr("User cancelled export"));
ExportStopped();
}
void Exporter::StartExporting() void Exporter::StartExporting()
{ {
// Default to error state until ExportEnd is called // Default to error state until ExportEnd is called
@@ -101,6 +119,7 @@ void Exporter::ExportSucceeded()
if (video_backend_) { if (video_backend_) {
video_backend_->deleteLater(); video_backend_->deleteLater();
video_backend_ = nullptr;
} }
export_status_ = true; export_status_ = true;
@@ -112,9 +131,10 @@ void Exporter::ExportSucceeded()
Qt::QueuedConnection); Qt::QueuedConnection);
} }
void Exporter::ExportFailed() void Exporter::ExportStopped()
{ {
emit ExportEnded(); emit ExportEnded();
encoder_->deleteLater();
} }
void Exporter::EncodeFrame() void Exporter::EncodeFrame()
@@ -195,6 +215,7 @@ void Exporter::AudioRendered()
// We don't need the audio backend anymore // We don't need the audio backend anymore
audio_backend_->deleteLater(); audio_backend_->deleteLater();
audio_backend_ = nullptr;
} }
void Exporter::AudioEncodeComplete() void Exporter::AudioEncodeComplete()
@@ -225,14 +246,14 @@ void Exporter::EncoderOpenedSuccessfully()
void Exporter::EncoderOpenFailed() void Exporter::EncoderOpenFailed()
{ {
SetExportMessage("Failed to open encoder"); SetExportMessage(tr("Failed to open encoder"));
ExportFailed(); ExportStopped();
} }
void Exporter::EncoderClosed() void Exporter::EncoderClosed()
{ {
emit ProgressChanged(100); emit ProgressChanged(100);
emit ExportEnded(); ExportStopped();
} }
void Exporter::VideoHashesComplete() void Exporter::VideoHashesComplete()
+3 -1
View File
@@ -26,6 +26,8 @@ public:
bool GetExportStatus() const; bool GetExportStatus() const;
const QString& GetExportError() const; const QString& GetExportError() const;
void Cancel();
public slots: public slots:
void StartExporting(); void StartExporting();
@@ -58,7 +60,7 @@ protected:
private: private:
void ExportSucceeded(); void ExportSucceeded();
void ExportFailed(); void ExportStopped();
void EncodeFrame(); void EncodeFrame();
+9 -3
View File
@@ -31,8 +31,10 @@ bool OpenGLBackend::InitInternal()
if (!proxy_->Init()) { if (!proxy_->Init()) {
proxy_thread->quit(); proxy_thread->quit();
proxy_thread->wait(); proxy_thread->wait();
delete proxy_thread; proxy_thread->deleteLater();
delete proxy_;
proxy_->deleteLater();
proxy_ = nullptr;
return false; return false;
} }
@@ -54,7 +56,11 @@ bool OpenGLBackend::InitInternal()
void OpenGLBackend::CloseInternal() void OpenGLBackend::CloseInternal()
{ {
if (proxy_) { if (proxy_) {
delete proxy_; proxy_->thread()->quit();
proxy_->thread()->wait();
proxy_->thread()->deleteLater();
proxy_->deleteLater();
proxy_ = nullptr; proxy_ = nullptr;
} }
@@ -21,6 +21,8 @@ OpenGLProxy::OpenGLProxy(QObject *parent) :
OpenGLProxy::~OpenGLProxy() OpenGLProxy::~OpenGLProxy()
{ {
Close();
surface_.destroy(); surface_.destroy();
} }
@@ -178,6 +180,7 @@ void OpenGLProxy::Close()
buffer_.Destroy(); buffer_.Destroy();
functions_ = nullptr; functions_ = nullptr;
delete ctx_; delete ctx_;
ctx_ = nullptr;
} }
void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable *output_params) void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable *output_params)
+4 -1
View File
@@ -326,7 +326,10 @@ void RenderBackend::CancelQueue()
if (processor_busy_state_.at(i)) if (processor_busy_state_.at(i))
busy++; busy++;
} }
qDebug() << this << "is waiting for" << busy << "busy workers";
if (busy) {
qDebug() << this << "is waiting for" << busy << "busy workers";
}
cancel_dialog_->RunIfWorkersAreBusy(); cancel_dialog_->RunIfWorkersAreBusy();
} }