/*** Olive - Non-Linear Video Editor Copyright (C) 2022 Olive Team Modifications Copyright (C) 2025 mikesolar 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 "exportvideotab.h" #include #include #include #include #include #include "exportadvancedvideodialog.h" #include "node/color/colormanager/colormanager.h" #include "oakengine/encoding.h" namespace olive { ExportVideoTab::ExportVideoTab(OakEngineColorManager *color_manager, QWidget *parent) : QWidget(parent) , color_manager_(color_manager) , threads_(0) , color_range_(0) // k_color_range_default { QVBoxLayout *outer_layout = new QVBoxLayout(this); outer_layout->addWidget(setup_resolution_section()); outer_layout->addWidget(setup_codec_section()); outer_layout->addWidget(setup_color_section()); outer_layout->addStretch(); } int ExportVideoTab::set_format(int format) { format_ = format; const int vcodec_count = oakengine_encoding_format_video_codec_count(format); setEnabled(vcodec_count > 0); codec_combobox()->clear(); for (int i = 0; i < vcodec_count; i++) { int vcodec = oakengine_encoding_format_video_codec_at(format, i); char buf[256]; oakengine_encoding_codec_name(vcodec, buf, sizeof(buf)); codec_combobox()->addItem(QString::fromUtf8(buf), vcodec); } return vcodec_count; } bool ExportVideoTab::is_image_sequence_set() const { ImageSection *img_section = dynamic_cast(codec_stack_->currentWidget()); return (img_section && img_section->is_image_sequence_checked()); } void ExportVideoTab::set_image_sequence(bool e) const { if (ImageSection *img_section = dynamic_cast(codec_stack_->currentWidget())) { img_section->set_image_sequence_checked(e); } } QWidget *ExportVideoTab::setup_resolution_section() { int row = 0; QGroupBox *resolution_group = new QGroupBox(); resolution_group->setTitle(tr("General")); QGridLayout *layout = new QGridLayout(resolution_group); layout->addWidget(new QLabel(tr("Width:")), row, 0); width_slider_ = new IntegerSlider(); width_slider_->set_minimum(1); layout->addWidget(width_slider_, row, 1); row++; layout->addWidget(new QLabel(tr("Height:")), row, 0); height_slider_ = new IntegerSlider(); height_slider_->set_minimum(1); layout->addWidget(height_slider_, row, 1); row++; layout->addWidget(new QLabel(tr("Maintain Aspect Ratio:")), row, 0); maintain_aspect_checkbox_ = new QCheckBox(); maintain_aspect_checkbox_->setChecked(true); layout->addWidget(maintain_aspect_checkbox_, row, 1); row++; layout->addWidget(new QLabel(tr("Scaling Method:")), row, 0); scaling_method_combobox_ = new QComboBox(); scaling_method_combobox_->setEnabled(false); scaling_method_combobox_->addItem(tr("Fit"), OAKENGINE_ENCODING_SCALING_FIT); scaling_method_combobox_->addItem(tr("Stretch"), OAKENGINE_ENCODING_SCALING_STRETCH); scaling_method_combobox_->addItem(tr("Crop"), OAKENGINE_ENCODING_SCALING_CROP); layout->addWidget(scaling_method_combobox_, row, 1); // Automatically enable/disable the scaling method depending on maintain aspect ratio connect(maintain_aspect_checkbox_, &QCheckBox::toggled, this, &ExportVideoTab::maintain_aspect_ratio_changed); row++; layout->addWidget(new QLabel(tr("Frame Rate:")), row, 0); frame_rate_combobox_ = new FrameRateComboBox(); connect(frame_rate_combobox_, &FrameRateComboBox::frame_rate_changed, this, &ExportVideoTab::update_frame_rate); layout->addWidget(frame_rate_combobox_, row, 1); row++; layout->addWidget(new QLabel(tr("Pixel Aspect Ratio:")), row, 0); pixel_aspect_combobox_ = new PixelAspectRatioComboBox(); layout->addWidget(pixel_aspect_combobox_, row, 1); row++; layout->addWidget(new QLabel(tr("Interlacing:")), row, 0); interlaced_combobox_ = new InterlacedComboBox(); layout->addWidget(interlaced_combobox_, row, 1); row++; layout->addWidget(new QLabel(tr("Quality:")), row, 0); pixel_format_field_ = new PixelFormatComboBox(false); layout->addWidget(pixel_format_field_, row, 1); return resolution_group; } QWidget *ExportVideoTab::setup_color_section() { color_space_chooser_ = new ColorSpaceChooser(color_manager_, true, false); connect(color_space_chooser_, &ColorSpaceChooser::input_color_space_changed, this, &ExportVideoTab::color_space_changed); return color_space_chooser_; } QWidget *ExportVideoTab::setup_codec_section() { int row = 0; QGroupBox *codec_group = new QGroupBox(); codec_group->setTitle(tr("Codec")); QGridLayout *codec_layout = new QGridLayout(codec_group); codec_layout->addWidget(new QLabel(tr("Codec:")), row, 0); codec_combobox_ = new QComboBox(); codec_layout->addWidget(codec_combobox_, row, 1); connect( codec_combobox_, static_cast(&QComboBox::currentIndexChanged), this, &ExportVideoTab::video_codec_changed); row++; codec_stack_ = new CodecStack(); codec_layout->addWidget(codec_stack_, row, 0, 1, 2); image_section_ = new ImageSection(); connect(image_section_, &ImageSection::time_changed, this, &ExportVideoTab::time_changed); codec_stack_->addWidget(image_section_); h264_section_ = new H264Section(); codec_stack_->addWidget(h264_section_); h265_section_ = new H265Section(); codec_stack_->addWidget(h265_section_); av1_section_ = new AV1Section(); codec_stack_->addWidget(av1_section_); cineform_section_ = new CineformSection(); codec_stack_->addWidget(cineform_section_); row++; QPushButton *advanced_btn = new QPushButton(tr("Advanced")); connect(advanced_btn, &QPushButton::clicked, this, &ExportVideoTab::open_advanced_dialog); codec_layout->addWidget(advanced_btn, row, 1); return codec_group; } void ExportVideoTab::maintain_aspect_ratio_changed(bool val) { scaling_method_combobox_->setEnabled(!val); } void ExportVideoTab::open_advanced_dialog() { // Find pixel formats compatible with this encoder QStringList pixel_formats; const int pix_count = oakengine_encoding_pix_fmt_count(format_, get_selected_codec()); for (int i = 0; i < pix_count; i++) { char buf[64]; oakengine_encoding_pix_fmt_at(format_, get_selected_codec(), i, buf, sizeof(buf)); pixel_formats.append(QString::fromUtf8(buf)); } ExportAdvancedVideoDialog d(pixel_formats, this); d.set_threads(threads_); d.set_pix_fmt(pix_fmt_); d.set_yuv_range(color_range_); if (d.exec() == QDialog::Accepted) { threads_ = d.threads(); pix_fmt_ = d.pix_fmt(); color_range_ = d.yuv_range(); } } void ExportVideoTab::update_frame_rate(Rational r) { // Convert frame rate to timebase r.flip(); for (int i = 0; i < codec_stack_->count(); i++) { ImageSection *img = dynamic_cast(codec_stack_->widget(i)); if (img) { img->set_timebase(r); } } } void ExportVideoTab::video_codec_changed() { int codec = get_selected_codec(); switch (codec) { case OAKENGINE_ENCODING_CODEC_H264: case OAKENGINE_ENCODING_CODEC_H264RGB: set_codec_section(h264_section_); break; case OAKENGINE_ENCODING_CODEC_H265: set_codec_section(h265_section_); break; case OAKENGINE_ENCODING_CODEC_AV1: set_codec_section(av1_section_); break; case OAKENGINE_ENCODING_CODEC_CINEFORM: set_codec_section(cineform_section_); break; default: set_codec_section( oakengine_encoding_codec_is_still_image(codec) ? image_section_ : nullptr); } // Set default pixel format QStringList pix_fmts; const int pix_count = oakengine_encoding_pix_fmt_count(format_, codec); for (int i = 0; i < pix_count; i++) { char buf[64]; oakengine_encoding_pix_fmt_at(format_, codec, i, buf, sizeof(buf)); pix_fmts.append(QString::fromUtf8(buf)); } if (!pix_fmts.isEmpty()) { pix_fmt_ = pix_fmts.first(); } else { pix_fmt_.clear(); } } void ExportVideoTab::set_time(const Rational &time) { for (int i = 0; i < codec_stack_->count(); i++) { ImageSection *img = dynamic_cast(codec_stack_->widget(i)); if (img) { img->set_time(time); } } } }