diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index 2edfe6f18..2872c6b20 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -1,5 +1,6 @@ #include "export.h" +#include #include #include #include @@ -11,14 +12,15 @@ #include "project/item/sequence/sequence.h" #include "project/project.h" -#include "render/backend/exporter.h" #include "render/pixelformat.h" #include "ui/icons/icons.h" ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : QDialog(parent), viewer_node_(viewer_node), - previously_selected_format_(0) + previously_selected_format_(0), + exporter_(nullptr), + cancelled_(false) { QHBoxLayout* layout = new QHBoxLayout(this); @@ -26,8 +28,13 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : splitter->setChildrenCollapsible(false); layout->addWidget(splitter); + QWidget* outer_preferences_area = new QWidget(); + QVBoxLayout* outer_preferences_layout = new QVBoxLayout(outer_preferences_area); + preferences_area_ = new QWidget(); + outer_preferences_layout->addWidget(preferences_area_); QGridLayout* preferences_layout = new QGridLayout(preferences_area_); + preferences_layout->setMargin(0); int row = 0; @@ -110,14 +117,20 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : preferences_tabs->addTab(audio_area, tr("Audio")); 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_->setEnabled(false); 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_->setCenterButtons(true); @@ -125,9 +138,9 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : 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); + outer_preferences_layout->addWidget(buttons_); - splitter->addWidget(preferences_area_); + splitter->addWidget(outer_preferences_area); QWidget* preview_area = new QWidget(); QVBoxLayout* preview_layout = new QVBoxLayout(preview_area); @@ -244,26 +257,38 @@ void ExportDialog::accept() Encoder* encoder = Encoder::CreateFromID("ffmpeg", encoding_params); - Exporter* exporter = new Exporter(viewer_node_, encoder); + exporter_ = new Exporter(viewer_node_, encoder); if (video_enabled_->isChecked()) { - exporter->EnableVideo(video_render_params, transform, color_processor); + exporter_->EnableVideo(video_render_params, transform, color_processor); } if (audio_enabled_->isChecked()) { - exporter->EnableAudio(audio_render_params); + exporter_->EnableAudio(audio_render_params); } - connect(exporter, &Exporter::ExportEnded, this, &ExportDialog::ExporterIsDone); - connect(exporter, &Exporter::ProgressChanged, progress_bar_, &QProgressBar::setValue); + connect(exporter_, &Exporter::ExportEnded, this, &ExportDialog::ExporterIsDone); + connect(exporter_, &Exporter::ProgressChanged, progress_bar_, &QProgressBar::setValue); - QMetaObject::invokeMethod(exporter, "StartExporting", Qt::QueuedConnection); + QMetaObject::invokeMethod(exporter_, "StartExporting", Qt::QueuedConnection); SetUIElementsEnabled(false); } 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); QDialog::closeEvent(e); @@ -464,7 +489,9 @@ QMatrix4x4 ExportDialog::GenerateMatrix(ExportVideoTab::ScalingMethod method, in void ExportDialog::SetUIElementsEnabled(bool enabled) { preferences_area_->setEnabled(enabled); - //buttons_->setEnabled(false); + buttons_->setEnabled(enabled); + + export_cancel_btn_->setEnabled(!enabled); } void ExportDialog::UpdateViewerDimensions() @@ -481,9 +508,7 @@ void ExportDialog::UpdateViewerDimensions() void ExportDialog::ExporterIsDone() { - Exporter* exporter = static_cast(sender()); - - if (exporter->GetExportStatus()) { + if (exporter_->GetExportStatus()) { QMessageBox::information(this, tr("Export Status"), tr("Export completed successfully."), @@ -491,11 +516,25 @@ void ExportDialog::ExporterIsDone() QDialog::accept(); } else { - QMessageBox::critical(this, - tr("Export Status"), - tr("Export failed: %1").arg(exporter->GetExportError()), - QMessageBox::Ok); + if (!cancelled_) { + QMessageBox::critical(this, + tr("Export Status"), + tr("Export failed: %1").arg(exporter_->GetExportError()), + QMessageBox::Ok); + } SetUIElementsEnabled(true); } + + exporter_->deleteLater(); + exporter_ = nullptr; + cancelled_ = false; +} + +void ExportDialog::CancelExport() +{ + if (exporter_) { + cancelled_ = true; + exporter_->Cancel(); + } } diff --git a/app/dialog/export/export.h b/app/dialog/export/export.h index c0ff94903..5f55012c7 100644 --- a/app/dialog/export/export.h +++ b/app/dialog/export/export.h @@ -11,6 +11,7 @@ #include "exportcodec.h" #include "exportformat.h" #include "exportvideotab.h" +#include "render/backend/exporter.h" #include "widget/viewer/viewer.h" class ExportDialog : public QDialog @@ -48,6 +49,8 @@ private: QLineEdit* filename_edit_; QComboBox* format_combobox_; + Exporter* exporter_; + ExportVideoTab* video_tab_; ExportAudioTab* audio_tab_; @@ -59,6 +62,9 @@ private: QWidget* preferences_area_; QDialogButtonBox* buttons_; + QPushButton* export_cancel_btn_; + + bool cancelled_; enum Format { kFormatDNxHD, @@ -102,6 +108,8 @@ private slots: void ExporterIsDone(); + void CancelExport(); + }; #endif // EXPORTDIALOG_H diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index 139a383ae..8969978c2 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -50,6 +50,24 @@ const QString &Exporter::GetExportError() const 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() { // Default to error state until ExportEnd is called @@ -101,6 +119,7 @@ void Exporter::ExportSucceeded() if (video_backend_) { video_backend_->deleteLater(); + video_backend_ = nullptr; } export_status_ = true; @@ -112,9 +131,10 @@ void Exporter::ExportSucceeded() Qt::QueuedConnection); } -void Exporter::ExportFailed() +void Exporter::ExportStopped() { emit ExportEnded(); + encoder_->deleteLater(); } void Exporter::EncodeFrame() @@ -195,6 +215,7 @@ void Exporter::AudioRendered() // We don't need the audio backend anymore audio_backend_->deleteLater(); + audio_backend_ = nullptr; } void Exporter::AudioEncodeComplete() @@ -225,14 +246,14 @@ void Exporter::EncoderOpenedSuccessfully() void Exporter::EncoderOpenFailed() { - SetExportMessage("Failed to open encoder"); - ExportFailed(); + SetExportMessage(tr("Failed to open encoder")); + ExportStopped(); } void Exporter::EncoderClosed() { emit ProgressChanged(100); - emit ExportEnded(); + ExportStopped(); } void Exporter::VideoHashesComplete() diff --git a/app/render/backend/exporter.h b/app/render/backend/exporter.h index f3e2c53c0..f372a4c14 100644 --- a/app/render/backend/exporter.h +++ b/app/render/backend/exporter.h @@ -26,6 +26,8 @@ public: bool GetExportStatus() const; const QString& GetExportError() const; + void Cancel(); + public slots: void StartExporting(); @@ -58,7 +60,7 @@ protected: private: void ExportSucceeded(); - void ExportFailed(); + void ExportStopped(); void EncodeFrame(); diff --git a/app/render/backend/opengl/openglbackend.cpp b/app/render/backend/opengl/openglbackend.cpp index f286023de..cfa200496 100644 --- a/app/render/backend/opengl/openglbackend.cpp +++ b/app/render/backend/opengl/openglbackend.cpp @@ -31,8 +31,10 @@ bool OpenGLBackend::InitInternal() if (!proxy_->Init()) { proxy_thread->quit(); proxy_thread->wait(); - delete proxy_thread; - delete proxy_; + proxy_thread->deleteLater(); + + proxy_->deleteLater(); + proxy_ = nullptr; return false; } @@ -54,7 +56,11 @@ bool OpenGLBackend::InitInternal() void OpenGLBackend::CloseInternal() { if (proxy_) { - delete proxy_; + proxy_->thread()->quit(); + proxy_->thread()->wait(); + proxy_->thread()->deleteLater(); + + proxy_->deleteLater(); proxy_ = nullptr; } diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index b0244dd91..05c978745 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -21,6 +21,8 @@ OpenGLProxy::OpenGLProxy(QObject *parent) : OpenGLProxy::~OpenGLProxy() { + Close(); + surface_.destroy(); } @@ -178,6 +180,7 @@ void OpenGLProxy::Close() buffer_.Destroy(); functions_ = nullptr; delete ctx_; + ctx_ = nullptr; } void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable *output_params) diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp index 9efe7937f..8d4cf1b5e 100644 --- a/app/render/backend/renderbackend.cpp +++ b/app/render/backend/renderbackend.cpp @@ -326,7 +326,10 @@ void RenderBackend::CancelQueue() if (processor_busy_state_.at(i)) busy++; } - qDebug() << this << "is waiting for" << busy << "busy workers"; + + if (busy) { + qDebug() << this << "is waiting for" << busy << "busy workers"; + } cancel_dialog_->RunIfWorkersAreBusy(); }