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