diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp index 64dee8e6b..ea3750720 100644 --- a/app/codec/encoder.cpp +++ b/app/codec/encoder.cpp @@ -88,6 +88,11 @@ void EncodingParams::set_video_threads(const int &threads) video_threads_ = threads; } +void EncodingParams::set_video_pix_fmt(const QString &s) +{ + video_pix_fmt_ = s; +} + const QString &EncodingParams::filename() const { return filename_; @@ -133,6 +138,11 @@ const int &EncodingParams::video_threads() const return video_threads_; } +const QString &EncodingParams::video_pix_fmt() const +{ + return video_pix_fmt_; +} + bool EncodingParams::audio_enabled() const { return audio_enabled_; diff --git a/app/codec/encoder.h b/app/codec/encoder.h index 260c735a3..412306388 100644 --- a/app/codec/encoder.h +++ b/app/codec/encoder.h @@ -51,6 +51,7 @@ public: void set_video_max_bit_rate(const int64_t& rate); void set_video_buffer_size(const int64_t& sz); void set_video_threads(const int& threads); + void set_video_pix_fmt(const QString& s); const QString& filename() const; @@ -62,6 +63,7 @@ public: const int64_t& video_max_bit_rate() const; const int64_t& video_buffer_size() const; const int& video_threads() const; + const QString& video_pix_fmt() const; bool audio_enabled() const; const ExportCodec::Codec &audio_codec() const; @@ -83,6 +85,7 @@ private: int64_t video_max_bit_rate_; int64_t video_buffer_size_; int video_threads_; + QString video_pix_fmt_; bool audio_enabled_; ExportCodec::Codec audio_codec_; diff --git a/app/codec/exportcodec.cpp b/app/codec/exportcodec.cpp index c68ff33ae..bd819e204 100644 --- a/app/codec/exportcodec.cpp +++ b/app/codec/exportcodec.cpp @@ -20,6 +20,11 @@ #include "exportcodec.h" +extern "C" { +#include +#include +} + OLIVE_NAMESPACE_ENTER QString ExportCodec::GetCodecName(ExportCodec::Codec c) @@ -77,4 +82,49 @@ bool ExportCodec::IsCodecAStillImage(ExportCodec::Codec c) return false; } +QStringList ExportCodec::GetPixelFormatsForCodec(ExportCodec::Codec c) +{ + QStringList pix_fmts; + + AVCodec* codec_info; + + switch (c) { + case kCodecH264: + codec_info = avcodec_find_encoder(AV_CODEC_ID_H264); + break; + case kCodecDNxHD: + codec_info = avcodec_find_encoder(AV_CODEC_ID_DNXHD); + break; + case kCodecProRes: + codec_info = avcodec_find_encoder(AV_CODEC_ID_PRORES); + break; + case kCodecH265: + codec_info = avcodec_find_encoder(AV_CODEC_ID_HEVC); + break; + case kCodecOpenEXR: + case kCodecPNG: + case kCodecTIFF: + // FIXME: Add these in (these will most likely use an OIIOEncoder which doesn't exist yet) + codec_info = nullptr; + break; + case kCodecMP2: + case kCodecMP3: + case kCodecAAC: + case kCodecPCM: + case kCodecCount: + // These are audio or invalid codecs and therefore have no pixel formats + codec_info = nullptr; + break; + } + + if (codec_info) { + for (int i=0; codec_info->pix_fmts[i]!=-1; i++) { + const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]); + pix_fmts.append(pix_fmt_name); + } + } + + return pix_fmts; +} + OLIVE_NAMESPACE_EXIT diff --git a/app/codec/exportcodec.h b/app/codec/exportcodec.h index 7fc4f5898..d7a41124b 100644 --- a/app/codec/exportcodec.h +++ b/app/codec/exportcodec.h @@ -51,6 +51,8 @@ public: static bool IsCodecAStillImage(Codec c); + static QStringList GetPixelFormatsForCodec(Codec c); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 3b034f7ad..84349c8d1 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -20,6 +20,10 @@ #include "ffmpegencoder.h" +extern "C" { +#include +} + #include #include "ffmpegcommon.h" @@ -441,9 +445,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV codec_ctx->height = params().video_params().height(); codec_ctx->sample_aspect_ratio = {1, 1}; codec_ctx->time_base = params().video_params().time_base().toAVRational(); - - // FIXME: Make this customizable again - codec_ctx->pix_fmt = encoder->pix_fmts[0]; + codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8()); // Set custom options { diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index d6e0deb72..b23950cfc 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -203,11 +203,6 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : this, &ExportDialog::ResolutionChanged); - connect(video_tab_->codec_combobox(), - static_cast(&QComboBox::currentIndexChanged), - this, - &ExportDialog::VideoCodecChanged); - connect(video_tab_, &ExportVideoTab::ColorSpaceChanged, preview_viewer_, @@ -232,8 +227,8 @@ void ExportDialog::StartExport() return; } - // Validate if the entered filename contains the correct extension (the extension is necessary for both FFmpeg and - // OIIO to determine the output format) + // Validate if the entered filename contains the correct extension (the extension is necessary + // for both FFmpeg and OIIO to determine the output format) QString necessary_ext = QStringLiteral(".%1").arg(ExportFormat::GetExtension(static_cast(format_combobox_->currentIndex()))); // If it doesn't, see if the user wants to append it automatically. If not, we don't abort the export. @@ -356,7 +351,6 @@ void ExportDialog::FormatChanged(int index) foreach (ExportCodec::Codec vcodec, ExportFormat::GetVideoCodecs(current_format)) { video_tab_->codec_combobox()->addItem(ExportCodec::GetCodecName(vcodec), vcodec); } - VideoCodecChanged(); audio_tab_->codec_combobox()->clear(); foreach (ExportCodec::Codec acodec, ExportFormat::GetAudioCodecs(current_format)) { @@ -396,17 +390,6 @@ void ExportDialog::ResolutionChanged() UpdateViewerDimensions(); } -void ExportDialog::VideoCodecChanged() -{ - ExportCodec::Codec codec = static_cast(video_tab_->codec_combobox()->currentData().toInt()); - - if (codec == ExportCodec::kCodecH264) { - video_tab_->SetCodecSection(video_tab_->h264_section()); - } else if (ExportCodec::IsCodecAStillImage(codec)) { - video_tab_->SetCodecSection(video_tab_->image_section()); - } -} - void ExportDialog::LoadPresets() { @@ -457,7 +440,7 @@ ExportParams ExportDialog::GenerateParams() const } if (video_enabled_->isChecked()) { - ExportCodec::Codec video_codec = static_cast(video_tab_->codec_combobox()->currentData().toInt()); + ExportCodec::Codec video_codec = video_tab_->GetSelectedCodec(); params.EnableVideo(video_render_params, video_codec); params.set_video_threads(video_tab_->threads()); @@ -465,6 +448,8 @@ ExportParams ExportDialog::GenerateParams() const video_tab_->GetCodecSection()->AddOpts(¶ms); params.set_color_transform(video_tab_->CurrentOCIOColorSpace()); + + params.set_video_pix_fmt(video_tab_->pix_fmt()); } if (audio_enabled_->isChecked()) { diff --git a/app/dialog/export/export.h b/app/dialog/export/export.h index 60a301af4..8313e59cf 100644 --- a/app/dialog/export/export.h +++ b/app/dialog/export/export.h @@ -81,8 +81,6 @@ private slots: void ResolutionChanged(); - void VideoCodecChanged(); - void UpdateViewerDimensions(); void StartExport(); diff --git a/app/dialog/export/exportadvancedvideodialog.cpp b/app/dialog/export/exportadvancedvideodialog.cpp index 1b11d85b9..f4fc6b6e6 100644 --- a/app/dialog/export/exportadvancedvideodialog.cpp +++ b/app/dialog/export/exportadvancedvideodialog.cpp @@ -2,42 +2,61 @@ #include #include +#include #include OLIVE_NAMESPACE_ENTER -ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(QWidget *parent) : +ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(const QList &pix_fmts, QWidget *parent) : QDialog(parent) { setWindowTitle(tr("Advanced")); - QGridLayout* layout = new QGridLayout(this); + QVBoxLayout* layout = new QVBoxLayout(this); - int row = 0; + { + // Pixel Settings + QGroupBox* pixel_group = new QGroupBox(); + layout->addWidget(pixel_group); + pixel_group->setTitle(tr("Pixel")); - layout->addWidget(new QLabel(tr("Threads:")), row, 0); + QGridLayout* pixel_layout = new QGridLayout(pixel_group); - thread_slider_ = new IntegerSlider(); - thread_slider_->SetMinimum(0); - thread_slider_->SetDefaultValue(0); - layout->addWidget(thread_slider_, row, 1); + int row = 0; - row++; + pixel_layout->addWidget(new QLabel(tr("Pixel Format:")), row, 0); + + pixel_format_combobox_ = new QComboBox(); + pixel_format_combobox_->addItems(pix_fmts); + pixel_layout->addWidget(pixel_format_combobox_, row, 1); + + row++; + } + + { + // Performance Settings + QGroupBox* performance_group = new QGroupBox(); + layout->addWidget(performance_group); + performance_group->setTitle(tr("Performance")); + + QGridLayout* performance_layout = new QGridLayout(performance_group); + + int row = 0; + + performance_layout->addWidget(new QLabel(tr("Threads:")), row, 0); + + thread_slider_ = new IntegerSlider(); + thread_slider_->SetMinimum(0); + thread_slider_->SetDefaultValue(0); + performance_layout->addWidget(thread_slider_, row, 1); + + row++; + } QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttons, &QDialogButtonBox::accepted, this, &ExportAdvancedVideoDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &ExportAdvancedVideoDialog::reject); - layout->addWidget(buttons, row, 0, 1, 2); -} - -int ExportAdvancedVideoDialog::threads() const -{ - return static_cast(thread_slider_->GetValue()); -} - -void ExportAdvancedVideoDialog::set_threads(int t) -{ - thread_slider_->SetValue(t); + layout->addWidget(buttons); } OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/export/exportadvancedvideodialog.h b/app/dialog/export/exportadvancedvideodialog.h index 71d3dc6e0..bdadbbebe 100644 --- a/app/dialog/export/exportadvancedvideodialog.h +++ b/app/dialog/export/exportadvancedvideodialog.h @@ -1,6 +1,7 @@ #ifndef EXPORTADVANCEDVIDEODIALOG_H #define EXPORTADVANCEDVIDEODIALOG_H +#include #include #include "widget/slider/integerslider.h" @@ -11,14 +12,34 @@ class ExportAdvancedVideoDialog : public QDialog { Q_OBJECT public: - ExportAdvancedVideoDialog(QWidget* parent = nullptr); + ExportAdvancedVideoDialog(const QList& pix_fmts, + QWidget* parent = nullptr); - int threads() const; - void set_threads(int t); + int threads() const + { + return static_cast(thread_slider_->GetValue()); + } + + void set_threads(int t) + { + thread_slider_->SetValue(t); + } + + QString pix_fmt() const + { + return pixel_format_combobox_->currentText(); + } + + void set_pix_fmt(const QString& s) + { + pixel_format_combobox_->setCurrentText(s); + } private: IntegerSlider* thread_slider_; + QComboBox* pixel_format_combobox_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/export/exportvideotab.cpp b/app/dialog/export/exportvideotab.cpp index 476dc495a..298e5bc11 100644 --- a/app/dialog/export/exportvideotab.cpp +++ b/app/dialog/export/exportvideotab.cpp @@ -49,6 +49,11 @@ ExportVideoTab::ExportVideoTab(ColorManager* color_manager, QWidget *parent) : outer_layout->addStretch(); } +ExportCodec::Codec ExportVideoTab::GetSelectedCodec() const +{ + return static_cast(codec_combobox()->currentData().toInt()); +} + QComboBox *ExportVideoTab::codec_combobox() const { return codec_combobox_; @@ -109,11 +114,6 @@ H264Section *ExportVideoTab::h264_section() const return h264_section_; } -const int &ExportVideoTab::threads() const -{ - return threads_; -} - QWidget* ExportVideoTab::SetupResolutionSection() { int row = 0; @@ -194,6 +194,10 @@ QWidget *ExportVideoTab::SetupCodecSection() codec_combobox_ = new QComboBox(); codec_layout->addWidget(codec_combobox_, row, 1); + connect(codec_combobox_, + static_cast(&QComboBox::currentIndexChanged), + this, + &ExportVideoTab::VideoCodecChanged); row++; @@ -222,13 +226,33 @@ void ExportVideoTab::MaintainAspectRatioChanged(bool val) void ExportVideoTab::OpenAdvancedDialog() { - ExportAdvancedVideoDialog d(this); + // Find export formats compatible with this encoder + QStringList pixel_formats = ExportCodec::GetPixelFormatsForCodec(GetSelectedCodec()); + + ExportAdvancedVideoDialog d(pixel_formats, this); d.set_threads(threads_); + d.set_pix_fmt(pix_fmt_); if (d.exec() == QDialog::Accepted) { threads_ = d.threads(); + pix_fmt_ = d.pix_fmt(); } } +void ExportVideoTab::VideoCodecChanged() +{ + ExportCodec::Codec codec = GetSelectedCodec(); + + if (codec == ExportCodec::kCodecH264) { + SetCodecSection(h264_section()); + } else if (ExportCodec::IsCodecAStillImage(codec)) { + SetCodecSection(image_section()); + } + + // Set default pixel format + pix_fmt_ = ExportCodec::GetPixelFormatsForCodec(codec).first(); + qDebug() << "Set default pix fmt" << pix_fmt_; +} + OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/export/exportvideotab.h b/app/dialog/export/exportvideotab.h index 7ad0c72d6..dd67e2ea7 100644 --- a/app/dialog/export/exportvideotab.h +++ b/app/dialog/export/exportvideotab.h @@ -40,6 +40,8 @@ class ExportVideoTab : public QWidget public: ExportVideoTab(ColorManager* color_manager, QWidget* parent = nullptr); + ExportCodec::Codec GetSelectedCodec() const; + QComboBox* codec_combobox() const; IntegerSlider* width_slider() const; @@ -57,7 +59,17 @@ public: ImageSection* image_section() const; H264Section* h264_section() const; - const int& threads() const; + const int& threads() const + { + return threads_; + } + + const QString& pix_fmt() const { + return pix_fmt_; + } + +public slots: + void VideoCodecChanged(); signals: void ColorSpaceChanged(const QString& colorspace); @@ -87,6 +99,8 @@ private: int threads_; + QString pix_fmt_; + private slots: void MaintainAspectRatioChanged(bool val);