From 1c3c8cd9b271e5c510447bd01dc7dcfbf19252ac Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 20 Apr 2020 02:37:10 +1000 Subject: [PATCH] began work on headless export path --- app/CMakeLists.txt | 1 + app/cli/CMakeLists.txt | 23 ++++ app/cli/cliprogress/CMakeLists.txt | 22 ++++ app/cli/cliprogress/cliprogressdialog.cpp | 105 +++++++++++++++ app/cli/cliprogress/cliprogressdialog.h | 52 ++++++++ app/cli/clitask/CMakeLists.txt | 22 ++++ app/cli/clitask/clitaskdialog.cpp | 33 +++++ app/cli/clitask/clitaskdialog.h | 41 ++++++ app/core.cpp | 100 +++++++++++---- app/core.h | 7 +- app/dialog/export/export.cpp | 150 ++++++++-------------- app/dialog/export/export.h | 4 +- app/dialog/export/exportvideotab.cpp | 7 +- app/dialog/export/exportvideotab.h | 6 - app/main.cpp | 19 ++- app/render/backend/CMakeLists.txt | 2 + app/render/backend/exporter.cpp | 95 +++++++++----- app/render/backend/exporter.h | 46 +++---- app/render/backend/exportparams.cpp | 89 +++++++++++++ app/render/backend/exportparams.h | 70 ++++++++++ 20 files changed, 695 insertions(+), 199 deletions(-) create mode 100644 app/cli/CMakeLists.txt create mode 100644 app/cli/cliprogress/CMakeLists.txt create mode 100644 app/cli/cliprogress/cliprogressdialog.cpp create mode 100644 app/cli/cliprogress/cliprogressdialog.h create mode 100644 app/cli/clitask/CMakeLists.txt create mode 100644 app/cli/clitask/clitaskdialog.cpp create mode 100644 app/cli/clitask/clitaskdialog.h create mode 100644 app/render/backend/exportparams.cpp create mode 100644 app/render/backend/exportparams.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 70a26bcad..24f7d9116 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -30,6 +30,7 @@ if (WIN32) endif() add_subdirectory(audio) +add_subdirectory(cli) add_subdirectory(codec) add_subdirectory(common) add_subdirectory(config) diff --git a/app/cli/CMakeLists.txt b/app/cli/CMakeLists.txt new file mode 100644 index 000000000..6fe09bdc8 --- /dev/null +++ b/app/cli/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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 . + +add_subdirectory(cliprogress) +add_subdirectory(clitask) + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + PARENT_SCOPE +) diff --git a/app/cli/cliprogress/CMakeLists.txt b/app/cli/cliprogress/CMakeLists.txt new file mode 100644 index 000000000..27a57775c --- /dev/null +++ b/app/cli/cliprogress/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + cli/cliprogress/cliprogressdialog.h + cli/cliprogress/cliprogressdialog.cpp + PARENT_SCOPE +) diff --git a/app/cli/cliprogress/cliprogressdialog.cpp b/app/cli/cliprogress/cliprogressdialog.cpp new file mode 100644 index 000000000..fa846c1ff --- /dev/null +++ b/app/cli/cliprogress/cliprogressdialog.cpp @@ -0,0 +1,105 @@ +/*** + + 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 . + +***/ + +#include "cliprogressdialog.h" + +#include + +OLIVE_NAMESPACE_ENTER + +CLIProgressDialog::CLIProgressDialog(const QString& title, QObject *parent) : + QObject(parent), + title_(title), + progress_(0), + drawn_(false) +{ +} + +void CLIProgressDialog::Update() +{ + if (drawn_) { + // We've been here before, do a carriage return back to the start of the terminal line + std::cout << "\r"; + } else { + drawn_ = true; + } + + // FIXME: Get real column count + int columns = 80; + + int title_columns = columns / 2 - 1; + + // Print "title" text + QString sized_title = title_; + + if (title_.size() > title_columns) { + sized_title = title_.left(title_columns - 3).append(QStringLiteral("...")); + } else { + sized_title = title_; + } + + std::cout << sized_title.toUtf8().constData(); + + // Pad out the rest of the title area if necessary + for (int i=sized_title.size(); i. + +***/ + +#ifndef CLIPROGRESSDIALOG_H +#define CLIPROGRESSDIALOG_H + +#include +#include + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +class CLIProgressDialog : public QObject +{ +public: + CLIProgressDialog(const QString &title, QObject* parent = nullptr); + +public slots: + void SetProgress(int p); + +private: + void Update(); + + QString title_; + + int progress_; + + bool drawn_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // CLIPROGRESSDIALOG_H diff --git a/app/cli/clitask/CMakeLists.txt b/app/cli/clitask/CMakeLists.txt new file mode 100644 index 000000000..d3a84091d --- /dev/null +++ b/app/cli/clitask/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + cli/clitask/clitaskdialog.h + cli/clitask/clitaskdialog.cpp + PARENT_SCOPE +) diff --git a/app/cli/clitask/clitaskdialog.cpp b/app/cli/clitask/clitaskdialog.cpp new file mode 100644 index 000000000..e68ac0cd7 --- /dev/null +++ b/app/cli/clitask/clitaskdialog.cpp @@ -0,0 +1,33 @@ +/*** + + 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 . + +***/ + +#include "clitaskdialog.h" + +OLIVE_NAMESPACE_ENTER + +CLITaskDialog::CLITaskDialog(Task *task, QObject* parent) : + CLIProgressDialog(task->GetTitle(), parent) +{ + //connect(task, &Task::ProgressChanged, this, &CLITaskDialog::SetProgress); + + task->Start(); +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/cli/clitask/clitaskdialog.h b/app/cli/clitask/clitaskdialog.h new file mode 100644 index 000000000..313e3aadc --- /dev/null +++ b/app/cli/clitask/clitaskdialog.h @@ -0,0 +1,41 @@ +/*** + + 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 . + +***/ + +#ifndef CLITASKDIALOG_H +#define CLITASKDIALOG_H + +#include "cli/cliprogress/cliprogressdialog.h" +#include "task/task.h" + +OLIVE_NAMESPACE_ENTER + +class CLITaskDialog : public CLIProgressDialog +{ +public: + CLITaskDialog(Task *task, QObject* parent = nullptr); + +private: + Task* task_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // CLITASKDIALOG_H diff --git a/app/core.cpp b/app/core.cpp index 3cc7ede0b..c634603b7 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -31,6 +31,7 @@ #include #include "audio/audiomanager.h" +#include "cli/clitask/clitaskdialog.h" #include "common/filefunctions.h" #include "common/xmlutils.h" #include "config/config.h" @@ -67,7 +68,8 @@ Core::Core() : main_window_(nullptr), tool_(Tool::kPointer), addable_object_(Tool::kAddableEmpty), - snapping_(true) + snapping_(true), + gui_active_(false) { } @@ -76,7 +78,7 @@ Core *Core::instance() return &instance_; } -void Core::Start() +bool Core::Start() { // // Parse command line arguments @@ -97,6 +99,10 @@ void Core::Start() QCommandLineOption fullscreen_option({"f", "fullscreen"}, tr("Start in full screen mode")); parser.addOption(fullscreen_option); + // Create headless export option + QCommandLineOption headless_export_option({"x", "export"}, tr("Export project from command line")); + parser.addOption(headless_export_option); + // Parse options parser.process(*app); @@ -116,6 +122,12 @@ void Core::Start() // Set up the index manager for renderers IndexManager::CreateInstance(); + // Initialize disk service + DiskManager::CreateInstance(); + + // Initialize task manager + TaskManager::CreateInstance(); + // Reset config (Config sets to default on construction already, but we do it again here as a workaround that fixes // the fact that some of the config paths set by default rely on the app name having been set (in main()) Config::Current().SetDefaults(); @@ -139,26 +151,56 @@ void Core::Start() // - // Start GUI (FIXME CLI mode) + // Start application // - StartGUI(parser.isSet(fullscreen_option)); + qInfo() << "Using Qt version:" << qVersion(); - // Load startup project - if (!startup_project_.isEmpty() && !QFileInfo::exists(startup_project_)) { - QMessageBox::warning(main_window(), - tr("Failed to open startup file"), - tr("The project \"%1\" doesn't exist. A new project will be started instead.").arg(startup_project_), - QMessageBox::Ok); + gui_active_ = !parser.isSet(headless_export_option); - startup_project_.clear(); - } + if (gui_active_) { + + // Start GUI + StartGUI(parser.isSet(fullscreen_option)); + + // Load startup project + if (!startup_project_.isEmpty() && !QFileInfo::exists(startup_project_)) { + QMessageBox::warning(main_window(), + tr("Failed to open startup file"), + tr("The project \"%1\" doesn't exist. A new project will be started instead.").arg(startup_project_), + QMessageBox::Ok); + + startup_project_.clear(); + } + + if (startup_project_.isEmpty()) { + // If no load project is set, create a new one on open + CreateNewProject(); + } else { + OpenProjectInternal(startup_project_); + } + + return true; - if (startup_project_.isEmpty()) { - // If no load project is set, create a new one on open - CreateNewProject(); } else { - OpenProjectInternal(startup_project_); + + if (parser.isSet(headless_export_option)) { + + if (startup_project_.isEmpty()) { + qCritical().noquote() << tr("You must specify a project file to export"); + } else { + OpenProjectInternal(startup_project_); + + qDebug() << "Ready for exporting!"; + + return true; + } + + } + + // Error fallback + return false; + } } @@ -525,12 +567,6 @@ void Core::StartGUI(bool full_screen) // Initialize audio service AudioManager::CreateInstance(); - // Initialize disk service - DiskManager::CreateInstance(); - - // Initialize task manager - TaskManager::CreateInstance(); - // Initialize pixel service PixelFormat::CreateInstance(); @@ -843,12 +879,22 @@ void Core::OpenProjectInternal(const QString &filename) ProjectLoadManager* plm = new ProjectLoadManager(filename); - // 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); + if (gui_active_) { - TaskDialog* task_dialog = new TaskDialog(plm, tr("Load Project"), main_window()); - task_dialog->open(); + // 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()); + task_dialog->open(); + + } else { + + connect(plm, &ProjectLoadManager::ProjectLoaded, this, &Core::AddOpenProject); + + CLITaskDialog task_dialog(plm); + + } } int Core::CountFilesInFileList(const QFileInfoList &filenames) diff --git a/app/core.h b/app/core.h index 8c5f8985d..20ef0a4ea 100644 --- a/app/core.h +++ b/app/core.h @@ -71,7 +71,7 @@ public: * * Main application launcher. Parses command line arguments and constructs main window (if entering a GUI mode). */ - void Start(); + bool Start(); /** * @brief Stop Olive Core @@ -464,6 +464,11 @@ private: */ QStringList recent_projects_; + /** + * @brief Internal variable for whether the GUI is active + */ + bool gui_active_; + /** * @brief Static singleton core instance */ diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index 3eb5656c1..6bb591b7c 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -33,6 +33,7 @@ #include "core.h" #include "project/item/sequence/sequence.h" #include "project/project.h" +#include "render/backend/exportparams.h" #include "render/pixelformat.h" #include "ui/icons/icons.h" #include "window/mainwindow/mainwindow.h" @@ -238,9 +239,9 @@ void ExportDialog::accept() { if (!video_enabled_->isChecked() && !audio_enabled_->isChecked()) { QMessageBox::critical(this, - tr("Invalid parameters"), - tr("Both video and audio are disabled. There's nothing to export."), - QMessageBox::Ok); + tr("Invalid parameters"), + tr("Both video and audio are disabled. There's nothing to export."), + QMessageBox::Ok); return; } @@ -282,70 +283,8 @@ void ExportDialog::accept() return; } - QMatrix4x4 transform; - - int dest_width = static_cast(video_tab_->width_slider()->GetValue()); - int dest_height = static_cast(video_tab_->height_slider()->GetValue()); - - if (video_tab_->scaling_method_combobox()->isEnabled()) { - int source_width = viewer_node_->video_params().width(); - int source_height = viewer_node_->video_params().height(); - - transform = GenerateMatrix(static_cast(video_tab_->scaling_method_combobox()->currentData().toInt()), - source_width, - source_height, - dest_width, - dest_height); - } - - RenderMode::Mode render_mode = RenderMode::kOnline; - - VideoRenderingParams video_render_params(dest_width, - dest_height, - video_tab_->frame_rate().flipped(), - PixelFormat::instance()->GetConfiguredFormatForMode(render_mode), - render_mode); - - AudioRenderingParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(), - audio_tab_->channel_layout_combobox()->currentData().toULongLong(), - SampleFormat::GetConfiguredFormatForMode(render_mode)); - - ColorProcessorPtr color_processor = ColorProcessor::Create(color_manager_, - color_manager_->GetReferenceColorSpace(), - video_tab_->CurrentOCIODisplay(), - video_tab_->CurrentOCIOView(), - video_tab_->CurrentOCIOLook()); - - // Set up encoder - EncodingParams encoding_params; - encoding_params.SetFilename(filename_edit_->text()); - encoding_params.SetExportLength(viewer_node_->Length()); - - if (video_enabled_->isChecked()) { - const ExportCodec& video_codec = codecs_.at(video_tab_->codec_combobox()->currentData().toInt()); - encoding_params.EnableVideo(video_render_params, - video_codec.id()); - - video_tab_->GetCodecSection()->AddOpts(&encoding_params); - } - - if (audio_enabled_->isChecked()) { - const ExportCodec& audio_codec = codecs_.at(audio_tab_->codec_combobox()->currentData().toInt()); - encoding_params.EnableAudio(audio_render_params, - audio_codec.id()); - } - - Encoder* encoder = Encoder::CreateFromID("ffmpeg", encoding_params); - - exporter_ = new Exporter(viewer_node_, encoder); - - if (video_enabled_->isChecked()) { - exporter_->EnableVideo(video_render_params, transform, color_processor); - } - - if (audio_enabled_->isChecked()) { - exporter_->EnableAudio(audio_render_params); - } + // Set up export parameters + exporter_ = new Exporter(viewer_node_, color_manager_, GenerateParams()); connect(exporter_, &Exporter::ExportEnded, this, &ExportDialog::ExporterIsDone); connect(exporter_, &Exporter::ProgressChanged, this, &ExportDialog::ProgressUpdated); @@ -556,30 +495,6 @@ void ExportDialog::SetDefaultFilename() filename_edit_->setText(file_location); } -QMatrix4x4 ExportDialog::GenerateMatrix(ExportVideoTab::ScalingMethod method, int source_width, int source_height, int dest_width, int dest_height) -{ - QMatrix4x4 preview_matrix; - - if (method == ExportVideoTab::kStretch) { - return preview_matrix; - } - - float export_ar = static_cast(dest_width) / static_cast(dest_height); - float source_ar = static_cast(source_width) / static_cast(source_height); - - if (qFuzzyCompare(export_ar, source_ar)) { - return preview_matrix; - } - - if ((export_ar > source_ar) == (method == ExportVideoTab::kFit)) { - preview_matrix.scale(source_ar / export_ar, 1.0F); - } else { - preview_matrix.scale(1.0F, export_ar / source_ar); - } - - return preview_matrix; -} - void ExportDialog::SetUIElementsEnabled(bool enabled) { preferences_area_->setEnabled(enabled); @@ -589,6 +504,49 @@ void ExportDialog::SetUIElementsEnabled(bool enabled) elapsed_label_->setEnabled(!enabled); } +ExportParams ExportDialog::GenerateParams() const +{ + RenderMode::Mode render_mode = RenderMode::kOnline; + + VideoRenderingParams video_render_params(static_cast(video_tab_->width_slider()->GetValue()), + static_cast(video_tab_->height_slider()->GetValue()), + video_tab_->frame_rate().flipped(), + PixelFormat::instance()->GetConfiguredFormatForMode(render_mode), + render_mode); + + AudioRenderingParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(), + audio_tab_->channel_layout_combobox()->currentData().toULongLong(), + SampleFormat::GetConfiguredFormatForMode(render_mode)); + + ExportParams params; + params.SetFilename(filename_edit_->text()); + params.SetExportLength(viewer_node_->Length()); + + if (video_tab_->scaling_method_combobox()->isEnabled()) { + params.set_video_scaling_method(static_cast(video_tab_->scaling_method_combobox()->currentData().toInt())); + } + + if (video_enabled_->isChecked()) { + const ExportCodec& video_codec = codecs_.at(video_tab_->codec_combobox()->currentData().toInt()); + params.EnableVideo(video_render_params, + video_codec.id()); + + video_tab_->GetCodecSection()->AddOpts(¶ms); + + params.set_ocio_output(video_tab_->CurrentOCIODisplay(), + video_tab_->CurrentOCIOView(), + video_tab_->CurrentOCIOLook()); + } + + if (audio_enabled_->isChecked()) { + const ExportCodec& audio_codec = codecs_.at(audio_tab_->codec_combobox()->currentData().toInt()); + params.EnableAudio(audio_render_params, + audio_codec.id()); + } + + return params; +} + void ExportDialog::UpdateTimeLabels() { qint64 elapsed, remaining; @@ -641,11 +599,11 @@ void ExportDialog::UpdateViewerDimensions() preview_viewer_->SetOverrideSize(static_cast(video_tab_->width_slider()->GetValue()), static_cast(video_tab_->height_slider()->GetValue())); - preview_viewer_->SetMatrix(GenerateMatrix(static_cast(video_tab_->scaling_method_combobox()->currentData().toInt()), - viewer_node_->video_params().width(), - viewer_node_->video_params().height(), - static_cast(video_tab_->width_slider()->GetValue()), - static_cast(video_tab_->height_slider()->GetValue()))); + preview_viewer_->SetMatrix(Exporter::GenerateMatrix(static_cast(video_tab_->scaling_method_combobox()->currentData().toInt()), + viewer_node_->video_params().width(), + viewer_node_->video_params().height(), + static_cast(video_tab_->width_slider()->GetValue()), + static_cast(video_tab_->height_slider()->GetValue()))); } void ExportDialog::ExporterIsDone() diff --git a/app/dialog/export/export.h b/app/dialog/export/export.h index 23fe741c8..82d610ced 100644 --- a/app/dialog/export/export.h +++ b/app/dialog/export/export.h @@ -53,10 +53,10 @@ private: void LoadPresets(); void SetDefaultFilename(); - QMatrix4x4 GenerateMatrix(ExportVideoTab::ScalingMethod method, int source_width, int source_height, int dest_width, int height); - void SetUIElementsEnabled(bool enabled); + ExportParams GenerateParams() const; + static QString TimeToString(int64_t ms); ViewerOutput* viewer_node_; diff --git a/app/dialog/export/exportvideotab.cpp b/app/dialog/export/exportvideotab.cpp index a52c41bd1..ba3031f82 100644 --- a/app/dialog/export/exportvideotab.cpp +++ b/app/dialog/export/exportvideotab.cpp @@ -26,6 +26,7 @@ #include #include "core.h" +#include "render/backend/exportparams.h" #include "render/colormanager.h" OLIVE_NAMESPACE_ENTER @@ -150,9 +151,9 @@ QWidget* ExportVideoTab::SetupResolutionSection() scaling_method_combobox_ = new QComboBox(); scaling_method_combobox_->setEnabled(false); - scaling_method_combobox_->addItem(tr("Fit"), kFit); - scaling_method_combobox_->addItem(tr("Stretch"), kStretch); - scaling_method_combobox_->addItem(tr("Crop"), kCrop); + scaling_method_combobox_->addItem(tr("Fit"), ExportParams::kFit); + scaling_method_combobox_->addItem(tr("Stretch"), ExportParams::kStretch); + scaling_method_combobox_->addItem(tr("Crop"), ExportParams::kCrop); layout->addWidget(scaling_method_combobox_, row, 1); // Automatically enable/disable the scaling method depending on maintain aspect ratio diff --git a/app/dialog/export/exportvideotab.h b/app/dialog/export/exportvideotab.h index a55ab59fe..288c3fcb8 100644 --- a/app/dialog/export/exportvideotab.h +++ b/app/dialog/export/exportvideotab.h @@ -40,12 +40,6 @@ class ExportVideoTab : public QWidget public: ExportVideoTab(ColorManager* color_manager, QWidget* parent = nullptr); - enum ScalingMethod { - kFit, - kStretch, - kCrop - }; - QComboBox* codec_combobox() const; IntegerSlider* width_slider() const; diff --git a/app/main.cpp b/app/main.cpp index f7f237326..1b4b6e3a7 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -66,8 +66,6 @@ int main(int argc, char *argv[]) { app_version.append(GITHASH); #endif - qInfo() << "Using Qt version:" << qVersion(); - QCoreApplication::setApplicationVersion(app_version); #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) @@ -85,11 +83,20 @@ int main(int argc, char *argv[]) { avfilter_register_all(); #endif - // Start core - OLIVE_NAMESPACE::Core::instance()->Start(); + int exit_code; - // Run application loop and receive exit code - int exit_code = a.exec(); + // Start core + if (OLIVE_NAMESPACE::Core::instance()->Start()) { + + // Run application loop and receive exit code + exit_code = a.exec(); + + } else { + + // Core failed to start, exit now + exit_code = 1; + + } // Clear core memory OLIVE_NAMESPACE::Core::instance()->Stop(); diff --git a/app/render/backend/CMakeLists.txt b/app/render/backend/CMakeLists.txt index 7309a482c..515f1b1e9 100644 --- a/app/render/backend/CMakeLists.txt +++ b/app/render/backend/CMakeLists.txt @@ -22,6 +22,8 @@ set(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.cpp diff --git a/app/render/backend/exporter.cpp b/app/render/backend/exporter.cpp index 75510b5e5..18917b39d 100644 --- a/app/render/backend/exporter.cpp +++ b/app/render/backend/exporter.cpp @@ -27,46 +27,52 @@ OLIVE_NAMESPACE_ENTER -Exporter::Exporter(ViewerOutput* viewer, - Encoder *encoder, +Exporter::Exporter(ViewerOutput *viewer_node, + ColorManager *color_manager, + const ExportParams& params, QObject* parent) : QObject(parent), + viewer_node_(viewer_node), + params_(params), video_backend_(nullptr), audio_backend_(nullptr), - viewer_node_(viewer), - video_done_(true), - audio_done_(true), - encoder_(encoder), 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); - export_range_ = TimeRange(0, viewer_node_->Length()); -} + if (params_.has_custom_range()) { + export_range_ = params_.custom_range(); + } else { + export_range_ = TimeRange(0, viewer_node_->Length()); + } -void Exporter::EnableVideo(const VideoRenderingParams &video_params, const QMatrix4x4 &transform, ColorProcessorPtr color_processor) -{ - video_params_ = video_params; - transform_ = transform; - color_processor_ = color_processor; + if (params_.video_enabled()) { - video_done_ = false; -} + // 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()); + } -void Exporter::EnableAudio(const AudioRenderingParams &audio_params) -{ - audio_params_ = audio_params; - - audio_done_ = false; -} - -void Exporter::OverrideExportRange(const TimeRange &range) -{ - export_range_ = range; + // Create color processor + color_processor_ = ColorProcessor::Create(color_manager, + color_manager->GetReferenceColorSpace(), + params.ocio_display(), + params.ocio_view(), + params.ocio_look()); + } } bool Exporter::GetExportStatus() const @@ -110,9 +116,9 @@ void Exporter::StartExporting() video_backend_->SetViewerNode(viewer_node_); video_backend_->SetParameters(VideoRenderingParams(viewer_node_->video_params().width(), viewer_node_->video_params().height(), - video_params_.time_base(), - video_params_.format(), - video_params_.mode())); + params_.video_params().time_base(), + params_.video_params().format(), + params_.video_params().mode())); waiting_for_frame_ = 0; } @@ -121,7 +127,7 @@ void Exporter::StartExporting() audio_backend_ = new AudioBackend(); audio_backend_->SetViewerNode(viewer_node_); - audio_backend_->SetParameters(audio_params_); + audio_backend_->SetParameters(params_.audio_params()); } // Open encoder and wait for result @@ -190,7 +196,7 @@ void Exporter::EncodeFrame() Qt::QueuedConnection, OLIVE_NS_ARG(FramePtr, frame)); - waiting_for_frame_ += video_params_.time_base(); + waiting_for_frame_ += params_.video_params().time_base(); // Calculate progress emit ProgressChanged(waiting_for_frame_.toDouble() / viewer_node_->Length().toDouble()); @@ -204,6 +210,30 @@ void Exporter::EncodeFrame() } } +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(dest_width) / static_cast(dest_height); + float source_ar = static_cast(source_width) / static_cast(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; +} + void Exporter::FrameRendered(const rational &time, FramePtr value) { debug_timer_.stop(); @@ -261,14 +291,14 @@ void Exporter::EncoderOpenedSuccessfully() video_backend_->SetOperatingMode(VideoRenderWorker::kHashOnly); connect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete); - video_backend_->InvalidateCache(TimeRange(0, viewer_node_->Length())); + video_backend_->InvalidateCache(export_range_); } 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(TimeRange(0, viewer_node_->Length())); + audio_backend_->InvalidateCache(export_range_); } } @@ -297,7 +327,6 @@ void Exporter::VideoHashesComplete() video_backend_->SetOperatingMode(VideoRenderWorker::kRenderOnly); video_backend_->SetOnlySignalLastFrameRequested(false); - // FIXME: Exporting is now broken because of this connect(video_backend_, &VideoRenderBackend::GeneratedFrame, this, &Exporter::FrameRendered); foreach (const TimeRange& range, ranges) { diff --git a/app/render/backend/exporter.h b/app/render/backend/exporter.h index 581e42f5d..3aaec6986 100644 --- a/app/render/backend/exporter.h +++ b/app/render/backend/exporter.h @@ -29,6 +29,7 @@ #include "codec/encoder.h" #include "node/output/viewer/viewer.h" #include "render/backend/audiorenderbackend.h" +#include "render/backend/exportparams.h" #include "render/backend/videorenderbackend.h" #include "render/colorprocessor.h" @@ -38,20 +39,18 @@ class Exporter : public QObject { Q_OBJECT public: - Exporter(ViewerOutput* viewer, - Encoder* encoder, + Exporter(ViewerOutput* viewer_node, + ColorManager* color_manager, + const ExportParams& params, QObject* parent = nullptr); - void EnableVideo(const VideoRenderingParams& video_params, const QMatrix4x4& transform, ColorProcessorPtr color_processor); - void EnableAudio(const AudioRenderingParams& audio_params); - - void OverrideExportRange(const TimeRange& range); - 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(); @@ -63,17 +62,23 @@ signals: protected: void SetExportMessage(const QString& s); +private: + void ExportSucceeded(); + + void ExportStopped(); + + void EncodeFrame(); + + ViewerOutput* viewer_node_; + + ColorProcessorPtr color_processor_; + + ExportParams params_; + // Renderers VideoRenderBackend* video_backend_; AudioRenderBackend* audio_backend_; - // Viewer node - ViewerOutput* viewer_node_; - - // Export parameters - VideoRenderingParams video_params_; - AudioRenderingParams audio_params_; - // Export transform QMatrix4x4 transform_; @@ -81,23 +86,14 @@ protected: bool audio_done_; - TimeRange export_range_; - -private: - void ExportSucceeded(); - - void ExportStopped(); - - void EncodeFrame(); - - ColorProcessorPtr color_processor_; - Encoder* encoder_; bool export_status_; QString export_msg_; + TimeRange export_range_; + rational waiting_for_frame_; QHash cached_frames_; diff --git a/app/render/backend/exportparams.cpp b/app/render/backend/exportparams.cpp new file mode 100644 index 000000000..671247ada --- /dev/null +++ b/app/render/backend/exportparams.cpp @@ -0,0 +1,89 @@ +/*** + + 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 . + +***/ + +#include "exportparams.h" + +OLIVE_NAMESPACE_ENTER + +ExportParams::ExportParams() : + video_scaling_method_(kStretch), + has_custom_range_(false) +{ +} + +const QString &ExportParams::encoder() const +{ + return encoder_id_; +} + +void ExportParams::set_encoder(const QString &id) +{ + encoder_id_ = id; +} + +bool ExportParams::has_custom_range() const +{ + return has_custom_range_; +} + +const TimeRange &ExportParams::custom_range() const +{ + return custom_range_; +} + +void ExportParams::set_custom_range(const TimeRange &custom_range) +{ + has_custom_range_ = true; + custom_range_ = custom_range; +} + +const ExportParams::VideoScalingMethod &ExportParams::video_scaling_method() const +{ + return video_scaling_method_; +} + +void ExportParams::set_video_scaling_method(const ExportParams::VideoScalingMethod &video_scaling_method) +{ + video_scaling_method_ = video_scaling_method; +} + +void ExportParams::set_ocio_output(const QString &display, const QString &view, const QString &look) +{ + ocio_display_ = display; + ocio_view_ = view; + ocio_look_ = look; +} + +const QString &ExportParams::ocio_display() const +{ + return ocio_display_; +} + +const QString &ExportParams::ocio_view() const +{ + return ocio_view_; +} + +const QString &ExportParams::ocio_look() const +{ + return ocio_look_; +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/render/backend/exportparams.h b/app/render/backend/exportparams.h new file mode 100644 index 000000000..87b9204c0 --- /dev/null +++ b/app/render/backend/exportparams.h @@ -0,0 +1,70 @@ +/*** + + 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 . + +***/ + +#ifndef EXPORTPARAMS_H +#define EXPORTPARAMS_H + +#include "codec/encoder.h" +#include "node/output/viewer/viewer.h" + +OLIVE_NAMESPACE_ENTER + +class ExportParams : public EncodingParams { +public: + enum VideoScalingMethod { + kFit, + kStretch, + kCrop + }; + + ExportParams(); + + const QString& encoder() const; + void set_encoder(const QString& id); + + bool has_custom_range() const; + const TimeRange& custom_range() const; + void set_custom_range(const TimeRange& custom_range); + + const VideoScalingMethod& video_scaling_method() const; + void set_video_scaling_method(const VideoScalingMethod& video_scaling_method); + + const QString& ocio_display() const; + const QString& ocio_view() const; + const QString& ocio_look() const; + void set_ocio_output(const QString& display, const QString& view, const QString& look); + +private: + QString encoder_id_; + + VideoScalingMethod video_scaling_method_; + + bool has_custom_range_; + TimeRange custom_range_; + + QString ocio_display_; + QString ocio_view_; + QString ocio_look_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // EXPORTPARAMS_H