task: massively simplified task system
Made various changes and fixes to the task system: - Tasks are built around QtConcurrent rather than QThread. Reduces code complexity significantly. - Task error reporting is now streamlined in both TaskManager and TaskDialog. - Moved ProjectImport/Save/LoadManager to the app/task folder
This commit is contained in:
@@ -26,7 +26,6 @@ CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) :
|
|||||||
CLIProgressDialog(task->GetTitle(), parent)
|
CLIProgressDialog(task->GetTitle(), parent)
|
||||||
{
|
{
|
||||||
// FIXME: Still developing this, don't try to use
|
// FIXME: Still developing this, don't try to use
|
||||||
task->Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
+45
-17
@@ -45,13 +45,14 @@
|
|||||||
#include "panel/panelmanager.h"
|
#include "panel/panelmanager.h"
|
||||||
#include "panel/project/project.h"
|
#include "panel/project/project.h"
|
||||||
#include "panel/viewer/viewer.h"
|
#include "panel/viewer/viewer.h"
|
||||||
#include "project/projectimportmanager.h"
|
|
||||||
#include "project/projectloadmanager.h"
|
|
||||||
#include "project/projectsavemanager.h"
|
|
||||||
#include "render/backend/opengl/opengltexturecache.h"
|
#include "render/backend/opengl/opengltexturecache.h"
|
||||||
#include "render/colormanager.h"
|
#include "render/colormanager.h"
|
||||||
#include "render/diskmanager.h"
|
#include "render/diskmanager.h"
|
||||||
#include "render/pixelformat.h"
|
#include "render/pixelformat.h"
|
||||||
|
#include "task/cache/cache.h"
|
||||||
|
#include "task/project/import/import.h"
|
||||||
|
#include "task/project/load/load.h"
|
||||||
|
#include "task/project/save/save.h"
|
||||||
#include "task/taskmanager.h"
|
#include "task/taskmanager.h"
|
||||||
#include "ui/style/style.h"
|
#include "ui/style/style.h"
|
||||||
#include "undo/undostack.h"
|
#include "undo/undostack.h"
|
||||||
@@ -239,7 +240,7 @@ void Core::ImportFiles(const QStringList &urls, ProjectViewModel* model, Folder*
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectImportManager* pim = new ProjectImportManager(model, parent, urls);
|
ProjectImportTask* pim = new ProjectImportTask(model, parent, urls);
|
||||||
|
|
||||||
if (!pim->GetFileCount()) {
|
if (!pim->GetFileCount()) {
|
||||||
// No files to import
|
// No files to import
|
||||||
@@ -247,9 +248,10 @@ void Core::ImportFiles(const QStringList &urls, ProjectViewModel* model, Folder*
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(pim, &ProjectImportManager::ImportComplete, this, &Core::ImportTaskComplete, Qt::BlockingQueuedConnection);
|
|
||||||
|
|
||||||
TaskDialog* task_dialog = new TaskDialog(pim, tr("Importing..."), main_window());
|
TaskDialog* task_dialog = new TaskDialog(pim, tr("Importing..."), main_window());
|
||||||
|
|
||||||
|
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::ImportTaskComplete);
|
||||||
|
|
||||||
task_dialog->open();
|
task_dialog->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,8 +484,19 @@ void Core::AddOpenProject(ProjectPtr p)
|
|||||||
emit ProjectOpened(p.get());
|
emit ProjectOpened(p.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::ImportTaskComplete(QUndoCommand *command)
|
void Core::AddOpenProjectFromTask(Task *task)
|
||||||
{
|
{
|
||||||
|
QList<ProjectPtr> projects = static_cast<ProjectLoadTask*>(task)->GetLoadedProjects();
|
||||||
|
|
||||||
|
foreach (ProjectPtr p, projects) {
|
||||||
|
AddOpenProject(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::ImportTaskComplete(Task* task)
|
||||||
|
{
|
||||||
|
QUndoCommand *command = static_cast<ProjectImportTask*>(task)->GetCommand();
|
||||||
|
|
||||||
undo_stack_.pushIfHasChildren(command);
|
undo_stack_.pushIfHasChildren(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,11 +614,11 @@ void Core::StartGUI(bool full_screen)
|
|||||||
void Core::SaveProjectInternal(ProjectPtr project)
|
void Core::SaveProjectInternal(ProjectPtr project)
|
||||||
{
|
{
|
||||||
// Create save manager
|
// Create save manager
|
||||||
ProjectSaveManager* psm = new ProjectSaveManager(project);
|
ProjectSaveTask* psm = new ProjectSaveTask(project);
|
||||||
|
TaskDialog* task_dialog = new TaskDialog(psm, tr("Save Project"), main_window_);
|
||||||
|
|
||||||
connect(psm, &ProjectSaveManager::ProjectSaveSucceeded, this, &Core::ProjectSaveSucceeded);
|
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::ProjectSaveSucceeded);
|
||||||
|
|
||||||
TaskDialog* task_dialog = new TaskDialog(psm, tr("Save Project"), main_window());
|
|
||||||
task_dialog->open();
|
task_dialog->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,8 +632,10 @@ void Core::SaveAutorecovery()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::ProjectSaveSucceeded(ProjectPtr p)
|
void Core::ProjectSaveSucceeded(Task* task)
|
||||||
{
|
{
|
||||||
|
ProjectPtr p = static_cast<ProjectSaveTask*>(task)->GetProject();
|
||||||
|
|
||||||
PushRecentlyOpenedProject(p->filename());
|
PushRecentlyOpenedProject(p->filename());
|
||||||
|
|
||||||
p->set_modified(false);
|
p->set_modified(false);
|
||||||
@@ -885,20 +900,19 @@ void Core::OpenProjectInternal(const QString &filename)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectLoadManager* plm = new ProjectLoadManager(filename);
|
ProjectLoadTask* plm = new ProjectLoadTask(filename);
|
||||||
|
|
||||||
if (gui_active_) {
|
if (gui_active_) {
|
||||||
|
|
||||||
// We use a blocking queued connection here because we want to ensure we have this project instance before the
|
|
||||||
// ProjectLoadManager is destroyed
|
|
||||||
connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject, Qt::BlockingQueuedConnection);
|
|
||||||
|
|
||||||
TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window());
|
TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window());
|
||||||
|
|
||||||
|
connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::AddOpenProjectFromTask);
|
||||||
|
|
||||||
task_dialog->open();
|
task_dialog->open();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject);
|
//connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject);
|
||||||
|
|
||||||
CLITaskDialog task_dialog(plm);
|
CLITaskDialog task_dialog(plm);
|
||||||
|
|
||||||
@@ -1100,6 +1114,20 @@ bool Core::CloseAllProjects(bool auto_open_new)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::CacheActiveSequence(bool in_out_only)
|
||||||
|
{
|
||||||
|
TimeBasedPanel* p = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
||||||
|
|
||||||
|
if (p && p->GetConnectedViewer()) {
|
||||||
|
// FIXME: Hardcoded divider...
|
||||||
|
// FIXME: Consider preventing caching the footage viewer
|
||||||
|
CacheTask* task = new CacheTask(p->GetConnectedViewer(), 2, in_out_only);
|
||||||
|
|
||||||
|
TaskDialog* dialog = new TaskDialog(task, tr("Caching Sequence"), main_window_);
|
||||||
|
dialog->open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Core::CloseAllProjects()
|
bool Core::CloseAllProjects()
|
||||||
{
|
{
|
||||||
return CloseAllProjects(true);
|
return CloseAllProjects(true);
|
||||||
|
|||||||
+9
-2
@@ -229,6 +229,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool CloseAllProjects(bool auto_open_new);
|
bool CloseAllProjects(bool auto_open_new);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runs a modal cache task on the currently active sequence
|
||||||
|
*/
|
||||||
|
void CacheActiveSequence(bool in_out_only);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Starts an open file dialog to load a project from file
|
* @brief Starts an open file dialog to load a project from file
|
||||||
@@ -477,14 +482,16 @@ private:
|
|||||||
private slots:
|
private slots:
|
||||||
void SaveAutorecovery();
|
void SaveAutorecovery();
|
||||||
|
|
||||||
void ProjectSaveSucceeded(OLIVE_NAMESPACE::ProjectPtr p);
|
void ProjectSaveSucceeded(Task *task);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds a project to the "open projects" list
|
* @brief Adds a project to the "open projects" list
|
||||||
*/
|
*/
|
||||||
void AddOpenProject(OLIVE_NAMESPACE::ProjectPtr p);
|
void AddOpenProject(OLIVE_NAMESPACE::ProjectPtr p);
|
||||||
|
|
||||||
void ImportTaskComplete(QUndoCommand* command);
|
void AddOpenProjectFromTask(Task* task);
|
||||||
|
|
||||||
|
void ImportTaskComplete(Task *task);
|
||||||
|
|
||||||
bool ConfirmImageSequence(const QString &filename);
|
bool ConfirmImageSequence(const QString &filename);
|
||||||
|
|
||||||
|
|||||||
+18
-186
@@ -31,21 +31,18 @@
|
|||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "dialog/task/task.h"
|
||||||
#include "project/item/sequence/sequence.h"
|
#include "project/item/sequence/sequence.h"
|
||||||
#include "project/project.h"
|
#include "project/project.h"
|
||||||
#include "render/backend/exportparams.h"
|
|
||||||
#include "render/pixelformat.h"
|
#include "render/pixelformat.h"
|
||||||
#include "ui/icons/icons.h"
|
#include "ui/icons/icons.h"
|
||||||
#include "window/mainwindow/mainwindow.h"
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -53,11 +50,7 @@ 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);
|
preferences_layout->setMargin(0);
|
||||||
|
|
||||||
@@ -142,44 +135,17 @@ 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);
|
||||||
|
|
||||||
QHBoxLayout* progress_bar_layout = new QHBoxLayout();
|
row++;
|
||||||
progress_bar_layout->setMargin(0);
|
|
||||||
outer_preferences_layout->addLayout(progress_bar_layout);
|
|
||||||
|
|
||||||
progress_bar_ = new QProgressBar();
|
|
||||||
progress_bar_->setEnabled(false);
|
|
||||||
progress_bar_->setValue(0);
|
|
||||||
progress_bar_layout->addWidget(progress_bar_);
|
|
||||||
|
|
||||||
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_);
|
|
||||||
|
|
||||||
QHBoxLayout* time_label_layout = new QHBoxLayout();
|
|
||||||
time_label_layout->setMargin(0);
|
|
||||||
outer_preferences_layout->addLayout(time_label_layout);
|
|
||||||
|
|
||||||
elapsed_label_ = new QLabel();
|
|
||||||
elapsed_label_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
||||||
time_label_layout->addWidget(elapsed_label_);
|
|
||||||
|
|
||||||
remaining_label_ = new QLabel();
|
|
||||||
remaining_label_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
||||||
time_label_layout->addWidget(remaining_label_);
|
|
||||||
|
|
||||||
UpdateTimeLabels();
|
|
||||||
|
|
||||||
buttons_ = new QDialogButtonBox();
|
buttons_ = new QDialogButtonBox();
|
||||||
buttons_->setCenterButtons(true);
|
buttons_->setCenterButtons(true);
|
||||||
buttons_->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
|
buttons_->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
|
||||||
buttons_->addButton(QDialogButtonBox::Cancel);
|
buttons_->addButton(QDialogButtonBox::Cancel);
|
||||||
connect(buttons_, SIGNAL(accepted()), this, SLOT(accept()));
|
connect(buttons_, &QDialogButtonBox::accepted, this, &ExportDialog::accept);
|
||||||
connect(buttons_, SIGNAL(rejected()), this, SLOT(reject()));
|
connect(buttons_, &QDialogButtonBox::rejected, this, &ExportDialog::reject);
|
||||||
outer_preferences_layout->addWidget(buttons_);
|
preferences_layout->addWidget(buttons_, row, 0, 1, 4);
|
||||||
|
|
||||||
splitter->addWidget(outer_preferences_area);
|
splitter->addWidget(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);
|
||||||
@@ -190,7 +156,6 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
splitter->addWidget(preview_area);
|
splitter->addWidget(preview_area);
|
||||||
|
|
||||||
// Set default filename
|
// Set default filename
|
||||||
// FIXME: Use Sequence name and project filename to construct this
|
|
||||||
SetDefaultFilename();
|
SetDefaultFilename();
|
||||||
|
|
||||||
// Set up available export formats
|
// Set up available export formats
|
||||||
@@ -227,9 +192,6 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
|||||||
preview_viewer_->ConnectViewerNode(viewer_node_);
|
preview_viewer_->ConnectViewerNode(viewer_node_);
|
||||||
preview_viewer_->SetColorMenuEnabled(false);
|
preview_viewer_->SetColorMenuEnabled(false);
|
||||||
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
|
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
|
||||||
|
|
||||||
progress_timer_.setInterval(1000);
|
|
||||||
connect(&progress_timer_, &QTimer::timeout, this, &ExportDialog::UpdateTimeLabels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::accept()
|
void ExportDialog::accept()
|
||||||
@@ -314,46 +276,13 @@ void ExportDialog::accept()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up export parameters
|
ExportTask* task = new ExportTask(viewer_node_, color_manager_, GenerateParams());
|
||||||
exporter_ = new Exporter(viewer_node_, color_manager_, GenerateParams());
|
TaskDialog* td = new TaskDialog(task, tr("Export"), this);
|
||||||
|
td->open();
|
||||||
connect(exporter_, &Exporter::ExportEnded, this, &ExportDialog::ExporterIsDone);
|
|
||||||
connect(exporter_, &Exporter::ProgressChanged, this, &ExportDialog::ProgressUpdated);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
Core::instance()->main_window()->SetTaskbarButtonState(TBPF_NORMAL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
export_start_ = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
|
|
||||||
flt_progress_ = 0;
|
|
||||||
|
|
||||||
progress_timer_.start();
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(exporter_, "StartExporting", Qt::QueuedConnection);
|
|
||||||
|
|
||||||
SetUIElementsEnabled(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::closeEvent(QCloseEvent *e)
|
void ExportDialog::closeEvent(QCloseEvent *e)
|
||||||
{
|
{
|
||||||
if (exporter_) {
|
|
||||||
QMessageBox b(this);
|
|
||||||
b.setIcon(QMessageBox::Question);
|
|
||||||
b.setWindowModality(Qt::WindowModal);
|
|
||||||
b.setWindowTitle(tr("Still Exporting"));
|
|
||||||
b.setText(tr("This sequence is still being exported. Do you wish to cancel it?"));
|
|
||||||
b.addButton(QMessageBox::Yes);
|
|
||||||
b.addButton(QMessageBox::No);
|
|
||||||
|
|
||||||
if (b.exec() == QMessageBox::Yes) {
|
|
||||||
CancelExport();
|
|
||||||
} else {
|
|
||||||
e->ignore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preview_viewer_->ConnectViewerNode(nullptr);
|
preview_viewer_->ConnectViewerNode(nullptr);
|
||||||
|
|
||||||
QDialog::closeEvent(e);
|
QDialog::closeEvent(e);
|
||||||
@@ -548,17 +477,6 @@ void ExportDialog::SetDefaultFilename()
|
|||||||
filename_edit_->setText(file_location);
|
filename_edit_->setText(file_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::SetUIElementsEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
preferences_area_->setEnabled(enabled);
|
|
||||||
buttons_->setEnabled(enabled);
|
|
||||||
|
|
||||||
progress_bar_->setEnabled(!enabled);
|
|
||||||
export_cancel_btn_->setEnabled(!enabled);
|
|
||||||
elapsed_label_->setEnabled(!enabled);
|
|
||||||
remaining_label_->setEnabled(!enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ExportDialog::AlignEvenNumber(double d)
|
int ExportDialog::AlignEvenNumber(double d)
|
||||||
{
|
{
|
||||||
return qCeil(d * 0.5) * 2;
|
return qCeil(d * 0.5) * 2;
|
||||||
@@ -607,40 +525,6 @@ ExportParams ExportDialog::GenerateParams() const
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::UpdateTimeLabels()
|
|
||||||
{
|
|
||||||
qint64 elapsed, remaining;
|
|
||||||
|
|
||||||
if (exporter_) {
|
|
||||||
elapsed = QDateTime::currentMSecsSinceEpoch() - export_start_;
|
|
||||||
|
|
||||||
if (flt_progress_ > 0) {
|
|
||||||
remaining = qRound64((1.0 - flt_progress_) * static_cast<double>(elapsed) / flt_progress_);
|
|
||||||
} else {
|
|
||||||
remaining = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
elapsed = 0;
|
|
||||||
remaining = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsed_label_->setText(tr("Elapsed: %1").arg(TimeToString(elapsed)));
|
|
||||||
remaining_label_->setText(tr("Remaining: %1").arg(TimeToString(remaining)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExportDialog::ProgressUpdated(double p)
|
|
||||||
{
|
|
||||||
int i_prog = qRound(100.0 * p);
|
|
||||||
|
|
||||||
progress_bar_->setValue(i_prog);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
UpdateTaskbarProgress(i_prog);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
flt_progress_ = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ExportDialog::TimeToString(int64_t ms)
|
QString ExportDialog::TimeToString(int64_t ms)
|
||||||
{
|
{
|
||||||
int64_t total_seconds = ms / 1000;
|
int64_t total_seconds = ms / 1000;
|
||||||
@@ -659,66 +543,14 @@ void ExportDialog::UpdateViewerDimensions()
|
|||||||
preview_viewer_->SetOverrideSize(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
preview_viewer_->SetOverrideSize(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||||
static_cast<int>(video_tab_->height_slider()->GetValue()));
|
static_cast<int>(video_tab_->height_slider()->GetValue()));
|
||||||
|
|
||||||
preview_viewer_->SetMatrix(Exporter::GenerateMatrix(static_cast<ExportParams::VideoScalingMethod>(video_tab_->scaling_method_combobox()->currentData().toInt()),
|
QMatrix4x4 transform =
|
||||||
viewer_node_->video_params().width(),
|
ExportParams::GenerateMatrix(static_cast<ExportParams::VideoScalingMethod>(video_tab_->scaling_method_combobox()->currentData().toInt()),
|
||||||
viewer_node_->video_params().height(),
|
viewer_node_->video_params().width(),
|
||||||
static_cast<int>(video_tab_->width_slider()->GetValue()),
|
viewer_node_->video_params().height(),
|
||||||
static_cast<int>(video_tab_->height_slider()->GetValue())));
|
static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||||
|
static_cast<int>(video_tab_->height_slider()->GetValue()));
|
||||||
|
|
||||||
|
preview_viewer_->SetMatrix(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::ExporterIsDone()
|
|
||||||
{
|
|
||||||
progress_timer_.stop();
|
|
||||||
|
|
||||||
if (exporter_->GetExportStatus()) {
|
|
||||||
QMessageBox b(this);
|
|
||||||
b.setIcon(QMessageBox::Information);
|
|
||||||
b.setWindowModality(Qt::WindowModal);
|
|
||||||
b.setWindowTitle(tr("Export Status"));
|
|
||||||
b.setText(tr("Export completed successfully."));
|
|
||||||
b.addButton(QMessageBox::Ok);
|
|
||||||
b.exec();
|
|
||||||
|
|
||||||
QDialog::accept();
|
|
||||||
} else {
|
|
||||||
if (!cancelled_) {
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
Core::instance()->main_window()->SetTaskbarButtonState(TBPF_ERROR);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QMessageBox b(this);
|
|
||||||
b.setIcon(QMessageBox::Critical);
|
|
||||||
b.setWindowModality(Qt::WindowModal);
|
|
||||||
b.setWindowTitle(tr("Export Status"));
|
|
||||||
b.setText(tr("Export failed: %1").arg(exporter_->GetExportError()));
|
|
||||||
b.addButton(QMessageBox::Ok);
|
|
||||||
b.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
SetUIElementsEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
exporter_ = nullptr;
|
|
||||||
cancelled_ = false;
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
Core::instance()->main_window()->SetTaskbarButtonState(TBPF_NOPROGRESS);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExportDialog::CancelExport()
|
|
||||||
{
|
|
||||||
if (exporter_) {
|
|
||||||
cancelled_ = true;
|
|
||||||
exporter_->Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
void ExportDialog::UpdateTaskbarProgress(int progress)
|
|
||||||
{
|
|
||||||
Core::instance()->main_window()->SetTaskbarButtonProgress(progress, 100);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -31,7 +31,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 "task/export/export.h"
|
||||||
#include "widget/viewer/viewer.h"
|
#include "widget/viewer/viewer.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
@@ -53,8 +53,6 @@ private:
|
|||||||
void LoadPresets();
|
void LoadPresets();
|
||||||
void SetDefaultFilename();
|
void SetDefaultFilename();
|
||||||
|
|
||||||
void SetUIElementsEnabled(bool enabled);
|
|
||||||
|
|
||||||
static int AlignEvenNumber(double d);
|
static int AlignEvenNumber(double d);
|
||||||
|
|
||||||
ExportParams GenerateParams() const;
|
ExportParams GenerateParams() const;
|
||||||
@@ -75,8 +73,6 @@ 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_;
|
||||||
|
|
||||||
@@ -84,19 +80,8 @@ private:
|
|||||||
|
|
||||||
ColorManager* color_manager_;
|
ColorManager* color_manager_;
|
||||||
|
|
||||||
QProgressBar* progress_bar_;
|
|
||||||
|
|
||||||
QTimer progress_timer_;
|
|
||||||
QLabel* elapsed_label_;
|
|
||||||
QLabel* remaining_label_;
|
|
||||||
qint64 export_start_;
|
|
||||||
double flt_progress_;
|
|
||||||
|
|
||||||
QWidget* preferences_area_;
|
QWidget* preferences_area_;
|
||||||
QDialogButtonBox* buttons_;
|
QDialogButtonBox* buttons_;
|
||||||
QPushButton* export_cancel_btn_;
|
|
||||||
|
|
||||||
bool cancelled_;
|
|
||||||
|
|
||||||
enum Format {
|
enum Format {
|
||||||
kFormatDNxHD,
|
kFormatDNxHD,
|
||||||
@@ -138,18 +123,6 @@ private slots:
|
|||||||
|
|
||||||
void UpdateViewerDimensions();
|
void UpdateViewerDimensions();
|
||||||
|
|
||||||
void ExporterIsDone();
|
|
||||||
|
|
||||||
void CancelExport();
|
|
||||||
|
|
||||||
void UpdateTimeLabels();
|
|
||||||
|
|
||||||
void ProgressUpdated(double p);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
|
||||||
void UpdateTaskbarProgress(int progress);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
#include "render/backend/exportparams.h"
|
|
||||||
#include "widget/slider/integerslider.h"
|
#include "widget/slider/integerslider.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "exportadvancedvideodialog.h"
|
#include "exportadvancedvideodialog.h"
|
||||||
#include "render/backend/exportparams.h"
|
|
||||||
#include "render/colormanager.h"
|
#include "render/colormanager.h"
|
||||||
|
#include "task/export/exportparams.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,12 @@
|
|||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "window/mainwindow/mainwindow.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
ProgressDialog::ProgressDialog(const QString& message, const QString& title, QWidget *parent) :
|
ProgressDialog::ProgressDialog(const QString& message, const QString& title, QWidget *parent) :
|
||||||
@@ -56,9 +59,46 @@ ProgressDialog::ProgressDialog(const QString& message, const QString& title, QWi
|
|||||||
cancel_layout->addStretch();
|
cancel_layout->addStretch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProgressDialog::showEvent(QShowEvent *e)
|
||||||
|
{
|
||||||
|
QDialog::showEvent(e);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
Core::instance()->main_window()->SetTaskbarButtonState(TBPF_NORMAL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressDialog::closeEvent(QCloseEvent *e)
|
||||||
|
{
|
||||||
|
QDialog::closeEvent(e);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
Core::instance()->main_window()->SetTaskbarButtonState(TBPF_NOPROGRESS);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void ProgressDialog::SetProgress(int value)
|
void ProgressDialog::SetProgress(int value)
|
||||||
{
|
{
|
||||||
bar_->setValue(value);
|
bar_->setValue(value);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
Core::instance()->main_window()->SetTaskbarButtonProgress(value, 100);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgressDialog::ShowErrorMessage(const QString &title, const QString &message)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
Core::instance()->main_window()->SetTaskbarButtonState(TBPF_ERROR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QMessageBox b(this);
|
||||||
|
b.setIcon(QMessageBox::Critical);
|
||||||
|
b.setWindowModality(Qt::WindowModal);
|
||||||
|
b.setWindowTitle(title);
|
||||||
|
b.setText(message);
|
||||||
|
b.addButton(QMessageBox::Ok);
|
||||||
|
b.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -34,12 +34,20 @@ class ProgressDialog : public QDialog
|
|||||||
public:
|
public:
|
||||||
ProgressDialog(const QString &message, const QString &title, QWidget* parent = nullptr);
|
ProgressDialog(const QString &message, const QString &title, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void showEvent(QShowEvent* e) override;
|
||||||
|
|
||||||
|
virtual void closeEvent(QCloseEvent *) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetProgress(int value);
|
void SetProgress(int value);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void Cancelled();
|
void Cancelled();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ShowErrorMessage(const QString& title, const QString& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QProgressBar* bar_;
|
QProgressBar* bar_;
|
||||||
|
|
||||||
|
|||||||
+32
-39
@@ -20,76 +20,69 @@
|
|||||||
|
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
TaskDialog::TaskDialog(Task* task, const QString& title, QWidget *parent) :
|
TaskDialog::TaskDialog(Task* task, const QString& title, QWidget *parent) :
|
||||||
ProgressDialog(task->GetTitle(), title, parent),
|
ProgressDialog(task->GetTitle(), title, parent),
|
||||||
task_(task),
|
task_(task),
|
||||||
task_failed_(false)
|
destroy_on_close_(true)
|
||||||
{
|
{
|
||||||
thread_ = new QThread();
|
// Clear task when this dialog is destroyed
|
||||||
|
task_->setParent(this);
|
||||||
|
|
||||||
// Connect the save manager progress signal to the progress bar update on the dialog
|
// Connect the save manager progress signal to the progress bar update on the dialog
|
||||||
connect(task_, &Task::ProgressChanged, this, &TaskDialog::SetProgress, Qt::QueuedConnection);
|
connect(task_, &Task::ProgressChanged, this, &TaskDialog::SetProgress, Qt::QueuedConnection);
|
||||||
|
|
||||||
// Connect error reporting
|
// Connect cancel signal (must be a direct connection or it'll be queued after the task has
|
||||||
connect(task_, &Task::Failed, this, &TaskDialog::TaskFailed, Qt::QueuedConnection);
|
// already finished)
|
||||||
|
|
||||||
// Connect cancel signal (must be a direct connection or it'll be queued after the task has already finished)
|
|
||||||
connect(this, &TaskDialog::Cancelled, task_, &Task::Cancel, Qt::DirectConnection);
|
connect(this, &TaskDialog::Cancelled, task_, &Task::Cancel, Qt::DirectConnection);
|
||||||
|
|
||||||
// Connect cleanup functions (ensure everything new'd in this function is deleteLater'd)
|
|
||||||
connect(task_, &Task::Finished, this, &TaskDialog::close, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
// When task is finished, signal thread to quit
|
|
||||||
connect(task_, &Task::Finished, thread_, &QThread::quit, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
// When thread has quit, delete both task and thread
|
|
||||||
connect(thread_, &QThread::finished, task_, &Task::deleteLater, Qt::QueuedConnection);
|
|
||||||
connect(thread_, &QThread::finished, thread_, &QThread::deleteLater, Qt::QueuedConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskDialog::showEvent(QShowEvent *e)
|
void TaskDialog::showEvent(QShowEvent *e)
|
||||||
{
|
{
|
||||||
QDialog::showEvent(e);
|
ProgressDialog::showEvent(e);
|
||||||
|
|
||||||
// Create a separate thread to run this task in
|
// Create watcher for when the task finishes
|
||||||
thread_->start();
|
QFutureWatcher<bool>* task_watcher = new QFutureWatcher<bool>();
|
||||||
|
|
||||||
// Move the task to this thread
|
// Listen for when the task finishes
|
||||||
task_->moveToThread(thread_);
|
connect(task_watcher, &QFutureWatcher<bool>::finished,
|
||||||
|
this, &TaskDialog::TaskFinished, Qt::QueuedConnection);
|
||||||
|
|
||||||
// Start the task
|
// Run task in another thread with QtConcurrent
|
||||||
QMetaObject::invokeMethod(task_, "Start", Qt::QueuedConnection);
|
task_watcher->setFuture(QtConcurrent::run(task_, &Task::Run));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskDialog::closeEvent(QCloseEvent *e)
|
void TaskDialog::closeEvent(QCloseEvent *e)
|
||||||
{
|
{
|
||||||
// Show error if the task failed
|
|
||||||
if (!task_->IsCancelled() && task_failed_) {
|
|
||||||
QMessageBox::critical(this,
|
|
||||||
tr("Error"),
|
|
||||||
task_error_,
|
|
||||||
QMessageBox::Ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel task if it is running
|
// Cancel task if it is running
|
||||||
task_->Cancel();
|
task_->Cancel();
|
||||||
|
|
||||||
// Standard close function
|
// Standard close function
|
||||||
QDialog::closeEvent(e);
|
ProgressDialog::closeEvent(e);
|
||||||
|
|
||||||
// Clean up this dialog (FIXME: Is this necessary?)
|
// Clean up this task and dialog
|
||||||
deleteLater();
|
if (destroy_on_close_) {
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskDialog::TaskFailed(const QString &s)
|
void TaskDialog::TaskFinished()
|
||||||
{
|
{
|
||||||
task_failed_ = true;
|
QFutureWatcher<bool>* task_watcher = static_cast<QFutureWatcher<bool>*>(sender());
|
||||||
task_error_ = s;
|
|
||||||
|
if (task_watcher->result()) {
|
||||||
|
emit TaskSucceeded(task_);
|
||||||
|
} else {
|
||||||
|
ShowErrorMessage(tr("Task Failed"), task_->GetError());
|
||||||
|
emit TaskFailed(task_);
|
||||||
|
}
|
||||||
|
|
||||||
|
task_watcher->deleteLater();
|
||||||
|
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
+32
-5
@@ -30,23 +30,50 @@ class TaskDialog : public ProgressDialog
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief TaskDialog Constructor
|
||||||
|
*
|
||||||
|
* Creates a TaskDialog. The TaskDialog takes ownership of the Task and will destroy it on close.
|
||||||
|
* Connect to the Task::Succeeded() if you want to retrieve information from the task before it
|
||||||
|
* gets destroyed.
|
||||||
|
*/
|
||||||
TaskDialog(Task *task, const QString &title, QWidget* parent = nullptr);
|
TaskDialog(Task *task, const QString &title, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set whether TaskDialog should destroy itself (and the task) when it's closed
|
||||||
|
*
|
||||||
|
* This is TRUE by default.
|
||||||
|
*/
|
||||||
|
void SetDestroyOnClose(bool e)
|
||||||
|
{
|
||||||
|
destroy_on_close_ = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns this dialog's task
|
||||||
|
*/
|
||||||
|
Task* GetTask() const
|
||||||
|
{
|
||||||
|
return task_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void showEvent(QShowEvent* e) override;
|
virtual void showEvent(QShowEvent* e) override;
|
||||||
|
|
||||||
virtual void closeEvent(QCloseEvent* e) override;
|
virtual void closeEvent(QCloseEvent* e) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void TaskSucceeded(Task* task);
|
||||||
|
|
||||||
|
void TaskFailed(Task* task);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Task* task_;
|
Task* task_;
|
||||||
|
|
||||||
QThread* thread_;
|
bool destroy_on_close_;
|
||||||
|
|
||||||
bool task_failed_;
|
|
||||||
QString task_error_;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void TaskFailed(const QString& s);
|
void TaskFinished();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ TaskManagerPanel::TaskManagerPanel(QWidget* parent) :
|
|||||||
|
|
||||||
// Connect task view to the task manager
|
// Connect task view to the task manager
|
||||||
connect(TaskManager::instance(), &TaskManager::TaskAdded, view_, &TaskView::AddTask);
|
connect(TaskManager::instance(), &TaskManager::TaskAdded, view_, &TaskView::AddTask);
|
||||||
|
connect(TaskManager::instance(), &TaskManager::TaskRemoved, view_, &TaskView::RemoveTask);
|
||||||
|
connect(TaskManager::instance(), &TaskManager::TaskFailed, view_, &TaskView::TaskFailed);
|
||||||
|
|
||||||
// Set strings
|
// Set strings
|
||||||
Retranslate();
|
Retranslate();
|
||||||
|
|||||||
@@ -20,12 +20,6 @@ set(OLIVE_SOURCES
|
|||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
project/project.h
|
project/project.h
|
||||||
project/project.cpp
|
project/project.cpp
|
||||||
project/projectimportmanager.h
|
|
||||||
project/projectimportmanager.cpp
|
|
||||||
project/projectloadmanager.h
|
|
||||||
project/projectloadmanager.cpp
|
|
||||||
project/projectsavemanager.h
|
|
||||||
project/projectsavemanager.cpp
|
|
||||||
project/projectviewmodel.h
|
project/projectviewmodel.h
|
||||||
project/projectviewmodel.cpp
|
project/projectviewmodel.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ add_subdirectory(opengl)
|
|||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
|
|
||||||
render/backend/exporter.h
|
|
||||||
render/backend/exporter.cpp
|
|
||||||
render/backend/exportparams.h
|
|
||||||
render/backend/exportparams.cpp
|
|
||||||
|
|
||||||
render/backend/renderbackend.h
|
render/backend/renderbackend.h
|
||||||
render/backend/renderbackend.cpp
|
render/backend/renderbackend.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -1,373 +0,0 @@
|
|||||||
/***
|
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2019 Olive Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#include "exporter.h"
|
|
||||||
|
|
||||||
#include <QtConcurrent/QtConcurrent>
|
|
||||||
|
|
||||||
#include "render/backend/opengl/openglbackend.h"
|
|
||||||
#include "render/colormanager.h"
|
|
||||||
#include "render/pixelformat.h"
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
|
||||||
|
|
||||||
Exporter::Exporter(ViewerOutput *viewer_node,
|
|
||||||
ColorManager *color_manager,
|
|
||||||
const ExportParams& params,
|
|
||||||
QObject* parent) :
|
|
||||||
QObject(parent),
|
|
||||||
viewer_node_(viewer_node),
|
|
||||||
params_(params),
|
|
||||||
renderer_(nullptr),
|
|
||||||
export_status_(false),
|
|
||||||
export_msg_(tr("Export hasn't started yet"))
|
|
||||||
{
|
|
||||||
encoder_ = Encoder::CreateFromID(params_.encoder(), params_);
|
|
||||||
|
|
||||||
video_done_ = !params_.video_enabled();
|
|
||||||
audio_done_ = !params_.audio_enabled();
|
|
||||||
|
|
||||||
debug_timer_.setInterval(5000);
|
|
||||||
connect(&debug_timer_, &QTimer::timeout, this, &Exporter::DebugTimerMessage);
|
|
||||||
|
|
||||||
connect(this, &Exporter::ExportEnded, this, &Exporter::deleteLater);
|
|
||||||
|
|
||||||
if (params_.has_custom_range()) {
|
|
||||||
export_range_ = params_.custom_range();
|
|
||||||
} else {
|
|
||||||
export_range_ = TimeRange(0, viewer_node_->GetLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params_.video_enabled()) {
|
|
||||||
|
|
||||||
// If a transformation matrix is applied to this video, create it here
|
|
||||||
if (params_.video_scaling_method() != ExportParams::kStretch) {
|
|
||||||
transform_ = GenerateMatrix(params_.video_scaling_method(),
|
|
||||||
viewer_node_->video_params().width(),
|
|
||||||
viewer_node_->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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Exporter::GetExportStatus() const
|
|
||||||
{
|
|
||||||
return export_status_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &Exporter::GetExportError() const
|
|
||||||
{
|
|
||||||
return export_msg_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::Cancel()
|
|
||||||
{
|
|
||||||
if (renderer_) {
|
|
||||||
renderer_->CancelQueue();
|
|
||||||
renderer_->deleteLater();
|
|
||||||
renderer_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetExportMessage(tr("User cancelled export"));
|
|
||||||
ExportStopped();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::StartExporting()
|
|
||||||
{
|
|
||||||
// Default to error state until ExportEnd is called
|
|
||||||
export_status_ = false;
|
|
||||||
|
|
||||||
// Create renderers
|
|
||||||
renderer_ = new OpenGLBackend();
|
|
||||||
renderer_->SetViewerNode(viewer_node_);
|
|
||||||
|
|
||||||
if (!video_done_) {
|
|
||||||
renderer_->SetPixelFormat(params_.video_params().format());
|
|
||||||
renderer_->SetMode(params_.video_params().mode());
|
|
||||||
|
|
||||||
waiting_for_frame_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!audio_done_) {
|
|
||||||
renderer_->SetSampleFormat(params_.audio_params().format());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open encoder and wait for result
|
|
||||||
connect(encoder_, &Encoder::OpenSucceeded, this, &Exporter::EncoderOpenedSuccessfully, Qt::QueuedConnection);
|
|
||||||
connect(encoder_, &Encoder::OpenFailed, this, &Exporter::EncoderOpenFailed, Qt::QueuedConnection);
|
|
||||||
connect(encoder_, &Encoder::AudioComplete, this, &Exporter::AudioEncodeComplete, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(encoder_,
|
|
||||||
"Open",
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::SetExportMessage(const QString &s)
|
|
||||||
{
|
|
||||||
export_msg_ = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::ExportSucceeded()
|
|
||||||
{
|
|
||||||
if (!audio_done_ || !video_done_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderer_) {
|
|
||||||
renderer_->deleteLater();
|
|
||||||
renderer_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export_status_ = true;
|
|
||||||
|
|
||||||
connect(encoder_, &Encoder::Closed, this, &Exporter::EncoderClosed);
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(encoder_,
|
|
||||||
"Close",
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::ExportStopped()
|
|
||||||
{
|
|
||||||
emit ExportEnded();
|
|
||||||
encoder_->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::EncodeFrame()
|
|
||||||
{
|
|
||||||
while (cached_frames_.contains(waiting_for_frame_)) {
|
|
||||||
FramePtr frame = cached_frames_.take(waiting_for_frame_);
|
|
||||||
|
|
||||||
// Encode (may require re-associating alpha?)
|
|
||||||
QMetaObject::invokeMethod(encoder_,
|
|
||||||
"WriteFrame",
|
|
||||||
Qt::QueuedConnection,
|
|
||||||
OLIVE_NS_ARG(FramePtr, frame),
|
|
||||||
OLIVE_NS_ARG(rational, waiting_for_frame_));
|
|
||||||
|
|
||||||
waiting_for_frame_ += params_.video_params().time_base();
|
|
||||||
|
|
||||||
// Calculate progress
|
|
||||||
emit ProgressChanged(waiting_for_frame_.toDouble() / viewer_node_->GetLength().toDouble());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (waiting_for_frame_ >= viewer_node_->GetLength()) {
|
|
||||||
video_done_ = true;
|
|
||||||
debug_timer_.stop();
|
|
||||||
|
|
||||||
ExportSucceeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QMatrix4x4 Exporter::GenerateMatrix(ExportParams::VideoScalingMethod method, int source_width, int source_height, int dest_width, int dest_height)
|
|
||||||
{
|
|
||||||
QMatrix4x4 preview_matrix;
|
|
||||||
|
|
||||||
if (method == ExportParams::kStretch) {
|
|
||||||
return preview_matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
|
|
||||||
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
|
|
||||||
|
|
||||||
if (qFuzzyCompare(export_ar, source_ar)) {
|
|
||||||
return preview_matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((export_ar > source_ar) == (method == ExportParams::kFit)) {
|
|
||||||
preview_matrix.scale(source_ar / export_ar, 1.0F);
|
|
||||||
} else {
|
|
||||||
preview_matrix.scale(1.0F, export_ar / source_ar);
|
|
||||||
}
|
|
||||||
|
|
||||||
return preview_matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
FramePtr FrameColorConvert(ColorProcessorPtr processor, FramePtr frame)
|
|
||||||
{
|
|
||||||
qDebug() << "Converting" << frame->timestamp();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::FrameRendered(FramePtr frame)
|
|
||||||
{
|
|
||||||
// Start color space conversion in another thread
|
|
||||||
QFutureWatcher<FramePtr>* watcher = new QFutureWatcher<FramePtr>();
|
|
||||||
|
|
||||||
connect(watcher, &QFutureWatcher<FramePtr>::finished, this, &Exporter::FrameColorFinished);
|
|
||||||
|
|
||||||
QFuture<FramePtr> future = QtConcurrent::run(FrameColorConvert,
|
|
||||||
color_processor_,
|
|
||||||
frame);
|
|
||||||
|
|
||||||
watcher->setFuture(future);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::AudioRendered()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
// Retrieve the audio filename
|
|
||||||
QString cache_fn = audio_backend_->CachePathName();
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(encoder_,
|
|
||||||
"WriteAudio",
|
|
||||||
Qt::QueuedConnection,
|
|
||||||
OLIVE_NS_ARG(AudioRenderingParams, audio_backend_->params()),
|
|
||||||
Q_ARG(const QString&, cache_fn),
|
|
||||||
OLIVE_NS_ARG(TimeRange, export_range_));
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::AudioEncodeComplete()
|
|
||||||
{
|
|
||||||
audio_done_ = true;
|
|
||||||
|
|
||||||
ExportSucceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::EncoderOpenedSuccessfully()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
// Invalidate caches
|
|
||||||
if (!video_done_) {
|
|
||||||
// First we generate the hashes so we know exactly how many frames we need
|
|
||||||
video_backend_->SetOperatingMode(VideoRenderWorker::kHashOnly);
|
|
||||||
connect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete);
|
|
||||||
|
|
||||||
video_backend_->InvalidateCache(export_range_, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!audio_done_) {
|
|
||||||
// We set the audio backend to render the full sequence to the disk
|
|
||||||
connect(audio_backend_, &AudioRenderBackend::AudioComplete, this, &Exporter::AudioRendered);
|
|
||||||
|
|
||||||
audio_backend_->InvalidateCache(export_range_, nullptr);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::EncoderOpenFailed()
|
|
||||||
{
|
|
||||||
SetExportMessage(tr("Failed to open encoder"));
|
|
||||||
ExportStopped();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::EncoderClosed()
|
|
||||||
{
|
|
||||||
emit ProgressChanged(100);
|
|
||||||
ExportStopped();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::VideoHashesComplete()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
// We've got our hashes, time to kick off actual rendering
|
|
||||||
disconnect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete);
|
|
||||||
|
|
||||||
// Determine what frames will be hashed
|
|
||||||
TimeRangeList ranges;
|
|
||||||
ranges.append(TimeRange(0, viewer_node_->GetLength()));
|
|
||||||
|
|
||||||
// Set video backend to render mode but NOT hash or download
|
|
||||||
video_backend_->SetOperatingMode(VideoRenderWorker::kRenderOnly);
|
|
||||||
video_backend_->SetOnlySignalLastFrameRequested(false);
|
|
||||||
video_backend_->SetFrameGenerationParams(params_.video_params().width(), params_.video_params().height(), transform_);
|
|
||||||
|
|
||||||
connect(video_backend_, &VideoRenderBackend::GeneratedFrame, this, &Exporter::FrameRendered);
|
|
||||||
|
|
||||||
// Remove duplicate frames from cache invalidation
|
|
||||||
const QMap<rational, QByteArray>& time_hash_map = video_backend_->frame_cache()->time_hash_map();
|
|
||||||
QList<QByteArray> hashes_already_seen;
|
|
||||||
QMap<rational, QByteArray>::const_iterator i;
|
|
||||||
|
|
||||||
for (i=time_hash_map.begin(); i!=time_hash_map.end(); i++) {
|
|
||||||
if (hashes_already_seen.contains(i.value())) {
|
|
||||||
ranges.RemoveTimeRange(TimeRange(i.key(), i.key() + params_.video_params().time_base()));
|
|
||||||
} else {
|
|
||||||
hashes_already_seen.append(i.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (const TimeRange& range, ranges) {
|
|
||||||
video_backend_->InvalidateCache(range, nullptr);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::DebugTimerMessage()
|
|
||||||
{
|
|
||||||
qDebug() << "Still waiting for" << waiting_for_frame_.toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exporter::FrameColorFinished()
|
|
||||||
{
|
|
||||||
if (!renderer_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFutureWatcher<FramePtr>* watcher = static_cast< QFutureWatcher<FramePtr>* >(sender());
|
|
||||||
FramePtr frame = watcher->result();
|
|
||||||
watcher->deleteLater();
|
|
||||||
|
|
||||||
debug_timer_.stop();
|
|
||||||
|
|
||||||
const QMap<rational, QByteArray>& time_hash_map = viewer_node_->video_frame_cache()->time_hash_map();
|
|
||||||
|
|
||||||
QByteArray this_hash = time_hash_map.value(frame->timestamp());
|
|
||||||
|
|
||||||
qDebug() << "Received" << this_hash.toHex();
|
|
||||||
|
|
||||||
QList<rational> matching_times = time_hash_map.keys(this_hash);
|
|
||||||
|
|
||||||
foreach (const rational& t, matching_times) {
|
|
||||||
qDebug() << " Matches" << t.toDouble();
|
|
||||||
|
|
||||||
cached_frames_.insert(t, frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << " Waiting for" << waiting_for_frame_.toDouble();
|
|
||||||
|
|
||||||
debug_timer_.start();
|
|
||||||
|
|
||||||
EncodeFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
/***
|
|
||||||
|
|
||||||
Olive - Non-Linear Video Editor
|
|
||||||
Copyright (C) 2019 Olive Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
#ifndef EXPORTER_H
|
|
||||||
#define EXPORTER_H
|
|
||||||
|
|
||||||
#include <QMatrix4x4>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
|
||||||
#include "node/output/viewer/viewer.h"
|
|
||||||
#include "render/backend/exportparams.h"
|
|
||||||
#include "render/backend/renderbackend.h"
|
|
||||||
#include "render/colorprocessor.h"
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
|
||||||
|
|
||||||
class Exporter : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Exporter(ViewerOutput* viewer_node,
|
|
||||||
ColorManager* color_manager,
|
|
||||||
const ExportParams& params,
|
|
||||||
QObject* parent = nullptr);
|
|
||||||
|
|
||||||
bool GetExportStatus() const;
|
|
||||||
const QString& GetExportError() const;
|
|
||||||
|
|
||||||
void Cancel();
|
|
||||||
|
|
||||||
static QMatrix4x4 GenerateMatrix(ExportParams::VideoScalingMethod method, int source_width, int source_height, int dest_width, int dest_height);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void StartExporting();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void ProgressChanged(double);
|
|
||||||
|
|
||||||
void ExportEnded();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void SetExportMessage(const QString& s);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void ExportSucceeded();
|
|
||||||
|
|
||||||
void ExportStopped();
|
|
||||||
|
|
||||||
void EncodeFrame();
|
|
||||||
|
|
||||||
ViewerOutput* viewer_node_;
|
|
||||||
|
|
||||||
ColorProcessorPtr color_processor_;
|
|
||||||
|
|
||||||
ExportParams params_;
|
|
||||||
|
|
||||||
// Renderers
|
|
||||||
RenderBackend* renderer_;
|
|
||||||
|
|
||||||
// Export transform
|
|
||||||
QMatrix4x4 transform_;
|
|
||||||
|
|
||||||
bool video_done_;
|
|
||||||
|
|
||||||
bool audio_done_;
|
|
||||||
|
|
||||||
Encoder* encoder_;
|
|
||||||
|
|
||||||
bool export_status_;
|
|
||||||
|
|
||||||
QString export_msg_;
|
|
||||||
|
|
||||||
TimeRange export_range_;
|
|
||||||
|
|
||||||
rational waiting_for_frame_;
|
|
||||||
|
|
||||||
QHash<rational, FramePtr> cached_frames_;
|
|
||||||
|
|
||||||
QTimer debug_timer_;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void FrameRendered(FramePtr frame);
|
|
||||||
|
|
||||||
void AudioRendered();
|
|
||||||
|
|
||||||
void AudioEncodeComplete();
|
|
||||||
|
|
||||||
void EncoderOpenedSuccessfully();
|
|
||||||
|
|
||||||
void EncoderOpenFailed();
|
|
||||||
|
|
||||||
void EncoderClosed();
|
|
||||||
|
|
||||||
void VideoHashesComplete();
|
|
||||||
|
|
||||||
void DebugTimerMessage();
|
|
||||||
|
|
||||||
void FrameColorFinished();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
|
||||||
|
|
||||||
#endif // EXPORTER_H
|
|
||||||
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
add_subdirectory(cache)
|
add_subdirectory(cache)
|
||||||
add_subdirectory(conform)
|
add_subdirectory(conform)
|
||||||
|
add_subdirectory(export)
|
||||||
|
add_subdirectory(project)
|
||||||
add_subdirectory(proxy)
|
add_subdirectory(proxy)
|
||||||
|
add_subdirectory(render)
|
||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
task/task.h
|
task/task.h
|
||||||
task/task.cpp
|
|
||||||
task/taskmanager.h
|
task/taskmanager.h
|
||||||
task/taskmanager.cpp
|
task/taskmanager.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
|
|||||||
Vendored
+7
-153
@@ -22,181 +22,35 @@
|
|||||||
|
|
||||||
#include <QLinkedList>
|
#include <QLinkedList>
|
||||||
|
|
||||||
#include "common/timecodefunctions.h"
|
|
||||||
#include "project/item/sequence/sequence.h"
|
#include "project/item/sequence/sequence.h"
|
||||||
#include "render/backend/opengl/openglbackend.h"
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
CacheTask::CacheTask(ViewerOutput* viewer, int divider, bool in_out_only) :
|
CacheTask::CacheTask(ViewerOutput* viewer, int divider, bool in_out_only) :
|
||||||
viewer_(viewer),
|
RenderTask(viewer),
|
||||||
in_out_only_(in_out_only),
|
in_out_only_(in_out_only),
|
||||||
divider_(divider)
|
divider_(divider)
|
||||||
{
|
{
|
||||||
SetTitle(tr("Caching \"%1\"").arg(viewer_->media_name()));
|
SetTitle(tr("Caching \"%1\"").arg(viewer->media_name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TimeHashFuturePair {
|
bool CacheTask::Run()
|
||||||
rational time;
|
|
||||||
QFuture<QByteArray> hash_future;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HashFrameFuturePair {
|
|
||||||
QByteArray hash;
|
|
||||||
QFuture<FramePtr> frame_future;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HashDownloadFuturePair {
|
|
||||||
QByteArray hash;
|
|
||||||
QFuture<void> download_future;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HashTimePair {
|
|
||||||
rational time;
|
|
||||||
QByteArray hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CacheTask::Action()
|
|
||||||
{
|
{
|
||||||
OpenGLBackend backend;
|
|
||||||
|
|
||||||
RenderMode::Mode mode = RenderMode::kOffline;
|
|
||||||
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
|
|
||||||
|
|
||||||
backend.SetAudioEnabled(false);
|
|
||||||
backend.SetViewerNode(viewer_);
|
|
||||||
backend.SetPixelFormat(format);
|
|
||||||
backend.SetMode(mode);
|
|
||||||
backend.SetDivider(divider_);
|
|
||||||
backend.SetSampleFormat(SampleFormat::kInternalFormat);
|
|
||||||
|
|
||||||
// Get list of invalidated ranges
|
// Get list of invalidated ranges
|
||||||
TimeRangeList range_to_cache = viewer_->video_frame_cache()->GetInvalidatedRanges();
|
TimeRangeList range_to_cache = viewer()->video_frame_cache()->GetInvalidatedRanges();
|
||||||
|
|
||||||
// If we're caching only in-out, limit the range to that
|
// If we're caching only in-out, limit the range to that
|
||||||
if (in_out_only_) {
|
if (in_out_only_) {
|
||||||
Sequence* s = static_cast<Sequence*>(viewer_->parent());
|
Sequence* s = static_cast<Sequence*>(viewer()->parent());
|
||||||
|
|
||||||
if (s->workarea()->enabled()) {
|
if (s->workarea()->enabled()) {
|
||||||
range_to_cache = range_to_cache.Intersects(s->workarea()->range());
|
range_to_cache = range_to_cache.Intersects(s->workarea()->range());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Get hashes for each frame
|
|
||||||
QLinkedList<TimeHashFuturePair> hash_list;
|
|
||||||
|
|
||||||
while (!range_to_cache.isEmpty()) {
|
Render(range_to_cache, 2);
|
||||||
const TimeRange& range = range_to_cache.first();
|
|
||||||
|
|
||||||
const rational& timebase = viewer_->video_params().time_base();
|
return true;
|
||||||
|
|
||||||
rational time = range.in();
|
|
||||||
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
|
|
||||||
rational next;
|
|
||||||
|
|
||||||
if (snapped > time) {
|
|
||||||
next = snapped;
|
|
||||||
snapped -= timebase;
|
|
||||||
} else {
|
|
||||||
next = snapped + timebase;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_list.append({snapped, backend.Hash(snapped, false)});
|
|
||||||
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine any duplicates
|
|
||||||
QMap< QByteArray, QLinkedList<rational> > times_to_render;
|
|
||||||
foreach (const TimeHashFuturePair& i, hash_list) {
|
|
||||||
times_to_render[i.hash_future.result()].append(i.time);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render all frames necessary
|
|
||||||
QLinkedList<HashFrameFuturePair> render_lookup_table;
|
|
||||||
{
|
|
||||||
QLinkedList<HashTimePair> sorted_times;
|
|
||||||
QLinkedList<HashTimePair>::iterator sorted_iterator;
|
|
||||||
|
|
||||||
// Rendering is more efficient if we cache in order
|
|
||||||
QMap< QByteArray, QLinkedList<rational> >::const_iterator i;
|
|
||||||
for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) {
|
|
||||||
const QByteArray& hash = i.key();
|
|
||||||
const rational& time = i.value().first();
|
|
||||||
|
|
||||||
bool inserted = false;
|
|
||||||
|
|
||||||
for (sorted_iterator=sorted_times.begin(); sorted_iterator!=sorted_times.end(); sorted_iterator++) {
|
|
||||||
if (sorted_iterator->time > time) {
|
|
||||||
sorted_times.insert(sorted_iterator, {time, hash});
|
|
||||||
inserted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inserted) {
|
|
||||||
sorted_times.append({time, hash});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (const HashTimePair& p, sorted_times) {
|
|
||||||
render_lookup_table.append({p.hash, backend.RenderFrame(p.time, false, false)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
|
|
||||||
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();
|
|
||||||
|
|
||||||
QLinkedList<HashDownloadFuturePair> download_futures;
|
|
||||||
|
|
||||||
// Iterators
|
|
||||||
QLinkedList<HashFrameFuturePair>::iterator i;
|
|
||||||
QLinkedList<HashDownloadFuturePair>::iterator j;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// Start multithreaded download here
|
|
||||||
download_futures.append({i->hash,
|
|
||||||
QtConcurrent::run(FrameHashCache::SaveCacheFrame, i->hash, f)});
|
|
||||||
|
|
||||||
i = render_lookup_table.erase(i);
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal process
|
|
||||||
counter++;
|
|
||||||
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(nb_frames)));
|
|
||||||
|
|
||||||
j = download_futures.erase(j);
|
|
||||||
} else {
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
Vendored
+6
-7
@@ -21,23 +21,22 @@
|
|||||||
#ifndef CACHETASK_H
|
#ifndef CACHETASK_H
|
||||||
#define CACHETASK_H
|
#define CACHETASK_H
|
||||||
|
|
||||||
#include "node/output/viewer/viewer.h"
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include "task/task.h"
|
|
||||||
|
#include "task/render/render.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
class CacheTask : public Task
|
class CacheTask : public RenderTask
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CacheTask(ViewerOutput* viewer, int divider, bool in_out_only);
|
CacheTask(ViewerOutput* viewer, int divider, bool in_out_only);
|
||||||
|
|
||||||
protected:
|
public slots:
|
||||||
virtual void Action() override;
|
virtual bool Run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ViewerOutput* viewer_;
|
|
||||||
|
|
||||||
bool in_out_only_;
|
bool in_out_only_;
|
||||||
|
|
||||||
int divider_;
|
int divider_;
|
||||||
|
|||||||
@@ -31,10 +31,11 @@ ConformTask::ConformTask(AudioStreamPtr stream, const AudioRenderingParams& para
|
|||||||
SetTitle(tr("Conforming Audio %1:%2").arg(stream_->footage()->filename(), QString::number(stream_->index())));
|
SetTitle(tr("Conforming Audio %1:%2").arg(stream_->footage()->filename(), QString::number(stream_->index())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConformTask::Action()
|
bool ConformTask::Run()
|
||||||
{
|
{
|
||||||
if (stream_->footage()->decoder().isEmpty()) {
|
if (stream_->footage()->decoder().isEmpty()) {
|
||||||
emit Failed(tr("Failed to find decoder to conform audio stream"));
|
SetError(tr("Failed to find decoder to conform audio stream"));
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
||||||
|
|
||||||
@@ -42,10 +43,11 @@ void ConformTask::Action()
|
|||||||
|
|
||||||
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
|
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
|
||||||
|
|
||||||
if (decoder->ConformAudio(&IsCancelled(), params_)) {
|
if (!decoder->ConformAudio(&IsCancelled(), params_)) {
|
||||||
emit Succeeded();
|
SetError(tr("Failed to conform audio"));
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
emit Failed(QStringLiteral("Failed to conform audio"));
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ class ConformTask : public Task
|
|||||||
public:
|
public:
|
||||||
ConformTask(AudioStreamPtr stream, const AudioRenderingParams& params);
|
ConformTask(AudioStreamPtr stream, const AudioRenderingParams& params);
|
||||||
|
|
||||||
protected:
|
public slots:
|
||||||
virtual void Action() override;
|
virtual bool Run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioStreamPtr stream_;
|
AudioStreamPtr stream_;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Olive - Non-Linear Video Editor
|
||||||
|
# Copyright (C) 2019 Olive Team
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
set(OLIVE_SOURCES
|
||||||
|
${OLIVE_SOURCES}
|
||||||
|
task/export/export.h
|
||||||
|
task/export/export.cpp
|
||||||
|
task/export/exportparams.h
|
||||||
|
task/export/exportparams.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -18,40 +18,22 @@
|
|||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include "task.h"
|
#include "export.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
Task::Task() :
|
ExportTask::ExportTask(ViewerOutput* viewer_node,
|
||||||
title_(tr("Task"))
|
ColorManager* color_manager,
|
||||||
|
const ExportParams& params) :
|
||||||
|
RenderTask(viewer_node),
|
||||||
|
color_manager_(color_manager),
|
||||||
|
params_(params)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Task::Start()
|
bool ExportTask::Run()
|
||||||
{
|
{
|
||||||
Action();
|
return true;
|
||||||
|
|
||||||
emit Finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &Task::GetTitle()
|
|
||||||
{
|
|
||||||
return title_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Task::Cancel()
|
|
||||||
{
|
|
||||||
CancelableObject::Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Task::SetErrorText(const QString &s)
|
|
||||||
{
|
|
||||||
error_ = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Task::SetTitle(const QString &s)
|
|
||||||
{
|
|
||||||
title_ = s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2019 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef EXPORTTASK_H
|
||||||
|
#define EXPORTTASK_H
|
||||||
|
|
||||||
|
#include "exportparams.h"
|
||||||
|
#include "node/output/viewer/viewer.h"
|
||||||
|
#include "render/colorprocessor.h"
|
||||||
|
#include "task/render/render.h"
|
||||||
|
#include "task/task.h"
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
class ExportTask : public RenderTask
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExportTask(ViewerOutput *viewer_node, ColorManager *color_manager, const ExportParams ¶ms);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
virtual bool Run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ColorManager* color_manager_;
|
||||||
|
|
||||||
|
ExportParams params_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|
||||||
|
#endif // EXPORTTASK_H
|
||||||
@@ -74,4 +74,30 @@ void ExportParams::set_color_transform(const ColorTransform &color_transform)
|
|||||||
color_transform_ = color_transform;
|
color_transform_ = color_transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMatrix4x4 ExportParams::GenerateMatrix(ExportParams::VideoScalingMethod method,
|
||||||
|
int source_width, int source_height,
|
||||||
|
int dest_width, int dest_height)
|
||||||
|
{
|
||||||
|
QMatrix4x4 preview_matrix;
|
||||||
|
|
||||||
|
if (method == ExportParams::kStretch) {
|
||||||
|
return preview_matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
float export_ar = static_cast<float>(dest_width) / static_cast<float>(dest_height);
|
||||||
|
float source_ar = static_cast<float>(source_width) / static_cast<float>(source_height);
|
||||||
|
|
||||||
|
if (qFuzzyCompare(export_ar, source_ar)) {
|
||||||
|
return preview_matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((export_ar > source_ar) == (method == ExportParams::kFit)) {
|
||||||
|
preview_matrix.scale(source_ar / export_ar, 1.0F);
|
||||||
|
} else {
|
||||||
|
preview_matrix.scale(1.0F, export_ar / source_ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return preview_matrix;
|
||||||
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
#ifndef EXPORTPARAMS_H
|
#ifndef EXPORTPARAMS_H
|
||||||
#define EXPORTPARAMS_H
|
#define EXPORTPARAMS_H
|
||||||
|
|
||||||
|
#include <QMatrix4x4>
|
||||||
|
|
||||||
#include "codec/encoder.h"
|
#include "codec/encoder.h"
|
||||||
#include "node/output/viewer/viewer.h"
|
#include "node/output/viewer/viewer.h"
|
||||||
#include "render/colortransform.h"
|
#include "render/colortransform.h"
|
||||||
@@ -50,6 +52,10 @@ public:
|
|||||||
const ColorTransform& color_transform() const;
|
const ColorTransform& color_transform() const;
|
||||||
void set_color_transform(const ColorTransform& color_transform);
|
void set_color_transform(const ColorTransform& color_transform);
|
||||||
|
|
||||||
|
static QMatrix4x4 GenerateMatrix(ExportParams::VideoScalingMethod method,
|
||||||
|
int source_width, int source_height,
|
||||||
|
int dest_width, int dest_height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString encoder_id_;
|
QString encoder_id_;
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Olive - Non-Linear Video Editor
|
||||||
|
# Copyright (C) 2019 Olive Team
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
add_subdirectory(import)
|
||||||
|
add_subdirectory(load)
|
||||||
|
add_subdirectory(save)
|
||||||
|
|
||||||
|
set(OLIVE_SOURCES
|
||||||
|
${OLIVE_SOURCES}
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Olive - Non-Linear Video Editor
|
||||||
|
# Copyright (C) 2019 Olive Team
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
set(OLIVE_SOURCES
|
||||||
|
${OLIVE_SOURCES}
|
||||||
|
task/project/import/import.h
|
||||||
|
task/project/import/import.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include "projectimportmanager.h"
|
#include "import.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
ProjectImportManager::ProjectImportManager(ProjectViewModel *model, Folder *folder, const QStringList &filenames) :
|
ProjectImportTask::ProjectImportTask(ProjectViewModel *model, Folder *folder, const QStringList &filenames) :
|
||||||
|
command_(nullptr),
|
||||||
model_(model),
|
model_(model),
|
||||||
folder_(folder)
|
folder_(folder)
|
||||||
{
|
{
|
||||||
@@ -42,27 +43,29 @@ ProjectImportManager::ProjectImportManager(ProjectViewModel *model, Folder *fold
|
|||||||
SetTitle(tr("Importing %1 files").arg(file_count_));
|
SetTitle(tr("Importing %1 files").arg(file_count_));
|
||||||
}
|
}
|
||||||
|
|
||||||
const int &ProjectImportManager::GetFileCount()
|
const int &ProjectImportTask::GetFileCount() const
|
||||||
{
|
{
|
||||||
return file_count_;
|
return file_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectImportManager::Action()
|
bool ProjectImportTask::Run()
|
||||||
{
|
{
|
||||||
QUndoCommand* command = new QUndoCommand();
|
command_ = new QUndoCommand();
|
||||||
|
|
||||||
int imported = 0;
|
int imported = 0;
|
||||||
|
|
||||||
Import(folder_, filenames_, imported, command);
|
Import(folder_, filenames_, imported, command_);
|
||||||
|
|
||||||
if (IsCancelled()) {
|
if (IsCancelled()) {
|
||||||
delete command;
|
delete command_;
|
||||||
|
command_ = nullptr;
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
emit ImportComplete(command);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectImportManager::Import(Folder *folder, const QFileInfoList &import, int &counter, QUndoCommand* parent_command)
|
void ProjectImportTask::Import(Folder *folder, const QFileInfoList &import, int &counter, QUndoCommand* parent_command)
|
||||||
{
|
{
|
||||||
foreach (const QFileInfo& file_info, import) {
|
foreach (const QFileInfo& file_info, import) {
|
||||||
if (IsCancelled()) {
|
if (IsCancelled()) {
|
||||||
@@ -24,28 +24,32 @@
|
|||||||
#include <QFileInfoList>
|
#include <QFileInfoList>
|
||||||
#include <QUndoCommand>
|
#include <QUndoCommand>
|
||||||
|
|
||||||
#include "projectviewmodel.h"
|
#include "project/projectviewmodel.h"
|
||||||
#include "task/task.h"
|
#include "task/task.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
class ProjectImportManager : public Task
|
class ProjectImportTask : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ProjectImportManager(ProjectViewModel* model, Folder* folder, const QStringList& filenames);
|
ProjectImportTask(ProjectViewModel* model, Folder* folder, const QStringList& filenames);
|
||||||
|
|
||||||
const int& GetFileCount();
|
const int& GetFileCount() const;
|
||||||
|
|
||||||
protected:
|
QUndoCommand* GetCommand() const
|
||||||
virtual void Action() override;
|
{
|
||||||
|
return command_;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
public slots:
|
||||||
void ImportComplete(QUndoCommand* command);
|
virtual bool Run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Import(Folder* folder, const QFileInfoList &import, int& counter, QUndoCommand *parent_command);
|
void Import(Folder* folder, const QFileInfoList &import, int& counter, QUndoCommand *parent_command);
|
||||||
|
|
||||||
|
QUndoCommand* command_;
|
||||||
|
|
||||||
ProjectViewModel* model_;
|
ProjectViewModel* model_;
|
||||||
|
|
||||||
Folder* folder_;
|
Folder* folder_;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Olive - Non-Linear Video Editor
|
||||||
|
# Copyright (C) 2019 Olive Team
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
set(OLIVE_SOURCES
|
||||||
|
${OLIVE_SOURCES}
|
||||||
|
task/project/load/load.h
|
||||||
|
task/project/load/load.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include "projectloadmanager.h"
|
#include "load.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -28,13 +28,13 @@
|
|||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
ProjectLoadManager::ProjectLoadManager(const QString &filename) :
|
ProjectLoadTask::ProjectLoadTask(const QString &filename) :
|
||||||
filename_(filename)
|
filename_(filename)
|
||||||
{
|
{
|
||||||
SetTitle(tr("Loading '%1'").arg(filename));
|
SetTitle(tr("Loading '%1'").arg(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectLoadManager::Action()
|
bool ProjectLoadTask::Run()
|
||||||
{
|
{
|
||||||
QFile project_file(filename_);
|
QFile project_file(filename_);
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ void ProjectLoadManager::Action()
|
|||||||
moveToThread(qApp->thread());
|
moveToThread(qApp->thread());
|
||||||
|
|
||||||
if (!IsCancelled()) {
|
if (!IsCancelled()) {
|
||||||
emit ProjectLoaded(project);
|
projects_.append(project);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
@@ -68,14 +68,18 @@ void ProjectLoadManager::Action()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project_file.close();
|
||||||
|
|
||||||
if (reader.hasError()) {
|
if (reader.hasError()) {
|
||||||
qDebug() << "Found XML error:" << reader.errorString();
|
SetError(reader.errorString());
|
||||||
emit Failed(reader.errorString());
|
return false;
|
||||||
} else {
|
} else {
|
||||||
emit Succeeded();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
project_file.close();
|
} else {
|
||||||
|
SetError(tr("Failed to read file \"%1\" for reading.").arg(filename_));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,19 +26,23 @@
|
|||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
class ProjectLoadManager : public Task
|
class ProjectLoadTask : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ProjectLoadManager(const QString& filename);
|
ProjectLoadTask(const QString& filename);
|
||||||
|
|
||||||
protected:
|
const QList<ProjectPtr>& GetLoadedProjects()
|
||||||
virtual void Action() override;
|
{
|
||||||
|
return projects_;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
public slots:
|
||||||
void ProjectLoaded(OLIVE_NAMESPACE::ProjectPtr project);
|
virtual bool Run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QList<ProjectPtr> projects_;
|
||||||
|
|
||||||
QString filename_;
|
QString filename_;
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Olive - Non-Linear Video Editor
|
||||||
|
# Copyright (C) 2019 Olive Team
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
set(OLIVE_SOURCES
|
||||||
|
${OLIVE_SOURCES}
|
||||||
|
task/project/save/save.h
|
||||||
|
task/project/save/save.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -18,20 +18,20 @@
|
|||||||
|
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include "projectsavemanager.h"
|
#include "save.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
ProjectSaveManager::ProjectSaveManager(ProjectPtr project) :
|
ProjectSaveTask::ProjectSaveTask(ProjectPtr project) :
|
||||||
project_(project)
|
project_(project)
|
||||||
{
|
{
|
||||||
SetTitle(tr("Saving '%1'").arg(project->filename()));
|
SetTitle(tr("Saving '%1'").arg(project->filename()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectSaveManager::Action()
|
bool ProjectSaveTask::Run()
|
||||||
{
|
{
|
||||||
QFile project_file(project_->filename());
|
QFile project_file(project_->filename());
|
||||||
|
|
||||||
@@ -52,10 +52,12 @@ void ProjectSaveManager::Action()
|
|||||||
writer.writeEndDocument();
|
writer.writeEndDocument();
|
||||||
|
|
||||||
project_file.close();
|
project_file.close();
|
||||||
}
|
|
||||||
|
|
||||||
emit Succeeded();
|
return true;
|
||||||
emit ProjectSaveSucceeded(project_);
|
} else {
|
||||||
|
SetError(tr("Failed to open file \"%1\" for writing.").arg(project_->filename()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
@@ -26,17 +26,19 @@
|
|||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
class ProjectSaveManager : public Task
|
class ProjectSaveTask : public Task
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ProjectSaveManager(ProjectPtr project);
|
ProjectSaveTask(ProjectPtr project);
|
||||||
|
|
||||||
signals:
|
ProjectPtr GetProject() const
|
||||||
void ProjectSaveSucceeded(OLIVE_NAMESPACE::ProjectPtr p);
|
{
|
||||||
|
return project_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
public slots:
|
||||||
virtual void Action() override;
|
virtual bool Run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ProjectPtr project_;
|
ProjectPtr project_;
|
||||||
@@ -38,10 +38,11 @@ ProxyTask::ProxyTask(VideoStreamPtr stream, int divider) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyTask::Action()
|
bool ProxyTask::Run()
|
||||||
{
|
{
|
||||||
if (stream_->footage()->decoder().isEmpty()) {
|
if (stream_->footage()->decoder().isEmpty()) {
|
||||||
emit Failed(tr("Failed to find decoder to conform audio stream"));
|
SetError(tr("Failed to find decoder to conform audio stream"));
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
||||||
|
|
||||||
@@ -50,9 +51,10 @@ void ProxyTask::Action()
|
|||||||
connect(decoder.get(), &Decoder::IndexProgress, this, &ProxyTask::ProgressChanged);
|
connect(decoder.get(), &Decoder::IndexProgress, this, &ProxyTask::ProgressChanged);
|
||||||
|
|
||||||
if (decoder->ProxyVideo(&IsCancelled(), divider_)) {
|
if (decoder->ProxyVideo(&IsCancelled(), divider_)) {
|
||||||
emit Succeeded();
|
return true;
|
||||||
} else {
|
} else {
|
||||||
emit Failed(QStringLiteral("Failed to generate proxy"));
|
SetError(tr("Failed to generate proxy"));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class ProxyTask : public Task
|
|||||||
public:
|
public:
|
||||||
ProxyTask(VideoStreamPtr stream, int divider);
|
ProxyTask(VideoStreamPtr stream, int divider);
|
||||||
|
|
||||||
protected:
|
public slots:
|
||||||
virtual void Action() override;
|
virtual bool Run() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VideoStreamPtr stream_;
|
VideoStreamPtr stream_;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Olive - Non-Linear Video Editor
|
||||||
|
# Copyright (C) 2019 Olive Team
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
set(OLIVE_SOURCES
|
||||||
|
${OLIVE_SOURCES}
|
||||||
|
task/render/render.h
|
||||||
|
task/render/render.cpp
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2019 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "render.h"
|
||||||
|
|
||||||
|
#include "common/timecodefunctions.h"
|
||||||
|
#include "render/backend/opengl/openglbackend.h"
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
RenderTask::RenderTask(ViewerOutput* viewer) :
|
||||||
|
viewer_(viewer)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TimeHashFuturePair {
|
||||||
|
rational time;
|
||||||
|
QFuture<QByteArray> hash_future;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashFrameFuturePair {
|
||||||
|
QByteArray hash;
|
||||||
|
QFuture<FramePtr> frame_future;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashDownloadFuturePair {
|
||||||
|
QByteArray hash;
|
||||||
|
QFuture<void> download_future;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashTimePair {
|
||||||
|
rational time;
|
||||||
|
QByteArray hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RenderTask::Render(TimeRangeList range_to_cache, int divider)
|
||||||
|
{
|
||||||
|
OpenGLBackend backend;
|
||||||
|
|
||||||
|
RenderMode::Mode mode = RenderMode::kOffline;
|
||||||
|
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
|
||||||
|
|
||||||
|
backend.SetAudioEnabled(false);
|
||||||
|
backend.SetViewerNode(viewer_);
|
||||||
|
backend.SetPixelFormat(format);
|
||||||
|
backend.SetMode(mode);
|
||||||
|
backend.SetDivider(divider);
|
||||||
|
backend.SetSampleFormat(SampleFormat::kInternalFormat);
|
||||||
|
|
||||||
|
// Get hashes for each frame
|
||||||
|
QLinkedList<TimeHashFuturePair> hash_list;
|
||||||
|
|
||||||
|
while (!range_to_cache.isEmpty()) {
|
||||||
|
const TimeRange& range = range_to_cache.first();
|
||||||
|
|
||||||
|
const rational& timebase = viewer_->video_params().time_base();
|
||||||
|
|
||||||
|
rational time = range.in();
|
||||||
|
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
|
||||||
|
rational next;
|
||||||
|
|
||||||
|
if (snapped > time) {
|
||||||
|
next = snapped;
|
||||||
|
snapped -= timebase;
|
||||||
|
} else {
|
||||||
|
next = snapped + timebase;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_list.append({snapped, backend.Hash(snapped, false)});
|
||||||
|
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine any duplicates
|
||||||
|
QMap< QByteArray, QLinkedList<rational> > times_to_render;
|
||||||
|
foreach (const TimeHashFuturePair& i, hash_list) {
|
||||||
|
times_to_render[i.hash_future.result()].append(i.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render all frames necessary
|
||||||
|
QLinkedList<HashFrameFuturePair> render_lookup_table;
|
||||||
|
{
|
||||||
|
QLinkedList<HashTimePair> sorted_times;
|
||||||
|
QLinkedList<HashTimePair>::iterator sorted_iterator;
|
||||||
|
|
||||||
|
// Rendering is more efficient if we cache in order
|
||||||
|
QMap< QByteArray, QLinkedList<rational> >::const_iterator i;
|
||||||
|
for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) {
|
||||||
|
const QByteArray& hash = i.key();
|
||||||
|
const rational& time = i.value().first();
|
||||||
|
|
||||||
|
bool inserted = false;
|
||||||
|
|
||||||
|
for (sorted_iterator=sorted_times.begin(); sorted_iterator!=sorted_times.end(); sorted_iterator++) {
|
||||||
|
if (sorted_iterator->time > time) {
|
||||||
|
sorted_times.insert(sorted_iterator, {time, hash});
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inserted) {
|
||||||
|
sorted_times.append({time, hash});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const HashTimePair& p, sorted_times) {
|
||||||
|
render_lookup_table.append({p.hash, backend.RenderFrame(p.time, false, false)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
|
||||||
|
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();
|
||||||
|
|
||||||
|
QLinkedList<HashDownloadFuturePair> download_futures;
|
||||||
|
|
||||||
|
// Iterators
|
||||||
|
QLinkedList<HashFrameFuturePair>::iterator i;
|
||||||
|
QLinkedList<HashDownloadFuturePair>::iterator j;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Start multithreaded download here
|
||||||
|
download_futures.append({i->hash,
|
||||||
|
QtConcurrent::run(FrameHashCache::SaveCacheFrame, i->hash, f)});
|
||||||
|
|
||||||
|
i = render_lookup_table.erase(i);
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal process
|
||||||
|
counter++;
|
||||||
|
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(nb_frames)));
|
||||||
|
|
||||||
|
j = download_futures.erase(j);
|
||||||
|
} else {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_EXIT
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
Olive - Non-Linear Video Editor
|
||||||
|
Copyright (C) 2019 Olive Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef RENDERTASK_H
|
||||||
|
#define RENDERTASK_H
|
||||||
|
|
||||||
|
#include "node/output/viewer/viewer.h"
|
||||||
|
#include "task/task.h"
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
class RenderTask : public Task
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RenderTask(ViewerOutput* viewer);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Render(TimeRangeList range_to_cache, int divider = 1);
|
||||||
|
|
||||||
|
ViewerOutput* viewer() const
|
||||||
|
{
|
||||||
|
return viewer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ViewerOutput* viewer_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|
||||||
|
#endif // RENDERTASK_H
|
||||||
+43
-42
@@ -52,59 +52,68 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Task Constructor
|
* @brief Task Constructor
|
||||||
*/
|
*/
|
||||||
Task();
|
Task() :
|
||||||
|
title_(tr("Task")),
|
||||||
|
error_(tr("Unknown error"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the current title of this Task
|
* @brief Retrieve the current title of this Task
|
||||||
*/
|
*/
|
||||||
const QString& GetTitle();
|
const QString& GetTitle()
|
||||||
|
{
|
||||||
|
return title_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the error that occurred if Run() returns false
|
||||||
|
*/
|
||||||
|
const QString& GetError()
|
||||||
|
{
|
||||||
|
return error_;
|
||||||
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* @brief Try to start this Task
|
* @brief Run this task
|
||||||
*
|
*
|
||||||
* The main function for starting this Task. If this task is currently waiting, this function will start a new thread
|
* @return True if the task completed successfully, false if not.
|
||||||
* and set the status to kWorking.
|
|
||||||
*
|
*
|
||||||
* This function also checks its dependency Tasks and will only start if all of them are complete. If they are still
|
* \see GetError() if this returns false.
|
||||||
* working, this function will return FALSE and the status will continue to be kWaiting. If any of them failed, this
|
|
||||||
* Task will also fail - this function will return FALSE and the status will be set to kError.
|
|
||||||
*/
|
*/
|
||||||
void Start();
|
virtual bool Run() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset state so that Run() can be called again.
|
||||||
|
*
|
||||||
|
* Override this if your class holds any persistent state that should be cleared/modified before
|
||||||
|
* it's safe for Run() to run again.
|
||||||
|
*/
|
||||||
|
virtual void Reset(){}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Cancel the Task
|
* @brief Cancel the Task
|
||||||
*
|
*
|
||||||
* Sends a signal to the Task to stop as soon as possible. Always call this directly or connect with
|
* Sends a signal to the Task to stop as soon as possible. Always call this directly or connect
|
||||||
* Qt::DirectConnection, or else it'll be queued *after* the task has already finished.
|
* with Qt::DirectConnection, or else it'll be queued *after* the task has already finished.
|
||||||
*/
|
*/
|
||||||
void Cancel();
|
void Cancel()
|
||||||
|
{
|
||||||
|
CancelableObject::Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
|
||||||
* @brief The main Task function which is run in a separate thread
|
|
||||||
*
|
|
||||||
* Action() is the function that gets called once the separate thread has been created. This function should be
|
|
||||||
* overridden in subclasses.
|
|
||||||
*
|
|
||||||
* It's also recommended to emit ProgressChanged() throughout your Action() so that any attached ProgressBars can
|
|
||||||
* show accurate progress information.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*
|
|
||||||
* TRUE if the Task could complete successfully. FALSE if not. Note that FALSE should only be returned if the Task
|
|
||||||
* could not finish, not if the Task found a negative result (see Task documentation for details). Before returning
|
|
||||||
* FALSE, it's recommended to use set_error() to signal to the user what caused the failure.
|
|
||||||
*/
|
|
||||||
virtual void Action() = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the error message
|
* @brief Set the error message
|
||||||
*
|
*
|
||||||
* It is recommended to use this if your Action() function ever returns FALSE to tell the user why the failure
|
* It is recommended to use this if your Action() function ever returns FALSE to tell the user why the failure
|
||||||
* occurred.
|
* occurred.
|
||||||
*/
|
*/
|
||||||
void SetErrorText(const QString& s);
|
void SetError(const QString& s)
|
||||||
|
{
|
||||||
|
error_ = s;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the Task title
|
* @brief Set the Task title
|
||||||
@@ -113,7 +122,10 @@ protected:
|
|||||||
* and shouldn't need to change during the life of the Task. To show an error message, it's recommended to use
|
* and shouldn't need to change during the life of the Task. To show an error message, it's recommended to use
|
||||||
* set_error() instead.
|
* set_error() instead.
|
||||||
*/
|
*/
|
||||||
void SetTitle(const QString& s);
|
void SetTitle(const QString& s)
|
||||||
|
{
|
||||||
|
title_ = s;
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
@@ -127,17 +139,6 @@ signals:
|
|||||||
*/
|
*/
|
||||||
void ProgressChanged(int p);
|
void ProgressChanged(int p);
|
||||||
|
|
||||||
void Succeeded();
|
|
||||||
|
|
||||||
void Failed(const QString& error);
|
|
||||||
|
|
||||||
void Finished();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Signal emitted when this Task is removed from TaskManager
|
|
||||||
*/
|
|
||||||
void Removed();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString title_;
|
QString title_;
|
||||||
|
|
||||||
|
|||||||
+29
-177
@@ -27,45 +27,22 @@ OLIVE_NAMESPACE_ENTER
|
|||||||
|
|
||||||
TaskManager* TaskManager::instance_ = nullptr;
|
TaskManager* TaskManager::instance_ = nullptr;
|
||||||
|
|
||||||
TaskManager::TaskManager() :
|
TaskManager::TaskManager()
|
||||||
active_thread_count_(0)
|
|
||||||
{
|
{
|
||||||
// Initialize threads to run tasks on
|
|
||||||
threads_.resize(QThread::idealThreadCount());
|
|
||||||
|
|
||||||
for (int i=0;i<threads_.size();i++) {
|
|
||||||
QThread* t = new QThread(this);
|
|
||||||
t->start(QThread::IdlePriority);
|
|
||||||
threads_.replace(i, {t, false});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskManager::~TaskManager()
|
TaskManager::~TaskManager()
|
||||||
{
|
{
|
||||||
// First send the signal to all tasks to start cancelling
|
thread_pool_.clear();
|
||||||
foreach (const TaskContainer& task_info, tasks_) {
|
|
||||||
if (task_info.status == kWorking) {
|
foreach (Task* t, tasks_) {
|
||||||
task_info.task->Cancel();
|
t->Cancel();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, signal each thread to quit as its next event in the queue
|
thread_pool_.waitForDone();
|
||||||
foreach (const ThreadContainer& tc, threads_) {
|
|
||||||
tc.thread->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for each thread's event queue to finish
|
foreach (Task* t, tasks_) {
|
||||||
foreach (const ThreadContainer& tc, threads_) {
|
t->deleteLater();
|
||||||
tc.thread->wait();
|
|
||||||
|
|
||||||
// This is technically unnecessary since each QThread is a child of this object, but we may as well
|
|
||||||
delete tc.thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally delete all task objects (they shouldn't have been deleted by TaskSucceeded() or TaskFailed() because our
|
|
||||||
// event queue shouldn't be active by this point
|
|
||||||
foreach (const TaskContainer& task_info, tasks_) {
|
|
||||||
delete task_info.task;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,171 +69,46 @@ int TaskManager::GetTaskCount() const
|
|||||||
|
|
||||||
Task *TaskManager::GetFirstTask() const
|
Task *TaskManager::GetFirstTask() const
|
||||||
{
|
{
|
||||||
return tasks_.first().task;
|
return tasks_.begin().value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskManager::AddTask(Task* t)
|
void TaskManager::AddTask(Task* t)
|
||||||
{
|
{
|
||||||
// Connect Task's status signal to the Callback
|
// Create a watcher for signalling
|
||||||
connect(t, &Task::Succeeded, this, &TaskManager::TaskSucceeded, Qt::QueuedConnection);
|
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>();
|
||||||
connect(t, &Task::Failed, this, &TaskManager::TaskFailed, Qt::QueuedConnection);
|
connect(watcher, &QFutureWatcher<bool>::finished, this, &TaskManager::TaskFinished);
|
||||||
connect(t, &Task::Finished, this, &TaskManager::TaskFinished, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
// Add the Task to the queue
|
// Add the Task to the queue
|
||||||
tasks_.append({t, kWaiting});
|
tasks_.insert(watcher, t);
|
||||||
|
|
||||||
|
// Run task concurrently
|
||||||
|
watcher->setFuture(QtConcurrent::run(t, &Task::Run));
|
||||||
|
|
||||||
// Emit signal that a Task was added
|
// Emit signal that a Task was added
|
||||||
emit TaskAdded(t);
|
emit TaskAdded(t);
|
||||||
emit TaskListChanged();
|
emit TaskListChanged();
|
||||||
|
|
||||||
// Scan through queue and start any Tasks that can (including this one)
|
|
||||||
StartNextWaiting();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskManager::StartNextWaiting()
|
|
||||||
{
|
|
||||||
// If there are no tasks in the queue, there is nothing to be done
|
|
||||||
if (tasks_.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all threads are occupied, nothing to be done
|
|
||||||
if (active_thread_count_ == threads_.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a list of tasks that are waiting
|
|
||||||
QList<Task*> waiting_tasks;
|
|
||||||
foreach (const TaskContainer& task_info, tasks_) {
|
|
||||||
if (task_info.status == kWaiting) {
|
|
||||||
waiting_tasks.append(task_info.task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No tasks waiting to start
|
|
||||||
if (waiting_tasks.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For any inactive threads,
|
|
||||||
for (int i=0;i<threads_.size();i++) {
|
|
||||||
if (!threads_.at(i).active) {
|
|
||||||
// This thread is inactive and needs a new Task
|
|
||||||
Task* task = waiting_tasks.takeFirst();
|
|
||||||
|
|
||||||
task->moveToThread(threads_.at(i).thread);
|
|
||||||
|
|
||||||
threads_[i].active = true;
|
|
||||||
active_thread_count_++;
|
|
||||||
|
|
||||||
SetTaskStatus(task, kWorking);
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(task,
|
|
||||||
"Start",
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
if (active_thread_count_ == threads_.size() || waiting_tasks.isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskManager::DeleteTask(Task *t)
|
|
||||||
{
|
|
||||||
if (GetTaskStatus(t) == kWorking) {
|
|
||||||
// Send a signal to the task to cancel, it will likely continue to cancel in the background after it's removed
|
|
||||||
t->Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove instances of Task from queue
|
|
||||||
for (int i=0;i<tasks_.size();i++) {
|
|
||||||
if (tasks_.at(i).task == t) {
|
|
||||||
tasks_.removeAt(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit t->Removed();
|
|
||||||
emit TaskListChanged();
|
|
||||||
|
|
||||||
if (GetTaskStatus(t) != kWorking) {
|
|
||||||
// If the task isn't doing anything, we can simply delete it
|
|
||||||
t->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskManager::TaskFinished()
|
void TaskManager::TaskFinished()
|
||||||
{
|
{
|
||||||
Task* task_sender = static_cast<Task*>(sender());
|
QFutureWatcher<bool>* watcher = static_cast<QFutureWatcher<bool>*>(sender());
|
||||||
|
Task* t = tasks_.value(watcher);
|
||||||
|
|
||||||
// Set this thread's active value to false
|
tasks_.remove(watcher);
|
||||||
for (int i=0;i<threads_.size();i++) {
|
|
||||||
if (threads_.at(i).thread == task_sender->thread()) {
|
if (watcher->result()) {
|
||||||
threads_[i].active = false;
|
// Task completed successfully
|
||||||
}
|
emit TaskRemoved(t);
|
||||||
|
t->deleteLater();
|
||||||
|
} else {
|
||||||
|
// Task failed, keep it so the user can see the error message
|
||||||
|
emit TaskFailed(t);
|
||||||
|
failed_tasks_.append(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we can delete this task
|
watcher->deleteLater();
|
||||||
if (GetTaskStatus(task_sender) == kFinished) {
|
|
||||||
DeleteTask(task_sender);
|
|
||||||
} else if (GetTaskStatus(task_sender) == kError) {
|
|
||||||
// If this task has already been deleted, we'll free its memory now
|
|
||||||
bool was_deleted = true;
|
|
||||||
|
|
||||||
for (int i=0;i<tasks_.size();i++) {
|
|
||||||
if (tasks_.at(i).task == task_sender) {
|
|
||||||
was_deleted = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (was_deleted) {
|
|
||||||
task_sender->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrement the active thread count
|
|
||||||
active_thread_count_--;
|
|
||||||
|
|
||||||
// Signal that the task has finished
|
|
||||||
emit TaskListChanged();
|
emit TaskListChanged();
|
||||||
|
|
||||||
// Start any tasks that could start now
|
|
||||||
StartNextWaiting();
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskManager::TaskStatus TaskManager::GetTaskStatus(Task *t)
|
|
||||||
{
|
|
||||||
foreach (const TaskContainer& container, tasks_) {
|
|
||||||
if (container.task == t) {
|
|
||||||
return container.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return kError;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskManager::SetTaskStatus(Task *t, TaskStatus status)
|
|
||||||
{
|
|
||||||
for (int i=0;i<tasks_.size();i++) {
|
|
||||||
TaskContainer& cont = tasks_[i];
|
|
||||||
|
|
||||||
if (cont.task == t) {
|
|
||||||
cont.status = status;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskManager::TaskSucceeded()
|
|
||||||
{
|
|
||||||
SetTaskStatus(static_cast<Task*>(sender()), kFinished);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TaskManager::TaskFailed()
|
|
||||||
{
|
|
||||||
SetTaskStatus(static_cast<Task*>(sender()), kError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
+16
-67
@@ -21,6 +21,7 @@
|
|||||||
#ifndef TASKMANAGER_H
|
#ifndef TASKMANAGER_H
|
||||||
#define TASKMANAGER_H
|
#define TASKMANAGER_H
|
||||||
|
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QUndoCommand>
|
#include <QUndoCommand>
|
||||||
|
|
||||||
@@ -95,79 +96,31 @@ signals:
|
|||||||
*/
|
*/
|
||||||
void TaskListChanged();
|
void TaskListChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when a task is deleted
|
||||||
|
*/
|
||||||
|
void TaskRemoved(Task* t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal emitted when a task fails
|
||||||
|
*/
|
||||||
|
void TaskFailed(Task* t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* @brief The Status enum
|
|
||||||
*
|
|
||||||
* All states that a Task can be in. When subclassing, you don't need to set the Task's status as the base class
|
|
||||||
* does that automatically.
|
|
||||||
*/
|
|
||||||
enum TaskStatus {
|
|
||||||
/// This Task is yet to start
|
|
||||||
kWaiting,
|
|
||||||
|
|
||||||
/// This Task is currently running (see Action())
|
|
||||||
kWorking,
|
|
||||||
|
|
||||||
/// This Task has completed successfully
|
|
||||||
kFinished,
|
|
||||||
|
|
||||||
/// This Task failed and could not complete
|
|
||||||
kError
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TaskContainer {
|
|
||||||
Task* task;
|
|
||||||
TaskStatus status;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ThreadContainer {
|
|
||||||
QThread* thread;
|
|
||||||
bool active;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Scan through the task queue and start any Tasks that are able to start
|
|
||||||
*
|
|
||||||
* This function is run whenever a Task is added and whenever a Task finishes. It determines how many Tasks are
|
|
||||||
* currently running and therefore how many Tasks can be started (if any). It will then start ones that can.
|
|
||||||
*
|
|
||||||
* This function is aware of "dependency Tasks" and if a Task is waiting but has a dependency that hasn't finished,
|
|
||||||
* it will skip to the next one.
|
|
||||||
*
|
|
||||||
* Like AddTask, this function is NOT thread-safe and currently only intended to be run from the main thread.
|
|
||||||
*/
|
|
||||||
void StartNextWaiting();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Removes the Task from the queue and deletes it
|
|
||||||
*
|
|
||||||
* Recommended for use after a Task has completed or errorred.
|
|
||||||
*
|
|
||||||
* @param t
|
|
||||||
*
|
|
||||||
* Task to delete
|
|
||||||
*/
|
|
||||||
void DeleteTask(Task* t);
|
|
||||||
|
|
||||||
TaskStatus GetTaskStatus(Task* t);
|
|
||||||
|
|
||||||
void SetTaskStatus(Task* t, TaskStatus status);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Internal task array
|
* @brief Internal task array
|
||||||
*/
|
*/
|
||||||
QVector<TaskContainer> tasks_;
|
QHash<QFutureWatcher<bool>*, Task*> tasks_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Background threads to run tasks on
|
* @brief Internal list of failed tasks
|
||||||
*/
|
*/
|
||||||
QVector<ThreadContainer> threads_;
|
QLinkedList<Task*> failed_tasks_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Value for how many threads are currently active
|
* @brief Task thread pool
|
||||||
*/
|
*/
|
||||||
int active_thread_count_;
|
QThreadPool thread_pool_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief TaskManager singleton instance
|
* @brief TaskManager singleton instance
|
||||||
@@ -175,10 +128,6 @@ private:
|
|||||||
static TaskManager* instance_;
|
static TaskManager* instance_;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void TaskSucceeded();
|
|
||||||
|
|
||||||
void TaskFailed();
|
|
||||||
|
|
||||||
void TaskFinished();
|
void TaskFinished();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,20 @@ TaskView::TaskView(QWidget* parent) :
|
|||||||
void TaskView::AddTask(Task *t)
|
void TaskView::AddTask(Task *t)
|
||||||
{
|
{
|
||||||
// Create TaskViewItem (UI representation of a Task) and connect it
|
// Create TaskViewItem (UI representation of a Task) and connect it
|
||||||
layout_->insertWidget(layout_->count()-1, new TaskViewItem(t));
|
TaskViewItem* item = new TaskViewItem(t);
|
||||||
|
items_.insert(t, item);
|
||||||
|
layout_->insertWidget(layout_->count()-1, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskView::TaskFailed(Task *t)
|
||||||
|
{
|
||||||
|
items_.value(t)->Failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskView::RemoveTask(Task *t)
|
||||||
|
{
|
||||||
|
items_.value(t)->deleteLater();
|
||||||
|
items_.remove(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -50,9 +50,17 @@ public slots:
|
|||||||
*/
|
*/
|
||||||
void AddTask(Task* t);
|
void AddTask(Task* t);
|
||||||
|
|
||||||
|
void TaskFailed(Task* t);
|
||||||
|
|
||||||
|
void RemoveTask(Task* t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* central_widget_;
|
QWidget* central_widget_;
|
||||||
|
|
||||||
QVBoxLayout* layout_;
|
QVBoxLayout* layout_;
|
||||||
|
|
||||||
|
QHash<Task*, TaskViewItem*> items_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -61,8 +61,12 @@ TaskViewItem::TaskViewItem(Task* task, QWidget *parent) :
|
|||||||
|
|
||||||
// Connect to the task
|
// Connect to the task
|
||||||
connect(task_, &Task::ProgressChanged, progress_bar_, &QProgressBar::setValue);
|
connect(task_, &Task::ProgressChanged, progress_bar_, &QProgressBar::setValue);
|
||||||
connect(task_, &Task::Removed, this, &TaskViewItem::deleteLater);
|
|
||||||
connect(cancel_btn_, &QPushButton::clicked, task_, &Task::Cancel, Qt::DirectConnection);
|
connect(cancel_btn_, &QPushButton::clicked, task_, &Task::Cancel, Qt::DirectConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskViewItem::Failed()
|
||||||
|
{
|
||||||
|
task_status_lbl_->setText(task_->GetError());
|
||||||
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ class TaskViewItem : public QFrame
|
|||||||
public:
|
public:
|
||||||
TaskViewItem(Task *task, QWidget* parent = nullptr);
|
TaskViewItem(Task *task, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void Failed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel* task_name_lbl_;
|
QLabel* task_name_lbl_;
|
||||||
QProgressBar* progress_bar_;
|
QProgressBar* progress_bar_;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include "dialog/actionsearch/actionsearch.h"
|
#include "dialog/actionsearch/actionsearch.h"
|
||||||
#include "dialog/task/task.h"
|
#include "dialog/task/task.h"
|
||||||
#include "panel/panelmanager.h"
|
#include "panel/panelmanager.h"
|
||||||
#include "task/cache/cache.h"
|
|
||||||
#include "tool/tool.h"
|
#include "tool/tool.h"
|
||||||
#include "ui/style/style.h"
|
#include "ui/style/style.h"
|
||||||
#include "undo/undostack.h"
|
#include "undo/undostack.h"
|
||||||
@@ -613,21 +612,12 @@ void MainMenu::OpenRecentItemTriggered()
|
|||||||
|
|
||||||
void MainMenu::SequenceCacheTriggered()
|
void MainMenu::SequenceCacheTriggered()
|
||||||
{
|
{
|
||||||
TimeBasedPanel* p = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
Core::instance()->CacheActiveSequence(false);
|
||||||
|
|
||||||
if (p && p->GetConnectedViewer()) {
|
|
||||||
// FIXME: Hardcoded divider...
|
|
||||||
// FIXME: Consider preventing caching the footage viewer
|
|
||||||
CacheTask* task = new CacheTask(p->GetConnectedViewer(), 2, false);
|
|
||||||
|
|
||||||
TaskDialog* dialog = new TaskDialog(task, tr("Caching Sequence"), parentWidget());
|
|
||||||
dialog->open();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenu::SequenceCacheInOutTriggered()
|
void MainMenu::SequenceCacheInOutTriggered()
|
||||||
{
|
{
|
||||||
qDebug() << "STUB";
|
Core::instance()->CacheActiveSequence(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenu::Retranslate()
|
void MainMenu::Retranslate()
|
||||||
|
|||||||
Reference in New Issue
Block a user