exporter: add ability to cancel exports (including a confirm to cancel if the
user closes the dialog mid-export)
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user