Warning, file /multimedia/kdenlive/src/dialogs/encodingprofilesdialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2008 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003 
0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "encodingprofilesdialog.h"
0008 #include "core.h"
0009 #include "kdenlivesettings.h"
0010 #include "profiles/profilemodel.hpp"
0011 #include "profiles/profilerepository.hpp"
0012 
0013 #include "klocalizedstring.h"
0014 #include <KMessageWidget>
0015 #include <QLineEdit>
0016 #include <QPlainTextEdit>
0017 #include <QStandardItemModel>
0018 #include <QStandardPaths>
0019 #include <QVBoxLayout>
0020 
0021 QString EncodingProfilesManager::configGroupName(ProfileType type)
0022 {
0023     QString groupName;
0024     switch (type) {
0025     case ProfileType::ProxyClips:
0026         groupName = QStringLiteral("proxy");
0027         break;
0028     case ProfileType::V4LCapture:
0029         groupName = QStringLiteral("video4linux");
0030         break;
0031     case ProfileType::ScreenCapture:
0032         groupName = QStringLiteral("screengrab");
0033         break;
0034     case ProfileType::DecklinkCapture:
0035         groupName = QStringLiteral("decklink");
0036         break;
0037     case ProfileType::TimelinePreview:
0038     default:
0039         groupName = QStringLiteral("timelinepreview");
0040         break;
0041     }
0042     return groupName;
0043 }
0044 
0045 EncodingProfilesDialog::EncodingProfilesDialog(EncodingProfilesManager::ProfileType profileType, QWidget *parent)
0046     : QDialog(parent)
0047     , m_configGroup(nullptr)
0048 {
0049     setupUi(this);
0050     setWindowTitle(i18nc("@title:window", "Manage Encoding Profiles"));
0051     profile_type->addItem(i18n("Proxy Clips"), EncodingProfilesManager::ProxyClips);
0052     profile_type->addItem(i18n("Timeline Preview"), EncodingProfilesManager::TimelinePreview);
0053     profile_type->addItem(i18n("Video4Linux Capture"), EncodingProfilesManager::V4LCapture);
0054     profile_type->addItem(i18n("Screen Capture"), EncodingProfilesManager::ScreenCapture);
0055     profile_type->addItem(i18n("Decklink Capture"), EncodingProfilesManager::DecklinkCapture);
0056 
0057     m_configFile = new KConfig(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0058     profile_type->setCurrentIndex(profileType);
0059     connect(profile_type, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &EncodingProfilesDialog::slotLoadProfiles);
0060     connect(profile_list, &QListWidget::currentRowChanged, this, &EncodingProfilesDialog::slotShowParams);
0061     connect(button_delete, &QAbstractButton::clicked, this, &EncodingProfilesDialog::slotDeleteProfile);
0062     connect(button_add, &QAbstractButton::clicked, this, &EncodingProfilesDialog::slotAddProfile);
0063     connect(button_edit, &QAbstractButton::clicked, this, &EncodingProfilesDialog::slotEditProfile);
0064     profile_parameters->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
0065     slotLoadProfiles();
0066 }
0067 
0068 EncodingProfilesDialog::~EncodingProfilesDialog()
0069 {
0070     delete m_configGroup;
0071     delete m_configFile;
0072 }
0073 
0074 void EncodingProfilesDialog::slotLoadProfiles()
0075 {
0076     profile_list->blockSignals(true);
0077     profile_list->clear();
0078 
0079     delete m_configGroup;
0080     m_configGroup =
0081         new KConfigGroup(m_configFile, EncodingProfilesManager::configGroupName(EncodingProfilesManager::ProfileType(profile_type->currentIndex())));
0082     QMap<QString, QString> values = m_configGroup->entryMap();
0083     QMapIterator<QString, QString> i(values);
0084     while (i.hasNext()) {
0085         i.next();
0086         auto *item = new QListWidgetItem(i.key(), profile_list);
0087         item->setData(Qt::UserRole, i.value());
0088         // cout << i.key() << ": " << i.value() << endl;
0089     }
0090     profile_list->blockSignals(false);
0091     profile_list->setCurrentRow(0);
0092     const bool multiProfile(profile_list->count() > 0);
0093     button_delete->setEnabled(multiProfile);
0094     button_edit->setEnabled(multiProfile);
0095 }
0096 
0097 void EncodingProfilesDialog::slotShowParams()
0098 {
0099     profile_parameters->clear();
0100     QListWidgetItem *item = profile_list->currentItem();
0101     if (!item) {
0102         return;
0103     }
0104     profile_parameters->setPlainText(item->data(Qt::UserRole).toString().section(QLatin1Char(';'), 0, 0));
0105 }
0106 
0107 void EncodingProfilesDialog::slotDeleteProfile()
0108 {
0109     QListWidgetItem *item = profile_list->currentItem();
0110     if (!item) {
0111         return;
0112     }
0113     QString profile = item->text();
0114     m_configGroup->deleteEntry(profile);
0115     slotLoadProfiles();
0116 }
0117 
0118 void EncodingProfilesDialog::slotAddProfile()
0119 {
0120     QPointer<QDialog> d = new QDialog(this);
0121     auto *l = new QVBoxLayout;
0122     l->addWidget(new QLabel(i18n("Profile name:")));
0123     auto *pname = new QLineEdit;
0124     l->addWidget(pname);
0125     l->addWidget(new QLabel(i18n("Parameters:")));
0126     auto *pparams = new QPlainTextEdit;
0127     l->addWidget(pparams);
0128     l->addWidget(new QLabel(i18n("File extension:")));
0129     auto *pext = new QLineEdit;
0130     l->addWidget(pext);
0131     QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
0132     connect(box, &QDialogButtonBox::accepted, d.data(), &QDialog::accept);
0133     connect(box, &QDialogButtonBox::rejected, d.data(), &QDialog::reject);
0134     l->addWidget(box);
0135     d->setLayout(l);
0136 
0137     QListWidgetItem *item = profile_list->currentItem();
0138     if (item) {
0139         QString profilestr = item->data(Qt::UserRole).toString();
0140         pparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
0141         pext->setText(profilestr.section(QLatin1Char(';'), 1, 1));
0142     }
0143     if (d->exec() == QDialog::Accepted) {
0144         m_configGroup->writeEntry(pname->text(), pparams->toPlainText() + QLatin1Char(';') + pext->text());
0145         slotLoadProfiles();
0146     }
0147     delete d;
0148 }
0149 
0150 void EncodingProfilesDialog::slotEditProfile()
0151 {
0152     QPointer<QDialog> d = new QDialog(this);
0153     auto *l = new QVBoxLayout;
0154     l->addWidget(new QLabel(i18n("Profile name:")));
0155     auto *pname = new QLineEdit;
0156     l->addWidget(pname);
0157     l->addWidget(new QLabel(i18n("Parameters:")));
0158     auto *pparams = new QPlainTextEdit;
0159     l->addWidget(pparams);
0160     l->addWidget(new QLabel(i18n("File extension:")));
0161     auto *pext = new QLineEdit;
0162     l->addWidget(pext);
0163     QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
0164     connect(box, &QDialogButtonBox::accepted, d.data(), &QDialog::accept);
0165     connect(box, &QDialogButtonBox::rejected, d.data(), &QDialog::reject);
0166     l->addWidget(box);
0167     d->setLayout(l);
0168 
0169     QListWidgetItem *item = profile_list->currentItem();
0170     if (item) {
0171         pname->setText(item->text());
0172         QString profilestr = item->data(Qt::UserRole).toString();
0173         pparams->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
0174         pext->setText(profilestr.section(QLatin1Char(';'), 1, 1));
0175         pparams->setFocus();
0176     }
0177     if (d->exec() == QDialog::Accepted) {
0178         m_configGroup->writeEntry(pname->text(), pparams->toPlainText().simplified() + QLatin1Char(';') + pext->text());
0179         slotLoadProfiles();
0180     }
0181     delete d;
0182 }
0183 
0184 EncodingProfilesChooser::EncodingProfilesChooser(QWidget *parent, EncodingProfilesManager::ProfileType type, bool showAutoItem, const QString &configName,
0185                                                  bool native)
0186     : QWidget(parent)
0187     , m_type(type)
0188     , m_showAutoItem(showAutoItem)
0189 {
0190     QVBoxLayout *verticalLayout = new QVBoxLayout(this);
0191     verticalLayout->setContentsMargins(0, 0, 0, 0);
0192     m_profilesCombo = new QComboBox(this);
0193     if (!configName.isEmpty()) {
0194         m_profilesCombo->setObjectName(QStringLiteral("kcfg_%1").arg(configName));
0195     }
0196 
0197     QToolButton *buttonConfigure = new QToolButton();
0198     buttonConfigure->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0199     buttonConfigure->setText(i18n("Configure profiles"));
0200     buttonConfigure->setToolTip(i18n("Manage Encoding Profiles"));
0201     QToolButton *buttonInfo = new QToolButton();
0202     buttonInfo->setCheckable(true);
0203     buttonInfo->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
0204     buttonConfigure->setToolTip(i18n("Show Profile Parameters"));
0205 
0206     m_info = new QPlainTextEdit();
0207     m_info->setReadOnly(true);
0208     m_info->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 4);
0209     QHBoxLayout *hor = new QHBoxLayout;
0210     hor->addWidget(m_profilesCombo);
0211     hor->addWidget(buttonConfigure);
0212     hor->addWidget(buttonInfo);
0213 
0214     // Message widget
0215     m_messageWidget = new KMessageWidget(this);
0216     m_messageWidget->setWordWrap(true);
0217     m_messageWidget->setCloseButtonVisible(true);
0218 
0219     verticalLayout->addLayout(hor);
0220     verticalLayout->addWidget(m_info);
0221     verticalLayout->addWidget(m_messageWidget);
0222     m_info->setVisible(false);
0223     m_messageWidget->hide();
0224     connect(buttonConfigure, &QAbstractButton::clicked, this, &EncodingProfilesChooser::slotManageEncodingProfile);
0225     connect(buttonInfo, &QAbstractButton::clicked, m_info, &QWidget::setVisible);
0226     connect(m_profilesCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &EncodingProfilesChooser::slotUpdateProfile);
0227     if (native) {
0228         loadEncodingProfiles();
0229         if (!configName.isEmpty()) {
0230             KConfigGroup resourceConfig(KSharedConfig::openConfig(), "project");
0231             int ix = resourceConfig.readEntry(configName).toInt();
0232             m_profilesCombo->setCurrentIndex(ix);
0233             slotUpdateProfile(ix);
0234         }
0235     }
0236     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
0237 }
0238 
0239 void EncodingProfilesChooser::slotManageEncodingProfile()
0240 {
0241     QPointer<EncodingProfilesDialog> dia = new EncodingProfilesDialog(m_type);
0242     dia->exec();
0243     delete dia;
0244     loadEncodingProfiles();
0245     filterPreviewProfiles(pCore->getCurrentProfilePath());
0246 }
0247 
0248 void EncodingProfilesChooser::loadEncodingProfiles()
0249 {
0250     QSignalBlocker bk(m_profilesCombo);
0251     QString currentItem = m_profilesCombo->currentText();
0252     m_profilesCombo->clear();
0253     if (m_showAutoItem) {
0254         if (m_type == EncodingProfilesManager::TimelinePreview && (KdenliveSettings::nvencEnabled() || KdenliveSettings::vaapiEnabled())) {
0255             m_profilesCombo->addItem(QIcon::fromTheme(QStringLiteral("speedometer")), i18n("Automatic"));
0256         } else {
0257             m_profilesCombo->addItem(i18n("Automatic"));
0258         }
0259     }
0260 
0261     KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0262     KConfigGroup group(&conf, EncodingProfilesManager::configGroupName(m_type));
0263     QMap<QString, QString> values = group.entryMap();
0264     QMapIterator<QString, QString> i(values);
0265     while (i.hasNext()) {
0266         i.next();
0267         if (!i.key().isEmpty()) {
0268             m_profilesCombo->addItem(i.key(), i.value());
0269         }
0270     }
0271     if (!currentItem.isEmpty()) {
0272         int ix = m_profilesCombo->findText(currentItem);
0273         m_profilesCombo->setCurrentIndex(ix);
0274         slotUpdateProfile(ix);
0275     }
0276 }
0277 
0278 QString EncodingProfilesChooser::currentExtension()
0279 {
0280     QString profilestr = m_profilesCombo->currentData().toString();
0281     if (profilestr.isEmpty()) {
0282         return {};
0283     }
0284     return profilestr.section(QLatin1Char(';'), 1, 1);
0285 }
0286 
0287 QString EncodingProfilesChooser::currentParams()
0288 {
0289     QString profilestr = m_profilesCombo->currentData().toString();
0290     if (profilestr.isEmpty()) {
0291         return {};
0292     }
0293     return profilestr.section(QLatin1Char(';'), 0, 0);
0294 }
0295 
0296 void EncodingProfilesChooser::slotUpdateProfile(int ix)
0297 {
0298     m_info->clear();
0299     QString profilestr = m_profilesCombo->itemData(ix).toString();
0300     if (!profilestr.isEmpty()) {
0301         m_info->setPlainText(profilestr.section(QLatin1Char(';'), 0, 0));
0302     }
0303 }
0304 
0305 void EncodingProfilesChooser::hideMessage()
0306 {
0307     m_messageWidget->hide();
0308 }
0309 
0310 void EncodingProfilesChooser::filterPreviewProfiles(const QString & /*profile*/) {}
0311 
0312 EncodingTimelinePreviewProfilesChooser::EncodingTimelinePreviewProfilesChooser(QWidget *parent, bool showAutoItem, const QString &defaultValue,
0313                                                                                bool selectFromConfig)
0314     : EncodingProfilesChooser(parent, EncodingProfilesManager::TimelinePreview, showAutoItem, QString(), false)
0315 {
0316     loadEncodingProfiles();
0317     if (selectFromConfig) {
0318         KConfigGroup resourceConfig(KSharedConfig::openConfig(), "project");
0319         int ix = resourceConfig.readEntry(defaultValue).toInt();
0320         m_profilesCombo->setCurrentIndex(ix);
0321         slotUpdateProfile(ix);
0322     } else if (!defaultValue.isEmpty()) {
0323         int ix = m_profilesCombo->findData(defaultValue);
0324         if (ix == -1) {
0325             m_profilesCombo->addItem(i18n("Current Settings"), defaultValue);
0326             ix = m_profilesCombo->findData(defaultValue);
0327         }
0328         if (ix > -1) {
0329             m_profilesCombo->setCurrentIndex(ix);
0330             slotUpdateProfile(ix);
0331         }
0332     }
0333     connect(m_profilesCombo, &KComboBox::currentIndexChanged, m_messageWidget, &KMessageWidget::hide);
0334     connect(m_profilesCombo, &KComboBox::currentIndexChanged, this, &EncodingTimelinePreviewProfilesChooser::currentIndexChanged);
0335 }
0336 
0337 void EncodingTimelinePreviewProfilesChooser::loadEncodingProfiles()
0338 {
0339     QSignalBlocker bk(m_profilesCombo);
0340     QString currentItem = m_profilesCombo->currentText();
0341     m_profilesCombo->clear();
0342     if (m_showAutoItem) {
0343         if (KdenliveSettings::nvencEnabled() || KdenliveSettings::vaapiEnabled()) {
0344             m_profilesCombo->addItem(QIcon::fromTheme(QStringLiteral("speedometer")), i18n("Automatic"));
0345         } else {
0346             m_profilesCombo->addItem(i18n("Automatic"));
0347         }
0348     }
0349 
0350     KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
0351     KConfigGroup group(&conf, EncodingProfilesManager::configGroupName(m_type));
0352     QMap<QString, QString> values = group.entryMap();
0353     QMapIterator<QString, QString> i(values);
0354     while (i.hasNext()) {
0355         i.next();
0356         if (!i.key().isEmpty()) {
0357             // We filter out incompatible profiles
0358             if (i.value().contains(QLatin1String("nvenc"))) {
0359                 if (KdenliveSettings::nvencEnabled()) {
0360                     m_profilesCombo->addItem(QIcon::fromTheme(QStringLiteral("speedometer")), i.key(), i.value());
0361                 }
0362                 continue;
0363             }
0364             if (i.value().contains(QLatin1String("vaapi"))) {
0365                 if (KdenliveSettings::vaapiEnabled()) {
0366                     m_profilesCombo->addItem(QIcon::fromTheme(QStringLiteral("speedometer")), i.key(), i.value());
0367                 }
0368                 continue;
0369             }
0370             m_profilesCombo->addItem(i.key(), i.value());
0371         }
0372     }
0373     if (!currentItem.isEmpty()) {
0374         int ix = m_profilesCombo->findText(currentItem);
0375         m_profilesCombo->setCurrentIndex(ix);
0376         slotUpdateProfile(ix);
0377     }
0378 }
0379 
0380 void EncodingTimelinePreviewProfilesChooser::filterPreviewProfiles(const QString &profile)
0381 {
0382     QStandardItemModel *model = qobject_cast<QStandardItemModel *>(m_profilesCombo->model());
0383     Q_ASSERT(model != nullptr);
0384     int max = m_profilesCombo->count();
0385     int current = m_profilesCombo->currentIndex();
0386     double projectFps = ProfileRepository::get()->getProfile(profile)->fps();
0387     for (int i = 0; i < max; i++) {
0388         QString itemData = m_profilesCombo->itemData(i).toString();
0389         double fps = 0.;
0390         if (itemData.startsWith(QStringLiteral("r="))) {
0391             QString fpsString = itemData.section(QLatin1Char('='), 1).section(QLatin1Char(' '), 0, 0);
0392             fps = fpsString.toDouble();
0393         } else if (itemData.contains(QStringLiteral(" r="))) {
0394             QString fpsString = itemData.section(QLatin1String(" r="), 1).section(QLatin1Char(' '), 0, 0);
0395             // This profile has a hardcoded framerate, chack if same as project
0396             fps = fpsString.toDouble();
0397         }
0398         QStandardItem *item = model->item(i);
0399         if (fps > 0. && qAbs(fps - projectFps) > 0.01) {
0400             // Fps does not match, disable
0401             item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
0402             continue;
0403         }
0404         item->setFlags(item->flags() | Qt::ItemIsEnabled);
0405     }
0406     QStandardItem *item = model->item(current);
0407     if (!item || !(item->flags() & Qt::ItemIsEnabled)) {
0408         // Currently selected profile is not usable, switch back to automatic
0409         for (int i = 0; i < max; i++) {
0410             if (m_profilesCombo->itemData(i).isNull()) {
0411                 m_profilesCombo->setCurrentIndex(i);
0412                 m_messageWidget->setText(i18n("Selected Timeline preview profile is not compatible with the project framerate,\nreverting to Automatic."));
0413                 m_messageWidget->animatedShow();
0414                 break;
0415             }
0416         }
0417     }
0418 }