exportdialog: implemented basic preset saving/loading

This may not work perfectly yet, but we can build upon it
This commit is contained in:
itsmattkc
2022-08-12 09:16:59 -07:00
parent 402c4e1694
commit 5853c114c2
7 changed files with 447 additions and 33 deletions
+184 -1
View File
@@ -23,6 +23,7 @@
#include <QFile>
#include "common/timecodefunctions.h"
#include "common/xmlutils.h"
#include "ffmpeg/ffmpegencoder.h"
#include "oiio/oiioencoder.h"
@@ -99,6 +100,17 @@ EncodingParams::EncodingParams() :
{
}
QDir EncodingParams::GetPresetPath()
{
return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("exportpresets"));
}
QStringList EncodingParams::GetListOfPresets()
{
QDir d = EncodingParams::GetPresetPath();
return d.entryList(QDir::Files);
}
void EncodingParams::EnableVideo(const VideoParams &video_params, const ExportCodec::Codec &vcodec)
{
video_enabled_ = true;
@@ -142,9 +154,49 @@ void EncodingParams::DisableSubtitles()
subtitles_enabled_ = false;
}
bool EncodingParams::Load(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("export")) {
int version = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("version")) {
version = attr.value().toInt();
}
}
switch (version) {
case 1:
return LoadV1(reader);
}
} else {
reader->skipCurrentElement();
}
}
return false;
}
bool EncodingParams::Load(QIODevice *device)
{
QXmlStreamReader reader(device);
return Load(&reader);
}
void EncodingParams::Save(QIODevice *device) const
{
QXmlStreamWriter writer(device);
Save(&writer);
}
void EncodingParams::Save(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
writer->writeStartDocument();
writer->writeStartElement(QStringLiteral("export"));
writer->writeAttribute(QStringLiteral("version"), QString::number(kEncoderParamsVersion));
writer->writeTextElement(QStringLiteral("filename"), filename_);
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
@@ -222,6 +274,10 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // subtitles
writer->writeEndElement(); // audio
writer->writeEndElement(); // export
writer->writeEndDocument();
}
Encoder* Encoder::CreateFromID(Type id, const EncodingParams& params)
@@ -311,4 +367,131 @@ QMatrix4x4 EncodingParams::GenerateMatrix(EncodingParams::VideoScalingMethod met
return preview_matrix;
}
bool EncodingParams::LoadV1(QXmlStreamReader *reader)
{
rational custom_range_in, custom_range_out;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("filename")) {
filename_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("format")) {
format_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("range")) {
has_custom_range_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("customrangein")) {
custom_range_in = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("customrangeout")) {
custom_range_out = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("video")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
video_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
video_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("width")) {
video_params_.set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
video_params_.set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("format")) {
video_params_.set_format(static_cast<VideoParams::Format>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("timebase")) {
video_params_.set_time_base(rational::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("divider")) {
video_params_.set_divider(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("bitrate")) {
video_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("minbitrate")) {
video_min_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("maxbitrate")) {
video_max_bit_rate_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("bufsize")) {
video_buffer_size_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("threads")) {
video_threads_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixfmt")) {
video_pix_fmt_ = reader->readElementText();
} else if (reader->name() == QStringLiteral("imgseq")) {
video_is_image_sequence_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("color")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("output")) {
color_transform_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("vscale")) {
video_scaling_method_ = static_cast<VideoScalingMethod>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("opts")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("entry")) {
QString key, value;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
key = reader->readElementText();
} else if (reader->name() == QStringLiteral("value")) {
value = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
set_video_option(key, value);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("audio")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
audio_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("codec")) {
audio_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("samplerate")) {
audio_params_.set_sample_rate(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("channellayout")) {
audio_params_.set_channel_layout(reader->readElementText().toULongLong());
} else if (reader->name() == QStringLiteral("format")) {
audio_params_.set_format(static_cast<AudioParams::Format>(reader->readElementText().toInt()));
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("subtitles")) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("enabled")) {
subtitles_enabled_ = attr.value().toInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("sidecar")) {
subtitles_are_sidecar_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("sidecarformat")) {
subtitle_sidecar_fmt_ = static_cast<ExportFormat::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("codec")) {
subtitles_codec_ = static_cast<ExportCodec::Codec>(reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
return true;
}
}
+9
View File
@@ -52,6 +52,9 @@ public:
EncodingParams();
static QDir GetPresetPath();
static QStringList GetListOfPresets();
bool IsValid() const
{
return video_enabled_ || audio_enabled_ || subtitles_enabled_;
@@ -113,6 +116,10 @@ public:
const rational& GetExportLength() const { return export_length_; }
void SetExportLength(const rational& export_length) { export_length_ = export_length; }
bool Load(QIODevice *device);
bool Load(QXmlStreamReader *reader);
void Save(QIODevice *device) const;
void Save(QXmlStreamWriter* writer) const;
bool has_custom_range() const { return has_custom_range_; }
@@ -133,6 +140,8 @@ public:
private:
static const int kEncoderParamsVersion = 1;
bool LoadV1(QXmlStreamReader *reader);
QString filename_;
ExportFormat::Format format_;
+2
View File
@@ -26,6 +26,8 @@ set(OLIVE_SOURCES
dialog/export/exportaudiotab.h
dialog/export/exportformatcombobox.cpp
dialog/export/exportformatcombobox.h
dialog/export/exportsavepresetdialog.cpp
dialog/export/exportsavepresetdialog.h
dialog/export/exportsubtitlestab.cpp
dialog/export/exportsubtitlestab.h
dialog/export/exportvideotab.cpp
+87 -32
View File
@@ -33,6 +33,7 @@
#include "common/digit.h"
#include "common/qtutils.h"
#include "dialog/task/task.h"
#include "exportsavepresetdialog.h"
#include "node/project/project.h"
#include "node/project/sequence/sequence.h"
#include "task/taskmanager.h"
@@ -81,21 +82,21 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
QLabel* preset_lbl = new QLabel(tr("Preset:"));
preset_lbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
preferences_layout->addWidget(preset_lbl, row, 0);
QComboBox* preset_combobox = new QComboBox();
preset_combobox->addItem(tr("Same As Source - High Quality"));
preset_combobox->addItem(tr("Same As Source - Medium Quality"));
preset_combobox->addItem(tr("Same As Source - Low Quality"));
preferences_layout->addWidget(preset_combobox, row, 1);
preset_combobox_ = new QComboBox();
LoadPresets();
connect(preset_combobox_, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ExportDialog::PresetComboBoxChanged);
preferences_layout->addWidget(preset_combobox_, row, 1, 1, 2);
QPushButton* preset_load_btn = new QPushButton();
/*QPushButton* preset_load_btn = new QPushButton();
preset_load_btn->setIcon(icon::Open);
preset_load_btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
preferences_layout->addWidget(preset_load_btn, row, 2);
preferences_layout->addWidget(preset_load_btn, row, 2);*/
QPushButton* preset_save_btn = new QPushButton();
preset_save_btn->setIcon(icon::Save);
preset_save_btn->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
preferences_layout->addWidget(preset_save_btn, row, 3);
connect(preset_save_btn, &QPushButton::clicked, this, &ExportDialog::SavePreset);
row++;
@@ -197,25 +198,9 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
// Set defaults
previously_selected_format_ = ExportFormat::kFormatMPEG4Video;
format_combobox_->SetFormat(ExportFormat::kFormatMPEG4Video);
connect(format_combobox_, &ExportFormatComboBox::FormatChanged, this, &ExportDialog::FormatChanged);
FormatChanged(format_combobox_->GetFormat());
VideoParams vp = viewer_node_->GetVideoParams();
AudioParams ap = viewer_node_->GetAudioParams();
video_tab_->width_slider()->SetValue(vp.width());
video_tab_->width_slider()->SetDefaultValue(vp.width());
video_tab_->height_slider()->SetValue(vp.height());
video_tab_->height_slider()->SetDefaultValue(vp.height());
video_tab_->SetSelectedFrameRate(vp.frame_rate());
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(OLIVE_CONFIG("OnlinePixelFormat").toInt()));
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
audio_tab_->sample_format_combobox()->SetAttemptToRestoreFormat(false);
audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
video_aspect_ratio_ = static_cast<double>(vp.width()) / static_cast<double>(vp.height());
connect(video_tab_->width_slider(),
@@ -247,21 +232,22 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
this,
&ExportDialog::ImageSequenceCheckBoxChanged);
// Set viewer to view the node
preview_viewer_->ConnectViewerNode(viewer_node_);
preview_viewer_->SetColorMenuEnabled(false);
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
// We don't check if the codec supports subtitles because we can always export to a sidecar file
bool has_subtitle_codecs = SequenceHasSubtitles();
bool has_subtitle_tracks = SequenceHasSubtitles();
connect(subtitles_enabled_, &QCheckBox::toggled, subtitle_tab_, &QWidget::setEnabled);
subtitles_enabled_->setChecked(has_subtitle_codecs);
subtitles_enabled_->setEnabled(has_subtitle_codecs);
subtitles_enabled_->setEnabled(has_subtitle_tracks);
// If the viewer already has cached params, use them
if (viewer_node_->GetLastUsedEncodingParams().IsValid()) {
SetParams(viewer_node_->GetLastUsedEncodingParams());
} else {
SetDefaults();
}
// Set viewer to view the node and set its colorspace
preview_viewer_->ConnectViewerNode(viewer_node_);
preview_viewer_->SetColorMenuEnabled(false);
preview_viewer_->SetColorTransform(video_tab_->CurrentOCIOColorSpace());
}
rational ExportDialog::GetSelectedTimebase() const
@@ -402,6 +388,29 @@ void ExportDialog::ImageSequenceCheckBoxChanged(bool e)
filename_edit_->setText(current_fileinfo.dir().filePath(basename));
}
void ExportDialog::SavePreset()
{
ExportSavePresetDialog d(GenerateParams(), this);
if (d.exec() == QDialog::Accepted) {
LoadPresets();
preset_combobox_->setCurrentText(d.GetSelectedPresetName());
}
}
void ExportDialog::PresetComboBoxChanged()
{
QComboBox *c = static_cast<QComboBox *>(sender());
int preset_number = c->currentData().toInt();
if (preset_number == kPresetDefault) {
SetDefaults();
} else if (preset_number == kPresetLastUsed) {
SetParams(viewer_node_->GetLastUsedEncodingParams());
} else {
SetParams(presets_.at(preset_number));
}
}
void ExportDialog::AddPreferencesTab(QWidget *inner_widget, const QString &title)
{
QScrollArea* scroll_area = new QScrollArea();
@@ -494,7 +503,32 @@ void ExportDialog::ResolutionChanged()
void ExportDialog::LoadPresets()
{
preset_combobox_->clear();
presets_.clear();
preset_combobox_->addItem(tr("Default"), kPresetDefault);
if (viewer_node_->GetLastUsedEncodingParams().IsValid()) {
preset_combobox_->addItem(tr("Last Used"), kPresetLastUsed);
}
preset_combobox_->insertSeparator(preset_combobox_->count());
QStringList l = EncodingParams::GetListOfPresets();
presets_.reserve(l.size());
for (const QString &preset : l) {
EncodingParams p;
QFile f(EncodingParams::GetPresetPath().filePath(preset));
if (f.open(QFile::ReadOnly)) {
if (p.Load(&f)) {
preset_combobox_->addItem(preset, int(presets_.size()));
presets_.push_back(p);
}
f.close();
}
}
}
void ExportDialog::SetDefaultFilename()
@@ -527,6 +561,28 @@ bool ExportDialog::SequenceHasSubtitles() const
return false;
}
void ExportDialog::SetDefaults()
{
format_combobox_->SetFormat(ExportFormat::kFormatMPEG4Video);
FormatChanged(format_combobox_->GetFormat());
VideoParams vp = viewer_node_->GetVideoParams();
AudioParams ap = viewer_node_->GetAudioParams();
video_tab_->width_slider()->SetValue(vp.width());
video_tab_->width_slider()->SetDefaultValue(vp.width());
video_tab_->height_slider()->SetValue(vp.height());
video_tab_->height_slider()->SetDefaultValue(vp.height());
video_tab_->SetSelectedFrameRate(vp.frame_rate());
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(OLIVE_CONFIG("OnlinePixelFormat").toInt()));
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
audio_tab_->sample_format_combobox()->SetAttemptToRestoreFormat(false);
audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout());
subtitles_enabled_->setChecked(SequenceHasSubtitles());
}
EncodingParams ExportDialog::GenerateParams() const
{
VideoParams video_render_params(static_cast<int>(video_tab_->width_slider()->GetValue()),
@@ -604,7 +660,6 @@ EncodingParams ExportDialog::GenerateParams() const
void ExportDialog::SetParams(const EncodingParams &e)
{
format_combobox_->SetFormat(e.format());
filename_edit_->setText(e.filename());
if (e.has_custom_range() && viewer_node_->GetWorkArea()->enabled()) {
range_combobox_->setCurrentIndex(kRangeInToOut);
+13
View File
@@ -69,6 +69,8 @@ private:
bool SequenceHasSubtitles() const;
void SetDefaults();
ViewerOutput* viewer_node_;
ExportFormat::Format previously_selected_format_;
@@ -81,9 +83,16 @@ private:
kRangeInToOut
};
enum AutoPreset {
kPresetDefault = -1,
kPresetLastUsed = -2,
};
QTabWidget* preferences_tabs_;
QComboBox* preset_combobox_;
QComboBox* range_combobox_;
std::vector<EncodingParams> presets_;
QCheckBox* video_enabled_;
QCheckBox* audio_enabled_;
@@ -119,6 +128,10 @@ private slots:
void ImageSequenceCheckBoxChanged(bool e);
void SavePreset();
void PresetComboBoxChanged();
};
}
@@ -0,0 +1,97 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 "exportsavepresetdialog.h"
#include <QDialogButtonBox>
#include <QLabel>
#include <QMessageBox>
#include <QVBoxLayout>
namespace olive {
ExportSavePresetDialog::ExportSavePresetDialog(const EncodingParams &p, QWidget *parent) :
QDialog(parent),
params_(p)
{
auto layout = new QVBoxLayout(this);
name_edit_ = new QLineEdit();
// Populate existing list
QStringList l = EncodingParams::GetListOfPresets();
if (!l.empty()) {
auto list_widget_ = new QListWidget();
for (const QString &f : l) {
list_widget_->addItem(f);
}
connect(list_widget_, &QListWidget::currentTextChanged, name_edit_, &QLineEdit::setText);
layout->addWidget(list_widget_);
}
auto name_layout = new QHBoxLayout();
layout->addLayout(name_layout);
name_layout->addWidget(new QLabel(tr("Name:")));
name_edit_->setFocus();
name_layout->addWidget(name_edit_);
auto btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(btns, &QDialogButtonBox::accepted, this, &ExportSavePresetDialog::accept);
connect(btns, &QDialogButtonBox::rejected, this, &ExportSavePresetDialog::reject);
layout->addWidget(btns);
setWindowTitle(tr("Save Export Preset"));
}
void ExportSavePresetDialog::accept()
{
if (name_edit_->text().isEmpty()) {
QMessageBox::critical(this, tr("Invalid Name"), tr("You must enter a name to save an export preset."));
return;
}
QDir d(EncodingParams::GetPresetPath());
if (!d.exists()) {
d.mkpath(QStringLiteral("."));
}
QFile f(d.filePath(name_edit_->text()));
if (f.exists()) {
if (QMessageBox::question(this, tr("Overwrite Preset"), tr("A preset with the name \"%1\" already exists. Do you wish to overwrite it?").arg(name_edit_->text()),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
return;
}
}
if (!f.open(QFile::WriteOnly)) {
QMessageBox::critical(this, tr("Write Error"), tr("Failed to open file \"%1\" for writing.").arg(f.fileName()));
return;
}
params_.Save(&f);
f.close();
QDialog::accept();
}
}
@@ -0,0 +1,55 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2022 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 EXPORTSAVEPRESETDIALOG_H
#define EXPORTSAVEPRESETDIALOG_H
#include <QDialog>
#include <QLineEdit>
#include <QListWidget>
#include "codec/encoder.h"
namespace olive {
class ExportSavePresetDialog : public QDialog
{
Q_OBJECT
public:
ExportSavePresetDialog(const EncodingParams &p, QWidget *parent = nullptr);
QString GetSelectedPresetName() const
{
return name_edit_->text();
}
public slots:
virtual void accept() override;
private:
QLineEdit *name_edit_;
EncodingParams params_;
};
}
#endif // EXPORTSAVEPRESETDIALOG_H