File indexing completed on 2022-11-22 14:07:11

0001 /*
0002     SPDX-FileCopyrightText: 2022 Julius Künzel <jk.kdedev@smartlab.uber.space>
0003 
0004     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "renderpresetdialog.h"
0008 
0009 #include "core.h"
0010 #include "kdenlivesettings.h"
0011 #include "monitor/monitormanager.h"
0012 #include "profiles/profilemodel.hpp"
0013 #include "renderpresets/renderpresetmodel.hpp"
0014 #include "renderpresets/renderpresetrepository.hpp"
0015 
0016 #include <KMessageBox>
0017 #include <QPushButton>
0018 #include <QScrollBar>
0019 
0020 // TODO replace this by std::gcd ones why require C++17 or greater
0021 static int gcd(int a, int b)
0022 {
0023     for (;;) {
0024         if (a == 0) return b;
0025         b %= a;
0026         if (b == 0) return a;
0027         a %= b;
0028     }
0029 }
0030 
0031 RenderPresetDialog::RenderPresetDialog(QWidget *parent, RenderPresetModel *preset, Mode mode)
0032     : QDialog(parent)
0033     , m_saveName(preset ? preset->name() : "")
0034     , m_monitor(nullptr)
0035     , m_fixedResRatio(1.)
0036     , m_manualPreset(false)
0037 {
0038     setupUi(this);
0039     if (preset) {
0040         m_manualPreset = preset->isManual();
0041     }
0042 
0043     m_uiParams.append({QStringLiteral("f"),
0044                        QStringLiteral("acodec"),
0045                        QStringLiteral("vcodec"),
0046                        QStringLiteral("width"),
0047                        QStringLiteral("height"),
0048                        QStringLiteral("s"),
0049                        QStringLiteral("r"),
0050                        QStringLiteral("frame_rate_num"),
0051                        QStringLiteral("frame_rate_den"),
0052                        QStringLiteral("crf"),
0053                        QStringLiteral("vb"),
0054                        QStringLiteral("vminrate"),
0055                        QStringLiteral("vmaxrate"),
0056                        QStringLiteral("vglobal_quality"),
0057                        QStringLiteral("vq"),
0058                        QStringLiteral("rc"),
0059                        QStringLiteral("g"),
0060                        QStringLiteral("bf"),
0061                        QStringLiteral("progressive"),
0062                        QStringLiteral("top_field_first"),
0063                        QStringLiteral("qscale"),
0064                        QStringLiteral("vbufsize"),
0065                        QStringLiteral("qmin"),
0066                        QStringLiteral("qp_i"),
0067                        QStringLiteral("qp_p"),
0068                        QStringLiteral("qp_b"),
0069                        QStringLiteral("ab"),
0070                        QStringLiteral("aq"),
0071                        QStringLiteral("compression_level"),
0072                        QStringLiteral("vbr"),
0073                        QStringLiteral("ar"),
0074                        QStringLiteral("display_aspect_num"),
0075                        QStringLiteral("display_aspect_den"),
0076                        QStringLiteral("sample_aspect_num"),
0077                        QStringLiteral("sample_aspect_den"),
0078                        QStringLiteral("aspect"),
0079                        QStringLiteral("sc_threshold"),
0080                        QStringLiteral("strict_gop"),
0081                        QStringLiteral("keyint_min"),
0082                        QStringLiteral("channels")});
0083 
0084     // TODO: implement colorspace
0085     colorspaceCombo->hide();
0086     colorspaceLabel->hide();
0087 
0088     formatCombo->addItems(RenderPresetRepository::supportedFormats());
0089     vCodecCombo->addItems(RenderPresetRepository::vcodecs());
0090     aCodecCombo->addItems(RenderPresetRepository::acodecs());
0091 
0092     aRateControlCombo->addItem(i18n("Average Bitrate"));
0093     aRateControlCombo->addItem(i18n("CBR – Constant Bitrate"));
0094     aRateControlCombo->addItem(i18n("VBR – Variable Bitrate"));
0095 
0096     audioChannels->addItem(i18n("1 (mono)"), 1);
0097     audioChannels->addItem(i18n("2 (stereo)"), 2);
0098     audioChannels->addItem(i18n("4"), 4);
0099     audioChannels->addItem(i18n("6"), 6);
0100 
0101     QValidator *validator = new QIntValidator(this);
0102     audioSampleRate->setValidator(validator);
0103 
0104     // Add some common pixel aspect ratios:
0105     // The following code works, because setting a yet
0106     // unknown ratio will add it to the combo box.
0107     setPixelAspectRatio(1, 1);
0108     setPixelAspectRatio(10, 11);
0109     setPixelAspectRatio(12, 11);
0110     setPixelAspectRatio(59, 54);
0111     setPixelAspectRatio(4, 3);
0112     setPixelAspectRatio(64, 45);
0113     setPixelAspectRatio(11, 9);
0114     setPixelAspectRatio(118, 81);
0115 
0116     //
0117     QPushButton *helpButton = buttonBox->button(QDialogButtonBox::Help);
0118     helpButton->setText(QString());
0119     helpButton->setIcon(QIcon::fromTheme(QStringLiteral("settings-configure")));
0120     helpButton->setToolTip(i18n("Edit Render Preset"));
0121     helpButton->setCheckable(true);
0122     helpButton->setChecked(KdenliveSettings::showRenderTextParameters());
0123     parameters->setVisible(KdenliveSettings::showRenderTextParameters());
0124     connect(helpButton, &QPushButton::toggled, [this](bool checked) {
0125         parameters->setVisible(checked);
0126         KdenliveSettings::setShowRenderTextParameters(checked);
0127     });
0128 
0129     connect(vRateControlCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int index) {
0130         switch (index) {
0131         case RenderPresetModel::RateControl::Average:
0132             default_vbitrate->setEnabled(true);
0133             cVBitrate->setEnabled(true);
0134             default_vquality->setEnabled(false);
0135             vquality_label->setEnabled(false);
0136             vBuffer->setEnabled(false);
0137             cBuffer->setEnabled(false);
0138             break;
0139         case RenderPresetModel::RateControl::Constant:
0140             default_vbitrate->setEnabled(true);
0141             cVBitrate->setEnabled(true);
0142             default_vquality->setEnabled(false);
0143             vquality_label->setEnabled(false);
0144             vBuffer->setEnabled(true);
0145             cBuffer->setEnabled(true);
0146             break;
0147         case RenderPresetModel::RateControl::Constrained:
0148             default_vbitrate->setEnabled(true);
0149             cVBitrate->setEnabled(true);
0150             default_vquality->setEnabled(true);
0151             vquality_label->setEnabled(true);
0152             vBuffer->setEnabled(true);
0153             cBuffer->setEnabled(true);
0154             break;
0155         case RenderPresetModel::RateControl::Quality:
0156             default_vbitrate->setEnabled(false);
0157             cVBitrate->setEnabled(false);
0158             default_vquality->setEnabled(true);
0159             vquality_label->setEnabled(true);
0160             vBuffer->setEnabled(false);
0161             cBuffer->setEnabled(false);
0162             break;
0163         };
0164         slotUpdateParams();
0165     });
0166 
0167     vRateControlCombo->addItem(i18n("Average Bitrate"));
0168     vRateControlCombo->addItem(i18n("CBR – Constant Bitrate"));
0169     vRateControlCombo->addItem(i18n("VBR – Variable Bitrate"));
0170     vRateControlCombo->addItem(i18n("Constrained VBR"));
0171 
0172     connect(scanningCombo, &QComboBox::currentTextChanged, this, [&]() {
0173         fieldOrderCombo->setEnabled(scanningCombo->currentIndex() != 1);
0174         cField->setEnabled(scanningCombo->currentIndex() != 1);
0175         slotUpdateParams();
0176     });
0177     connect(gopSpinner, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [&](int value) {
0178         fixedGop->setEnabled(value > 1);
0179         bFramesSpinner->setEnabled(value > 1);
0180         bFramesLabel->setEnabled(value > 1);
0181         if (value <= 1) {
0182             fixedGop->blockSignals(true);
0183             fixedGop->setChecked(false);
0184             fixedGop->blockSignals(false);
0185             bFramesSpinner->blockSignals(true);
0186             bFramesSpinner->setValue(-1);
0187             bFramesSpinner->blockSignals(false);
0188         } else {
0189             bFramesSpinner->setMaximum(value - 1);
0190         }
0191         slotUpdateParams();
0192     });
0193 
0194     groupName->addItems(RenderPresetRepository::get()->groupNames());
0195 
0196     std::unique_ptr<ProfileModel> &projectProfile = pCore->getCurrentProfile();
0197     int parNum = projectProfile->sample_aspect_num();
0198     int parDen = projectProfile->sample_aspect_den();
0199     connect(button_manual, &QPushButton::clicked, [this, helpButton]() {
0200         if (m_manualPreset) {
0201             return;
0202         }
0203         tabWidget->setVisible(false);
0204         label_container->setEnabled(false);
0205         formatCombo->setEnabled(false);
0206         parameters->setVisible(true);
0207         parameters->setReadOnly(false);
0208         helpButton->setEnabled(false);
0209         m_manualPreset = true;
0210     });
0211     if (preset) {
0212         groupName->setCurrentText(preset->groupName());
0213         if (mode != Mode::New) {
0214             preset_name->setText(preset->name());
0215         }
0216         preset_name->setFocus();
0217         if (m_manualPreset) {
0218             tabWidget->setVisible(false);
0219             label_container->setEnabled(false);
0220             formatCombo->setEnabled(false);
0221             parameters->setVisible(true);
0222             parameters->setPlainText(preset->params());
0223             parameters->setReadOnly(false);
0224             helpButton->setEnabled(false);
0225             preset_extension->setText(preset->extension());
0226         } else {
0227             formatCombo->setCurrentText(preset->getParam(QStringLiteral("f")));
0228             preset_extension->setText(preset->extension());
0229             QString width = preset->getParam(QStringLiteral("width"));
0230             QString height = preset->getParam(QStringLiteral("height"));
0231             QString size = preset->getParam(QStringLiteral("s"));
0232 
0233             if (!(width.isEmpty() && height.isEmpty())) {
0234                 resWidth->setValue(width.toInt());
0235                 resHeight->setValue(height.toInt());
0236                 cResolution->setChecked(true);
0237             } else if (!size.isEmpty()) {
0238                 QStringList list = size.split(QStringLiteral("x"));
0239                 if (list.count() == 2) {
0240                     resWidth->setValue(list.at(0).toInt());
0241                     resHeight->setValue(list.at(1).toInt());
0242                     cResolution->setChecked(true);
0243                 }
0244             } else {
0245                 resWidth->setValue(projectProfile->width());
0246                 resHeight->setValue(projectProfile->height());
0247             }
0248 
0249             // video tab
0250             if (preset->hasParam(QStringLiteral("vbufsize"))) {
0251                 bool ok = false;
0252                 int vbufsize = preset->getParam(QStringLiteral("vbufsize")).toInt(&ok);
0253                 if (ok) {
0254                     vBuffer->setValue(vbufsize / 1024 / 8);
0255                     cBuffer->setChecked(true);
0256                 }
0257             }
0258             QString vqParam = preset->getParam(QStringLiteral("vq"));
0259             QString vbParam = preset->getParam(QStringLiteral("vb"));
0260             if (vqParam.isEmpty()) {
0261                 vqParam = preset->getParam(QStringLiteral("vglobal_quality"));
0262             }
0263             if (vqParam.isEmpty()) {
0264                 vqParam = preset->getParam(QStringLiteral("qscale"));
0265             }
0266             if (vqParam.isEmpty()) {
0267                 vqParam = preset->getParam(QStringLiteral("crf"));
0268             }
0269             if (vqParam == QStringLiteral("%quality")) {
0270                 default_vquality->setValue(preset->defaultVQuality().toInt());
0271             } else {
0272                 default_vquality->setValue(vqParam.toInt());
0273             }
0274 
0275             vRateControlCombo->setCurrentIndex(preset->videoRateControl());
0276 
0277             if (vbParam.isEmpty()) {
0278                 vbParam = preset->getParam(QStringLiteral("vmaxrate"));
0279             }
0280             if (vbParam.contains(QStringLiteral("%bitrate"))) {
0281                 default_vbitrate->setValue(preset->defaultVBitrate().toInt());
0282                 cVBitrate->setChecked(true);
0283             } else if (!vbParam.isEmpty()) {
0284                 default_vbitrate->setValue(vbParam.replace('k', "").replace('M', "000").toInt());
0285                 cVBitrate->setChecked(true);
0286             } else {
0287                 cVBitrate->setChecked(false);
0288             }
0289 
0290             QString sampAspNum = preset->getParam(QStringLiteral("sample_aspect_num"));
0291             QString sampAspDen = preset->getParam(QStringLiteral("sample_aspect_den"));
0292             QString sampAsp = preset->getParam(QStringLiteral("aspect"));
0293 
0294             if (!(sampAspNum.isEmpty() && sampAspDen.isEmpty())) {
0295                 parNum = sampAspNum.toInt();
0296                 parDen = sampAspDen.toInt();
0297                 cPar->setChecked(true);
0298             } else if (!sampAsp.isEmpty()) {
0299                 QStringList list = sampAsp.split(QStringLiteral("/"));
0300                 if (list.count() == 2) {
0301                     parNum = list.at(0).toInt();
0302                     parDen = list.at(1).toInt();
0303                     cPar->setChecked(true);
0304                 }
0305             }
0306 
0307             if (preset->hasParam(QStringLiteral("display_aspect_num")) && preset->hasParam(QStringLiteral("display_aspect_den"))) {
0308                 displayAspectNum->setValue(preset->getParam(QStringLiteral("display_aspect_num")).toInt());
0309                 displayAspectDen->setValue(preset->getParam(QStringLiteral("display_aspect_den")).toInt());
0310                 cDar->setChecked(true);
0311             } else {
0312                 displayAspectNum->setValue(projectProfile->display_aspect_num());
0313                 displayAspectDen->setValue(projectProfile->display_aspect_den());
0314             }
0315 
0316             vCodecCombo->setCurrentText(preset->getParam(QStringLiteral("vcodec")));
0317             if (!preset->getParam(QStringLiteral("r")).isEmpty()) {
0318                 cFps->setChecked(true);
0319                 double val = preset->getParam(QStringLiteral("r")).toDouble();
0320                 if (val == 23.98 || val == 23.976 || val == (24000 / 1001)) {
0321                     framerateNum->setValue(24000);
0322                     framerateDen->setValue(1001);
0323                 } else if (val == 29.97 || val == (30000 / 1001)) {
0324                     framerateNum->setValue(30000);
0325                     framerateDen->setValue(1001);
0326                 } else if (val == 47.95 || val == (48000 / 1001)) {
0327                     framerateNum->setValue(48000);
0328                     framerateDen->setValue(1001);
0329                 } else if (val == 59.94 || val == (60000 / 1001)) {
0330                     framerateNum->setValue(60000);
0331                     framerateDen->setValue(1001);
0332                 } else {
0333                     framerateNum->setValue(qRound(val * 1000));
0334                     framerateDen->setValue(1000);
0335                 }
0336             } else if (preset->hasParam(QStringLiteral("frame_rate_num")) && preset->hasParam(QStringLiteral("frame_rate_den"))) {
0337                 framerateNum->setValue(preset->getParam(QStringLiteral("frame_rate_num")).toInt());
0338                 framerateDen->setValue(preset->getParam(QStringLiteral("frame_rate_den")).toInt());
0339                 cFps->setChecked(true);
0340             } else {
0341                 framerateNum->setValue(projectProfile->frame_rate_num());
0342                 framerateDen->setValue(projectProfile->frame_rate_den());
0343             }
0344             if (preset->hasParam(QStringLiteral("progressive"))) {
0345                 scanningCombo->setCurrentIndex(preset->getParam(QStringLiteral("progressive")).toInt());
0346                 cScanning->setChecked(true);
0347             }
0348             if (preset->hasParam(QStringLiteral("top_field_first"))) {
0349                 fieldOrderCombo->setCurrentIndex(preset->getParam(QStringLiteral("top_field_first")).toInt());
0350                 cField->setChecked(true);
0351             }
0352             int gopVal = -1;
0353             if (preset->hasParam(QStringLiteral("g"))) {
0354                 gopVal = preset->getParam(QStringLiteral("g")).toInt();
0355                 gopSpinner->setValue(gopVal);
0356             }
0357             if (preset->hasParam(QStringLiteral("sc_threshold")))
0358                 fixedGop->setChecked(preset->getParam(QStringLiteral("sc_threshold")).toInt() == 0);
0359             else if (preset->hasParam(QStringLiteral("keyint_min")))
0360                 fixedGop->setChecked(preset->getParam(QStringLiteral("keyint_min")).toInt() == gopVal);
0361             else if (preset->hasParam(QStringLiteral("strict_gop"))) {
0362                 fixedGop->setChecked(preset->getParam(QStringLiteral("strict_gop")).toInt() == 1);
0363             } else {
0364                 fixedGop->setChecked(false);
0365             }
0366             bFramesSpinner->setValue(preset->getParam(QStringLiteral("bf")).toInt());
0367 
0368             // audio tab
0369             if (preset->hasParam(QStringLiteral("channels"))) {
0370                 int ix = audioChannels->findData(preset->getParam(QStringLiteral("channels")).toInt());
0371                 audioChannels->setCurrentIndex(ix);
0372                 cChannels->setChecked(true);
0373             } else {
0374                 int ix = audioChannels->findData(2);
0375                 audioChannels->setCurrentIndex(ix);
0376             }
0377             aRateControlCombo->setCurrentIndex(preset->audioRateControl());
0378 
0379             if (preset->hasParam(QStringLiteral("channels"))) {
0380                 audioSampleRate->setCurrentText(preset->getParam(QStringLiteral("ar")));
0381                 cSampleR->setChecked(true);
0382             } else {
0383                 audioSampleRate->setCurrentText(QStringLiteral("44100"));
0384             }
0385 
0386             QString aqParam = preset->getParam(QStringLiteral("aq"));
0387             if (aqParam.isEmpty()) {
0388                 aqParam = preset->getParam(QStringLiteral("compression_level"));
0389             }
0390             if (aqParam.contains(QStringLiteral("%audioquality"))) {
0391                 aQuality->setValue(preset->defaultAQuality().toInt());
0392             } else {
0393                 aQuality->setValue(aqParam.toInt());
0394             }
0395             QString abParam = preset->getParam(QStringLiteral("ab"));
0396             if (abParam.contains(QStringLiteral("%audiobitrate"))) {
0397                 aBitrate->setValue(preset->defaultABitrate().toInt());
0398             } else {
0399                 aBitrate->setValue(abParam.replace('k', "").replace('M', "000").toInt());
0400             }
0401 
0402             aCodecCombo->setCurrentText(preset->getParam(QStringLiteral("acodec")));
0403 
0404             // general tab
0405             speeds_list->setText(preset->speeds().join('\n'));
0406             additionalParams->setPlainText(preset->params(m_uiParams));
0407         }
0408     } else {
0409         resHeight->setValue(projectProfile->height());
0410         resWidth->setValue(projectProfile->width());
0411         framerateNum->setValue(projectProfile->frame_rate_num());
0412         framerateDen->setValue(projectProfile->frame_rate_den());
0413         parNum = projectProfile->sample_aspect_num();
0414         parDen = projectProfile->sample_aspect_den();
0415         displayAspectNum->setValue(projectProfile->display_aspect_num());
0416         displayAspectDen->setValue(projectProfile->display_aspect_den());
0417         scanningCombo->setCurrentIndex(projectProfile->progressive() ? 1 : 0);
0418     }
0419 
0420     setPixelAspectRatio(parNum, parDen);
0421 
0422     if (groupName->currentText().isEmpty()) {
0423         groupName->setCurrentText(i18nc("Group Name", "Custom"));
0424     }
0425 
0426     if (mode == Mode::Edit) {
0427         setWindowTitle(i18n("Edit Render Preset"));
0428     }
0429 
0430     connect(preset_name, &QLineEdit::textChanged, this, [&](const QString &text) { buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty()); });
0431 
0432     connect(buttonBox, &QDialogButtonBox::accepted, this, [&, preset, mode]() {
0433         QString oldName;
0434         if (preset) {
0435             oldName = preset->name();
0436         }
0437         QString newPresetName = preset_name->text().simplified();
0438         if (newPresetName.isEmpty()) {
0439             KMessageBox::error(this, i18n("The preset name can't be empty"));
0440             return;
0441         }
0442         QString newGroupName = groupName->currentText().simplified();
0443         if (newGroupName.isEmpty()) {
0444             newGroupName = i18nc("Group Name", "Custom");
0445         }
0446         QString speeds_list_str = speeds_list->toPlainText().replace('\n', ';').simplified();
0447 
0448         std::unique_ptr<RenderPresetModel> newPreset(new RenderPresetModel(newPresetName, newGroupName, parameters->toPlainText().simplified(),
0449                                                                            preset_extension->text().simplified(), QString::number(default_vbitrate->value()),
0450                                                                            QString::number(default_vquality->value()), QString::number(aBitrate->value()),
0451                                                                            QString::number(aQuality->value()), speeds_list_str, m_manualPreset));
0452 
0453         m_saveName = RenderPresetRepository::get()->savePreset(newPreset.get(), mode == Mode::Edit);
0454         if ((mode == Mode::Edit) && !m_saveName.isEmpty() && (oldName != m_saveName)) {
0455             RenderPresetRepository::get()->deletePreset(oldName);
0456         }
0457         accept();
0458     });
0459 
0460     connect(formatCombo, &QComboBox::currentTextChanged, [this](const QString &format) {
0461         if (format == QLatin1String("matroska")) {
0462             preset_extension->setText(QStringLiteral("mkv"));
0463         } else {
0464             preset_extension->setText(format);
0465         }
0466         slotUpdateParams();
0467     });
0468     connect(vCodecCombo, &QComboBox::currentTextChanged, this, &RenderPresetDialog::slotUpdateParams);
0469 
0470     connect(fieldOrderCombo, &QComboBox::currentTextChanged, this, &RenderPresetDialog::slotUpdateParams);
0471     connect(aCodecCombo, &QComboBox::currentTextChanged, this, &RenderPresetDialog::slotUpdateParams);
0472     connect(linkResoultion, &QToolButton::clicked, this, [&]() { m_fixedResRatio = double(resWidth->value()) / double(resHeight->value()); });
0473     connect(resWidth, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [&](int value) {
0474         if (linkResoultion->isChecked()) {
0475             resHeight->blockSignals(true);
0476             resHeight->setValue(qRound(value / m_fixedResRatio));
0477             resHeight->blockSignals(false);
0478         }
0479         updateDisplayAspectRatio();
0480     });
0481     connect(resHeight, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [&](int value) {
0482         if (linkResoultion->isChecked()) {
0483             resWidth->blockSignals(true);
0484             resWidth->setValue(qRound(value * m_fixedResRatio));
0485             resWidth->blockSignals(false);
0486         }
0487         updateDisplayAspectRatio();
0488     });
0489     connect(parCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &RenderPresetDialog::updateDisplayAspectRatio);
0490     auto update_par = [&]() {
0491         int parNum = displayAspectNum->value() * resHeight->value();
0492         int parDen = displayAspectDen->value() * resWidth->value();
0493         setPixelAspectRatio(parNum, parDen);
0494         slotUpdateParams();
0495     };
0496     connect(displayAspectNum, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, update_par);
0497     connect(displayAspectDen, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, update_par);
0498     connect(framerateNum, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [&](int value) {
0499         if (value == 0) {
0500             framerateDen->blockSignals(true);
0501             framerateDen->setValue(0);
0502             framerateDen->blockSignals(false);
0503         }
0504         slotUpdateParams();
0505     });
0506     connect(framerateDen, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [&](int value) {
0507         if (value == 0) {
0508             framerateNum->blockSignals(true);
0509             framerateNum->setValue(0);
0510             framerateNum->blockSignals(false);
0511         }
0512         slotUpdateParams();
0513     });
0514     connect(fixedGop, &QCheckBox::stateChanged, this, &RenderPresetDialog::slotUpdateParams);
0515     connect(bFramesSpinner, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &RenderPresetDialog::slotUpdateParams);
0516     connect(additionalParams, &QPlainTextEdit::textChanged, this, &RenderPresetDialog::slotUpdateParams);
0517 
0518     connect(aRateControlCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &RenderPresetDialog::slotUpdateParams);
0519     connect(aBitrate, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &RenderPresetDialog::slotUpdateParams);
0520     connect(aQuality, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &RenderPresetDialog::slotUpdateParams);
0521     connect(audioChannels, &QComboBox::currentTextChanged, this, &RenderPresetDialog::slotUpdateParams);
0522     connect(audioSampleRate, &QComboBox::currentTextChanged, this, &RenderPresetDialog::slotUpdateParams);
0523 
0524     linkResoultion->setChecked(true);
0525     slotUpdateParams();
0526     connect(cResolution, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0527     connect(cPar, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0528     connect(cDar, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0529     connect(cFps, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0530     connect(cScanning, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0531     connect(cField, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0532     connect(cVBitrate, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0533     connect(cBuffer, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0534     connect(cChannels, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0535     connect(cSampleR, &QCheckBox::toggled, this, &RenderPresetDialog::slotUpdateParams);
0536 
0537     Q_FOREACH (QSpinBox *sp, findChildren<QSpinBox *>()) {
0538         sp->installEventFilter(this);
0539         sp->setFocusPolicy(Qt::StrongFocus);
0540     }
0541     Q_FOREACH (QComboBox *sp, findChildren<QComboBox *>()) {
0542         sp->installEventFilter(this);
0543         sp->setFocusPolicy(Qt::StrongFocus);
0544     }
0545 
0546     // TODO
0547     if (false && m_monitor == nullptr) {
0548         // QString profile = DvdWizardVob::getDvdProfile(format);
0549         /*m_monitor = new Monitor(Kdenlive::RenderMonitor, pCore->monitorManager(), this);
0550         m_monitor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
0551         mainBox->addWidget(m_monitor);
0552         pCore->monitorManager()->appendMonitor(m_monitor);
0553         // m_monitor->setCustomProfile(profile, m_tc);
0554         pCore->monitorManager()->activateMonitor(Kdenlive::RenderMonitor);
0555         m_monitor->start();*/
0556     }
0557 }
0558 
0559 RenderPresetDialog::~RenderPresetDialog()
0560 {
0561     if (m_monitor) {
0562         m_monitor->stop();
0563         pCore->monitorManager()->removeMonitor(m_monitor);
0564         delete m_monitor;
0565     }
0566 }
0567 
0568 bool RenderPresetDialog::eventFilter(QObject *o, QEvent *e)
0569 {
0570     if (e->type() == QEvent::Wheel && (qobject_cast<QComboBox *>(o) || qobject_cast<QAbstractSpinBox *>(o))) {
0571         if (scrollArea->verticalScrollBar()->isVisible() && !qobject_cast<QWidget *>(o)->hasFocus()) {
0572             e->ignore();
0573             return true;
0574         } else {
0575             e->accept();
0576             return false;
0577         }
0578     }
0579     return QWidget::eventFilter(o, e);
0580 }
0581 
0582 void RenderPresetDialog::slotUpdateParams()
0583 {
0584     if (m_manualPreset) {
0585         return;
0586     }
0587     QStringList params;
0588     QString vcodec = vCodecCombo->currentText();
0589     params.append(QStringLiteral("f=%1").arg(formatCombo->currentText()));
0590     // video tab
0591     params.append(QStringLiteral("vcodec=%1").arg(vcodec));
0592     if (cResolution->isChecked()) {
0593         if (resWidth->value() > 0) {
0594             params.append(QStringLiteral("width=%1").arg(resWidth->value()));
0595         }
0596         if (resHeight->value() > 0) {
0597             params.append(QStringLiteral("height=%1").arg(resHeight->value()));
0598         }
0599     }
0600     if (cPar->isChecked()) {
0601         QStringList par = parCombo->currentData().toString().split(QStringLiteral(":"));
0602         if (par.length() >= 2 && par.at(0).toInt() > 0 && par.at(1).toInt() > 0) {
0603             params.append(QStringLiteral("sample_aspect_num=%1 sample_aspect_den=%2").arg(par.at(0).toInt()).arg(par.at(1).toInt()));
0604         }
0605     }
0606     if (cDar->isChecked() && displayAspectNum->value() > 0 && displayAspectDen->value() > 0) {
0607         params.append(QStringLiteral("display_aspect_num=%1 display_aspect_den=%2").arg(displayAspectNum->value()).arg(displayAspectDen->value()));
0608     }
0609 
0610     if (cFps->isChecked() && framerateNum->value() > 0 && framerateDen->value() > 0) {
0611         params.append(QStringLiteral("frame_rate_num=%1").arg(framerateNum->value()));
0612         params.append(QStringLiteral("frame_rate_den=%1").arg(framerateDen->value()));
0613         frameRateDisplay->setText(QString::number(double(framerateNum->value()) / double(framerateDen->value()), 'g', 10));
0614         frameRateDisplay->show();
0615     } else {
0616         frameRateDisplay->hide();
0617     }
0618     // Adjust scanning
0619     if (cScanning->isChecked()) {
0620         params.append(QStringLiteral("progressive=%1").arg(scanningCombo->currentIndex()));
0621         if (cField->isChecked() && scanningCombo->currentIndex() == 0) {
0622             params.append(QStringLiteral("top_field_first=%1").arg(fieldOrderCombo->currentIndex()));
0623         }
0624     }
0625     if (vcodec == "libx265") {
0626         // Most x265 parameters must be supplied through x265-params.
0627         switch (vRateControlCombo->currentIndex()) {
0628         case RenderPresetModel::RateControl::Average:
0629             params.append(QStringLiteral("vb=%bitrate+'k'"));
0630             break;
0631         case RenderPresetModel::RateControl::Constant: {
0632             // x265 does not expect bitrate suffixes and requires Kb/s
0633             params.append(QStringLiteral("vb=%bitrate"));
0634             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0635                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0636             }
0637             break;
0638         }
0639         case RenderPresetModel::RateControl::Quality: {
0640             params.append(QStringLiteral("crf=%quality"));
0641             break;
0642         }
0643         case RenderPresetModel::RateControl::Constrained: {
0644             params.append(QStringLiteral("crf=%quality"));
0645             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0646                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0647             }
0648             if (cVBitrate->isChecked() && cVBitrate->isEnabled()) {
0649                 params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0650             }
0651             break;
0652         }
0653         }
0654         if (fixedGop->isEnabled() && fixedGop->isChecked()) {
0655             params.append(QStringLiteral("sc_threshold=0"));
0656         }
0657     } else if (vcodec.contains("nvenc")) {
0658         switch (vRateControlCombo->currentIndex()) {
0659         case RenderPresetModel::RateControl::Average:
0660             params.append(QStringLiteral("vb=%bitrate+'k'"));
0661             break;
0662         case RenderPresetModel::RateControl::Constant: {
0663             params.append(QStringLiteral("cbr=1 "));
0664             params.append(QStringLiteral("vb=%bitrate+'k'"));
0665             params.append(QStringLiteral("vminrate=%bitrate+'k'"));
0666             params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0667             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0668                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0669             }
0670             break;
0671         }
0672         case RenderPresetModel::RateControl::Quality: {
0673             params.append(QStringLiteral("rc=constqp"));
0674             params.append(QStringLiteral("vglobal_quality=%quality"));
0675             params.append(QStringLiteral("vq=%quality"));
0676             break;
0677         }
0678         case RenderPresetModel::RateControl::Constrained: {
0679             params.append(QStringLiteral("qmin=%quality"));
0680             params.append(QStringLiteral("vb=%cvbr")); // setIfNotSet(p, "vb", qRound(cvbr));
0681             if (cVBitrate->isChecked() && cVBitrate->isEnabled()) {
0682                 params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0683             }
0684             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0685                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0686             }
0687             break;
0688         }
0689         }
0690         /*if (ui->dualPassCheckbox->isChecked())
0691             setIfNotSet(p, "v2pass", 1);
0692         */
0693         if (fixedGop->isEnabled() && fixedGop->isChecked()) {
0694             params.append(QStringLiteral("sc_threshold=0 strict_gop=1"));
0695         }
0696     } else if (vcodec.endsWith("_amf")) {
0697         switch (vRateControlCombo->currentIndex()) {
0698         case RenderPresetModel::RateControl::Average:
0699             params.append(QStringLiteral("vb=%bitrate+'k'"));
0700             break;
0701         case RenderPresetModel::RateControl::Constant: {
0702             params.append(QStringLiteral("rc=cbr "));
0703             params.append(QStringLiteral("vb=%bitrate+'k'"));
0704             params.append(QStringLiteral("vminrate=%bitrate+'k'"));
0705             params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0706             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0707                 params.append(QStringLiteral("vbufsize=%1 ").arg(vBuffer->value() * 8 * 1024));
0708             }
0709             break;
0710         }
0711         case RenderPresetModel::RateControl::Quality: {
0712             params.append(QStringLiteral("rc=cqp"));
0713             params.append(QStringLiteral("qp_i=%quality"));
0714             params.append(QStringLiteral("qp_p=%quality"));
0715             params.append(QStringLiteral("qp_b=%quality"));
0716             params.append(QStringLiteral("vq=%quality"));
0717             break;
0718         }
0719         case RenderPresetModel::RateControl::Constrained: {
0720             params.append(QStringLiteral("rc=vbr_peak"));
0721             params.append(QStringLiteral("qmin=%quality"));
0722             params.append(QStringLiteral("vb=%cvbr")); // setIfNotSet(p, "vb", qRound(cvbr));
0723             if (cVBitrate->isChecked() && cVBitrate->isEnabled()) {
0724                 params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0725             }
0726             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0727                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0728             }
0729             break;
0730         }
0731         }
0732         /*if (ui->dualPassCheckbox->isChecked())
0733             setIfNotSet(p, "v2pass", 1);
0734             */
0735         if (fixedGop->isEnabled() && fixedGop->isChecked()) {
0736             params.append(QStringLiteral("sc_threshold=0 strict_gop=1"));
0737         }
0738     } else {
0739         switch (vRateControlCombo->currentIndex()) {
0740         case RenderPresetModel::RateControl::Average:
0741             params.append(QStringLiteral("vb=%bitrate+'k'"));
0742             break;
0743         case RenderPresetModel::RateControl::Constant: {
0744             // const QString& b = ui->videoBitrateCombo->currentText();
0745             params.append(QStringLiteral("vb=%bitrate+'k'"));
0746             params.append(QStringLiteral("vminrate=%bitrate+'k'"));
0747             params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0748             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0749                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0750             }
0751             break;
0752         }
0753         case RenderPresetModel::RateControl::Quality: {
0754             if (vcodec.startsWith("libx264")) {
0755                 params.append(QStringLiteral("crf=%quality"));
0756             } else if (vcodec.startsWith("libvpx") || vcodec.startsWith("libaom-")) {
0757                 params.append(QStringLiteral("crf=%quality"));
0758                 params.append(QStringLiteral("vb=0")); // VP9 needs this to prevent constrained quality mode.
0759             } else if (vcodec.endsWith("_vaapi")) {
0760                 params.append(QStringLiteral("vglobal_quality=%quality"));
0761                 params.append(QStringLiteral("vq=%quality"));
0762             } else {
0763                 params.append(QStringLiteral("qscale=%quality"));
0764             }
0765             break;
0766         }
0767         case RenderPresetModel::RateControl::Constrained: {
0768             if (vcodec.startsWith("libx264") || vcodec.startsWith("libvpx") || vcodec.startsWith("libaom-")) {
0769                 params.append(QStringLiteral("crf=%quality"));
0770             } else if (vcodec.endsWith("_qsv") || vcodec.endsWith("_videotoolbox")) {
0771                 params.append(QStringLiteral("vb=%cvbr")); // setIfNotSet(p, "vb", qRound(cvbr));
0772             } else if (vcodec.endsWith("_vaapi")) {
0773                 params.append(QStringLiteral("vb=%bitrate+'k'"));
0774                 params.append(QStringLiteral("vglobal_quality=%quality"));
0775                 params.append(QStringLiteral("vq=%quality"));
0776             } else {
0777                 params.append(QStringLiteral("qscale=%quality"));
0778             }
0779             if (cVBitrate->isChecked() && cVBitrate->isEnabled()) {
0780                 params.append(QStringLiteral("vmaxrate=%bitrate+'k'"));
0781             }
0782             if (cBuffer->isChecked() && cBuffer->isEnabled()) {
0783                 params.append(QStringLiteral("vbufsize=%1").arg(vBuffer->value() * 8 * 1024));
0784             }
0785             break;
0786         }
0787         }
0788         if (fixedGop->isEnabled() && fixedGop->isChecked()) {
0789             if (vcodec.startsWith("libvpx") || vcodec.startsWith("libaom-")) {
0790                 params.append(QStringLiteral("keyint_min=%1").arg(gopSpinner->value()));
0791             } else {
0792                 params.append(QStringLiteral("sc_threshold=0"));
0793             }
0794         }
0795         /*
0796         if (vcodec.endsWith("_videotoolbox")) {
0797             setIfNotSet(p, "pix_fmt", "nv12");
0798         } else if (vcodec.endsWith("_vaapi")) {
0799             setIfNotSet(p, "vprofile", "main");
0800             setIfNotSet(p, "connection_type", "x11");
0801         }*/
0802     }
0803     if (gopSpinner->value() > 0) {
0804         params.append(QStringLiteral("g=%1").arg(gopSpinner->value()));
0805         if (bFramesSpinner->value() >= 0) {
0806             params.append(QStringLiteral("bf=%1").arg(bFramesSpinner->value()));
0807         }
0808     }
0809 
0810     // audio tab
0811     QString acodec = aCodecCombo->currentText();
0812     params.append(QStringLiteral("acodec=%1").arg(acodec));
0813 
0814     if (cChannels->isChecked() && audioChannels->currentData().toInt() > 0) {
0815         params.append(QStringLiteral("channels=%1").arg(audioChannels->currentData().toInt()));
0816     }
0817     if (cSampleR->isChecked() && audioSampleRate->currentText().toInt() > 0) {
0818         params.append(QStringLiteral("ar=%1").arg(audioSampleRate->currentText().toInt()));
0819     }
0820     if (acodec.startsWith(QStringLiteral("pcm_"))) {
0821         aRateControlCombo->setEnabled(false);
0822         aBitrate->setEnabled(false);
0823         aQuality->setEnabled(false);
0824     } else {
0825         aRateControlCombo->setEnabled(true);
0826         switch (aRateControlCombo->currentIndex()) {
0827         case RenderPresetModel::RateControl::Average:
0828         case RenderPresetModel::RateControl::Constant:
0829             aBitrate->setEnabled(true);
0830             aQuality->setEnabled(false);
0831             break;
0832         case RenderPresetModel::RateControl::Quality:
0833         default:
0834             aBitrate->setEnabled(false);
0835             aQuality->setEnabled(true);
0836             break;
0837         };
0838         if (aRateControlCombo->currentIndex() == RenderPresetModel::RateControl::Average ||
0839             aRateControlCombo->currentIndex() == RenderPresetModel::RateControl::Constant) {
0840             params.append(QStringLiteral("ab=%audiobitrate+'k'"));
0841             if (acodec == "libopus") {
0842                 if (aRateControlCombo->currentIndex() == RenderPresetModel::RateControl::Constant) {
0843                     params.append(QStringLiteral("vbr=off"));
0844                 } else {
0845                     params.append(QStringLiteral("vbr=constrained"));
0846                 }
0847             }
0848         } else if (acodec == "libopus") {
0849             params.append(QStringLiteral("vbr=on"));
0850             params.append(QStringLiteral("compression_level=%audioquality"));
0851         } else {
0852             params.append(QStringLiteral("aq=%audioquality"));
0853         }
0854     }
0855 
0856     QString addionalParams = additionalParams->toPlainText().simplified();
0857 
0858     QStringList removed;
0859     for (const auto &p : qAsConst(m_uiParams)) {
0860         QString store = addionalParams;
0861         if (store != addionalParams.remove(QRegularExpression(QStringLiteral("((^|\\s)%1=\\S*)").arg(p)))) {
0862             removed.append(p);
0863         }
0864     }
0865     if (!removed.isEmpty()) {
0866         overrideParamsWarning->setText(
0867             xi18nc("@info",
0868                    "The following parameters will not have an effect, because they get overwritten by the equivalent user interface options: <icode>%1</icode>",
0869                    removed.join(", ")));
0870         overrideParamsWarning->show();
0871     } else {
0872         overrideParamsWarning->hide();
0873     }
0874     addionalParams = addionalParams.simplified().prepend(params.join(QStringLiteral(" ")) + QStringLiteral(" "));
0875     parameters->setPlainText(addionalParams.simplified());
0876 }
0877 
0878 void RenderPresetDialog::setPixelAspectRatio(int num, int den)
0879 {
0880     parCombo->blockSignals(true);
0881     if (num < 1) num = 1;
0882     if (den < 1) den = 1;
0883     int gcdV = gcd(num, den);
0884     QString data = QStringLiteral("%1:%2").arg(num / gcdV).arg(den / gcdV);
0885     int ix = parCombo->findData(data);
0886     if (ix < 0) {
0887         parCombo->addItem(QStringLiteral("%L1 (%2:%3)").arg(double(num) / double(den), 0, 'g', 8).arg(num / gcdV).arg(den / gcdV), data);
0888         ix = parCombo->count() - 1;
0889     }
0890     parCombo->setCurrentIndex(ix);
0891     parCombo->blockSignals(false);
0892 }
0893 
0894 void RenderPresetDialog::updateDisplayAspectRatio()
0895 {
0896     displayAspectNum->blockSignals(true);
0897     displayAspectDen->blockSignals(true);
0898     QStringList par = parCombo->currentData().toString().split(QStringLiteral(":"));
0899     int parNum = resWidth->value();
0900     int parDen = resHeight->value();
0901     if (par.length() >= 2 && par.at(0).toInt() > 0 && par.at(1).toInt() > 0) {
0902         parNum *= par.at(0).toInt();
0903         parDen *= par.at(1).toInt();
0904     }
0905     int gcdV = gcd(parNum, parDen);
0906     displayAspectNum->setValue(parNum / gcdV);
0907     displayAspectDen->setValue(parDen / gcdV);
0908     displayAspectNum->blockSignals(false);
0909     displayAspectDen->blockSignals(false);
0910     slotUpdateParams();
0911 };
0912 
0913 QString RenderPresetDialog::saveName()
0914 {
0915     return m_saveName;
0916 }