1 /*
2 SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
3
4 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6
7 #include "wizard.h"
8 #include "kdenlivesettings.h"
9 #include "profiles/profilemodel.hpp"
10 #include "profiles/profilerepository.hpp"
11 #include "profilesdialog.h"
12 #include "effects/effectsrepository.hpp"
13
14 #include "utils/thememanager.h"
15 #ifdef USE_V4L
16 #include "capture/v4lcapture.h"
17 #endif
18 #include "core.h"
19 #include <config-kdenlive.h>
20
21 #include <framework/mlt_version.h>
22 #include <mlt++/Mlt.h>
23
24 #include <KMessageWidget>
25 #include <KProcess>
26 #include <KRun>
27 #include <klocalizedstring.h>
28
29 #include "kdenlive_debug.h"
30 #include <QCheckBox>
31 #include <QFile>
32 #include <QLabel>
33 #include <QMimeDatabase>
34 #include <QMimeType>
35 #include <QPushButton>
36 #include <QStandardPaths>
37 #include <QTemporaryFile>
38 #include <QTimer>
39 #include <QApplication>
40 #include <kio_version.h>
41 #include <QXmlStreamWriter>
42
43 #if KIO_VERSION >= QT_VERSION_CHECK(5,71,0)
44 #include <KIO/OpenUrlJob>
45 #include <KIO/JobUiDelegate>
46 #endif
47
48 // Recommended MLT version
49 const int mltVersionMajor = MLT_MIN_MAJOR_VERSION;
50 const int mltVersionMinor = MLT_MIN_MINOR_VERSION;
51 const int mltVersionRevision = MLT_MIN_PATCH_VERSION;
52
53 static const char kdenlive_version[] = KDENLIVE_VERSION;
54
55 static QStringList acodecsList;
56 static QStringList vcodecsList;
57
MyWizardPage(QWidget * parent)58 MyWizardPage::MyWizardPage(QWidget *parent)
59 : QWizardPage(parent)
60
61 {
62 }
63
setComplete(bool complete)64 void MyWizardPage::setComplete(bool complete)
65 {
66 m_isComplete = complete;
67 }
68
isComplete() const69 bool MyWizardPage::isComplete() const
70 {
71 return m_isComplete;
72 }
73
Wizard(bool autoClose,bool appImageCheck,QWidget * parent)74 Wizard::Wizard(bool autoClose, bool appImageCheck, QWidget *parent)
75 : QWizard(parent)
76 , m_systemCheckIsOk(false)
77 , m_brokenModule(false)
78 {
79 setWindowTitle(i18n("Welcome to Kdenlive"));
80 int logoHeight = int(fontMetrics().height() * 2.5);
81 setWizardStyle(QWizard::ModernStyle);
82 setOption(QWizard::NoBackButtonOnLastPage, true);
83 // setOption(QWizard::ExtendedWatermarkPixmap, false);
84 m_page = new MyWizardPage(this);
85 m_page->setTitle(i18n("Welcome to Kdenlive %1", QString(kdenlive_version)));
86 m_page->setSubTitle(i18n("Using MLT %1", mlt_version_get_string()));
87 setPixmap(QWizard::LogoPixmap, QIcon::fromTheme(QStringLiteral(":/pics/kdenlive.png")).pixmap(logoHeight, logoHeight));
88 m_startLayout = new QVBoxLayout;
89 m_errorWidget = new KMessageWidget(this);
90 m_startLayout->addWidget(m_errorWidget);
91 m_errorWidget->setCloseButtonVisible(false);
92 m_errorWidget->hide();
93 m_page->setLayout(m_startLayout);
94 addPage(m_page);
95
96 /*QWizardPage *page2 = new QWizardPage;
97 page2->setTitle(i18n("Video Standard"));
98 m_standard.setupUi(page2);*/
99 setButtonText(QWizard::CancelButton, i18n("Abort"));
100 setButtonText(QWizard::FinishButton, i18n("OK"));
101 slotCheckMlt();
102 if (autoClose) {
103 // This is a first run instance, check HW encoders
104 testHwEncoders();
105 } else {
106 QPair<QStringList, QStringList> conversion = EffectsRepository::get()->fixDeprecatedEffects();
107 if (conversion.first.count() > 0) {
108 QLabel *lab = new QLabel(this);
109 lab->setText(i18n("Converting old custom effects successful:"));
110 m_startLayout->addWidget(lab);
111 auto *list = new QListWidget(this);
112 m_startLayout->addWidget(list);
113 list->addItems(conversion.first);
114 }
115 if (conversion.second.count() > 0) {
116 QLabel *lab = new QLabel(this);
117 lab->setText(i18n("Converting old custom effects failed:"));
118 m_startLayout->addWidget(lab);
119 auto *list = new QListWidget(this);
120 m_startLayout->addWidget(list);
121 list->addItems(conversion.second);
122 }
123 }
124 if (!m_errors.isEmpty() || !m_warnings.isEmpty() || (!m_infos.isEmpty() && !appImageCheck)) {
125 QLabel *lab = new QLabel(this);
126 lab->setText(i18n("Startup error or warning, check our <a href='#'>online manual</a>."));
127 connect(lab, &QLabel::linkActivated, this, &Wizard::slotOpenManual);
128 m_startLayout->addWidget(lab);
129 }
130 if (!m_infos.isEmpty()) {
131 auto *errorLabel = new KMessageWidget(this);
132 errorLabel->setText(QStringLiteral("<ul>") + m_infos + QStringLiteral("</ul>"));
133 errorLabel->setMessageType(KMessageWidget::Information);
134 errorLabel->setWordWrap(true);
135 errorLabel->setCloseButtonVisible(false);
136 m_startLayout->addWidget(errorLabel);
137 errorLabel->show();
138 }
139 if (m_errors.isEmpty() && m_warnings.isEmpty()) {
140 // Everything is ok only some info message, show codec status
141 m_page->setComplete(true);
142 if (autoClose) {
143 QTimer::singleShot(0, this, &QDialog::accept);
144 return;
145 }
146 auto *lab = new KMessageWidget(this);
147 lab->setText(i18n("Codecs have been updated, everything seems fine."));
148 lab->setMessageType(KMessageWidget::Positive);
149 lab->setCloseButtonVisible(false);
150 m_startLayout->addWidget(lab);
151 // HW accel
152 QCheckBox *cb = new QCheckBox(i18n("VAAPI hardware acceleration"), this);
153 m_startLayout->addWidget(cb);
154 cb->setChecked(KdenliveSettings::vaapiEnabled());
155 QCheckBox *cbn = new QCheckBox(i18n("NVIDIA hardware acceleration"), this);
156 m_startLayout->addWidget(cbn);
157 cbn->setChecked(KdenliveSettings::nvencEnabled());
158 QPushButton *pb = new QPushButton(i18n("Check hardware acceleration"), this);
159 connect(pb, &QPushButton::clicked, this, [&, cb, cbn, pb]() {
160 testHwEncoders();
161 pb->setEnabled(false);
162 cb->setChecked(KdenliveSettings::vaapiEnabled());
163 cbn->setChecked(KdenliveSettings::nvencEnabled());
164 updateHwStatus();
165 pb->setEnabled(true);
166 });
167 m_startLayout->addWidget(pb);
168 setOption(QWizard::NoCancelButton, true);
169 return;
170 }
171 if (!m_errors.isEmpty()) {
172 auto *errorLabel = new KMessageWidget(this);
173 errorLabel->setText(QStringLiteral("<ul>") + m_errors + QStringLiteral("</ul>"));
174 errorLabel->setMessageType(KMessageWidget::Error);
175 errorLabel->setWordWrap(true);
176 errorLabel->setCloseButtonVisible(false);
177 m_startLayout->addWidget(errorLabel);
178 m_page->setComplete(false);
179 errorLabel->show();
180 if (!autoClose) {
181 setButtonText(QWizard::CancelButton, i18n("Close"));
182 }
183 } else {
184 m_page->setComplete(true);
185 if (!autoClose) {
186 setOption(QWizard::NoCancelButton, true);
187 }
188 }
189 if (!m_warnings.isEmpty()) {
190 auto *errorLabel = new KMessageWidget(this);
191 errorLabel->setText(QStringLiteral("<ul>") + m_warnings + QStringLiteral("</ul>"));
192 errorLabel->setMessageType(KMessageWidget::Warning);
193 errorLabel->setWordWrap(true);
194 errorLabel->setCloseButtonVisible(false);
195 m_startLayout->addWidget(errorLabel);
196 errorLabel->show();
197 }
198 // build profiles lists
199 /*QMap<QString, QString> profilesInfo = ProfilesDialog::getProfilesInfo();
200 QMap<QString, QString>::const_iterator i = profilesInfo.constBegin();
201 while (i != profilesInfo.constEnd()) {
202 QMap< QString, QString > profileData = ProfilesDialog::getSettingsFromFile(i.key());
203 if (profileData.value(QStringLiteral("width")) == QLatin1String("720")) m_dvProfiles.insert(i.value(), i.key());
204 else if (profileData.value(QStringLiteral("width")).toInt() >= 1080) m_hdvProfiles.insert(i.value(), i.key());
205 else m_otherProfiles.insert(i.value(), i.key());
206 ++i;
207 }
208
209 m_standard.button_all->setChecked(true);
210 connect(m_standard.button_all, SIGNAL(toggled(bool)), this, SLOT(slotCheckStandard()));
211 connect(m_standard.button_hdv, SIGNAL(toggled(bool)), this, SLOT(slotCheckStandard()));
212 connect(m_standard.button_dv, SIGNAL(toggled(bool)), this, SLOT(slotCheckStandard()));
213 slotCheckStandard();
214 connect(m_standard.profiles_list, SIGNAL(itemSelectionChanged()), this, SLOT(slotCheckSelectedItem()));
215
216 // select default profile
217 if (!KdenliveSettings::default_profile().isEmpty()) {
218 for (int i = 0; i < m_standard.profiles_list->count(); ++i) {
219 if (m_standard.profiles_list->item(i)->data(Qt::UserRole).toString() == KdenliveSettings::default_profile()) {
220 m_standard.profiles_list->setCurrentRow(i);
221 m_standard.profiles_list->scrollToItem(m_standard.profiles_list->currentItem());
222 break;
223 }
224 }
225 }
226
227 setPage(2, page2);
228
229 QWizardPage *page3 = new QWizardPage;
230 page3->setTitle(i18n("Additional Settings"));
231 m_extra.setupUi(page3);
232 m_extra.projectfolder->setMode(KFile::Directory);
233 m_extra.projectfolder->setUrl(QUrl(KdenliveSettings::defaultprojectfolder()));
234 m_extra.videothumbs->setChecked(KdenliveSettings::videothumbnails());
235 m_extra.audiothumbs->setChecked(KdenliveSettings::audiothumbnails());
236 m_extra.autosave->setChecked(KdenliveSettings::crashrecovery());
237 connect(m_extra.videothumbs, SIGNAL(stateChanged(int)), this, SLOT(slotCheckThumbs()));
238 connect(m_extra.audiothumbs, SIGNAL(stateChanged(int)), this, SLOT(slotCheckThumbs()));
239 slotCheckThumbs();
240 addPage(page3);*/
241
242 #ifndef Q_WS_MAC
243 /*QWizardPage *page6 = new QWizardPage;
244 page6->setTitle(i18n("Capture device"));
245 m_capture.setupUi(page6);
246 bool found_decklink = Render::getBlackMagicDeviceList(m_capture.decklink_devices);
247 KdenliveSettings::setDecklink_device_found(found_decklink);
248 if (found_decklink) m_capture.decklink_status->setText(i18n("Default Blackmagic Decklink card:"));
249 else m_capture.decklink_status->setText(i18n("No Blackmagic Decklink device found"));
250 connect(m_capture.decklink_devices, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDecklinkDevice(int)));
251 connect(m_capture.button_reload, SIGNAL(clicked()), this, SLOT(slotDetectWebcam()));
252 connect(m_capture.v4l_devices, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCaptureParameters()));
253 connect(m_capture.v4l_formats, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSaveCaptureFormat()));
254 m_capture.button_reload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));*/
255
256 #endif
257
258 // listViewDelegate = new WizardDelegate(treeWidget);
259 // m_check.programList->setItemDelegate(listViewDelegate);
260 // slotDetectWebcam();
261 // QTimer::singleShot(500, this, SLOT(slotCheckMlt()));
262 }
263
slotDetectWebcam()264 void Wizard::slotDetectWebcam()
265 {
266 #ifdef USE_V4L
267 m_capture.v4l_devices->blockSignals(true);
268 m_capture.v4l_devices->clear();
269
270 // Video 4 Linux device detection
271 for (int i = 0; i < 10; ++i) {
272 QString path = "/dev/video" + QString::number(i);
273 if (QFile::exists(path)) {
274 QStringList deviceInfo = V4lCaptureHandler::getDeviceName(path.toUtf8().constData());
275 if (!deviceInfo.isEmpty()) {
276 m_capture.v4l_devices->addItem(deviceInfo.at(0), path);
277 m_capture.v4l_devices->setItemData(m_capture.v4l_devices->count() - 1, deviceInfo.at(1), Qt::UserRole + 1);
278 }
279 }
280 }
281 if (m_capture.v4l_devices->count() > 0) {
282 m_capture.v4l_status->setText(i18n("Default video4linux device:"));
283 // select default device
284 bool found = false;
285 for (int i = 0; i < m_capture.v4l_devices->count(); ++i) {
286 QString device = m_capture.v4l_devices->itemData(i).toString();
287 if (device == KdenliveSettings::video4vdevice()) {
288 m_capture.v4l_devices->setCurrentIndex(i);
289 found = true;
290 break;
291 }
292 }
293 slotUpdateCaptureParameters();
294 if (!found) {
295 m_capture.v4l_devices->setCurrentIndex(0);
296 }
297 } else {
298 m_capture.v4l_status->setText(i18n("No device found, plug your webcam and refresh."));
299 }
300 m_capture.v4l_devices->blockSignals(false);
301 #endif /* USE_V4L */
302 }
303
slotUpdateCaptureParameters()304 void Wizard::slotUpdateCaptureParameters()
305 {
306 QString device = m_capture.v4l_devices->itemData(m_capture.v4l_devices->currentIndex()).toString();
307 if (!device.isEmpty()) {
308 KdenliveSettings::setVideo4vdevice(device);
309 }
310
311 QString formats = m_capture.v4l_devices->itemData(m_capture.v4l_devices->currentIndex(), Qt::UserRole + 1).toString();
312
313 m_capture.v4l_formats->blockSignals(true);
314 m_capture.v4l_formats->clear();
315
316 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
317 if (!dir.exists()) {
318 dir.mkpath(QStringLiteral("."));
319 }
320
321 if (ProfileRepository::get()->profileExists(dir.absoluteFilePath(QStringLiteral("video4linux")))) {
322 auto &profileInfo = ProfileRepository::get()->getProfile(dir.absoluteFilePath(QStringLiteral("video4linux")));
323 m_capture.v4l_formats->addItem(i18n("Current settings (%1x%2, %3/%4fps)", profileInfo->width(), profileInfo->height(), profileInfo->frame_rate_num(),
324 profileInfo->frame_rate_den()),
325 QStringList() << QStringLiteral("unknown") << QString::number(profileInfo->width())
326 << QString::number(profileInfo->height()) << QString::number(profileInfo->frame_rate_num())
327 << QString::number(profileInfo->frame_rate_den()));
328 }
329 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
330 QStringList pixelformats = formats.split('>', QString::SkipEmptyParts);
331 #else
332 QStringList pixelformats = formats.split('>', Qt::SkipEmptyParts);
333 #endif
334 QString itemSize;
335 QString pixelFormat;
336 QStringList itemRates;
337 for (int i = 0; i < pixelformats.count(); ++i) {
338 QString format = pixelformats.at(i).section(QLatin1Char(':'), 0, 0);
339 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
340 QStringList sizes = pixelformats.at(i).split(':', QString::SkipEmptyParts);
341 #else
342 QStringList sizes = pixelformats.at(i).split(':', Qt::SkipEmptyParts);
343 #endif
344 pixelFormat = sizes.takeFirst();
345 for (int j = 0; j < sizes.count(); ++j) {
346 itemSize = sizes.at(j).section(QLatin1Char('='), 0, 0);
347 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
348 itemRates = sizes.at(j).section(QLatin1Char('='), 1, 1).split(QLatin1Char(','), QString::SkipEmptyParts);
349 #else
350 itemRates = sizes.at(j).section(QLatin1Char('='), 1, 1).split(QLatin1Char(','), Qt::SkipEmptyParts);
351 #endif
352 for (int k = 0; k < itemRates.count(); ++k) {
353 QString formatDescription =
354 QLatin1Char('[') + format + QStringLiteral("] ") + itemSize + QStringLiteral(" (") + itemRates.at(k) + QLatin1Char(')');
355 if (m_capture.v4l_formats->findText(formatDescription) == -1) {
356 m_capture.v4l_formats->addItem(formatDescription, QStringList() << format << itemSize.section('x', 0, 0) << itemSize.section('x', 1, 1)
357 << itemRates.at(k).section(QLatin1Char('/'), 0, 0)
358 << itemRates.at(k).section(QLatin1Char('/'), 1, 1));
359 }
360 }
361 }
362 }
363 if (!dir.exists(QStringLiteral("video4linux"))) {
364 if (m_capture.v4l_formats->count() > 9) {
365 slotSaveCaptureFormat();
366 } else {
367 // No existing profile and no autodetected profiles
368 std::unique_ptr<ProfileParam> profileInfo(new ProfileParam(pCore->getCurrentProfile().get()));
369 profileInfo->m_width = 320;
370 profileInfo->m_height = 200;
371 profileInfo->m_frame_rate_num = 15;
372 profileInfo->m_frame_rate_den = 1;
373 profileInfo->m_display_aspect_num = 4;
374 profileInfo->m_display_aspect_den = 3;
375 profileInfo->m_sample_aspect_num = 1;
376 profileInfo->m_sample_aspect_den = 1;
377 profileInfo->m_progressive = true;
378 profileInfo->m_colorspace = 601;
379 ProfileRepository::get()->saveProfile(profileInfo.get(), dir.absoluteFilePath(QStringLiteral("video4linux")));
380 m_capture.v4l_formats->addItem(i18n("Default settings (%1x%2, %3/%4fps)", profileInfo->width(), profileInfo->height(),
381 profileInfo->frame_rate_num(), profileInfo->frame_rate_den()),
382 QStringList()
383 << QStringLiteral("unknown") << QString::number(profileInfo->width()) << QString::number(profileInfo->height())
384 << QString::number(profileInfo->frame_rate_num()) << QString::number(profileInfo->frame_rate_den()));
385 }
386 }
387 m_capture.v4l_formats->blockSignals(false);
388 }
389
checkMltComponents()390 void Wizard::checkMltComponents()
391 {
392 m_brokenModule = false;
393 if (!pCore->getMltRepository()) {
394 m_errors.append(i18n("<li>Cannot start MLT backend, check your installation</li>"));
395 m_systemCheckIsOk = false;
396 } else {
397 int mltVersion = (mltVersionMajor << 16) + (mltVersionMinor << 8) + mltVersionRevision;
398 int runningVersion = mlt_version_get_int();
399 if (runningVersion < mltVersion) {
400 m_errors.append(
401 i18n("<li>Unsupported MLT version<br/>Please <b>upgrade</b> to %1.%2.%3</li>", mltVersionMajor, mltVersionMinor, mltVersionRevision));
402 m_systemCheckIsOk = false;
403 }
404 // Retrieve the list of available transitions.
405 Mlt::Properties *producers = pCore->getMltRepository()->producers();
406 QStringList producersItemList;
407 producersItemList.reserve(producers->count());
408 for (int i = 0; i < producers->count(); ++i) {
409 producersItemList << producers->get_name(i);
410 }
411 delete producers;
412
413 // Check that we have the frei0r effects installed
414 Mlt::Properties *filters = pCore->getMltRepository()->filters();
415 bool hasFrei0r = false;
416 QString filterName;
417 for (int i = 0; i < filters->count(); ++i) {
418 filterName = filters->get_name(i);
419 if (filterName.startsWith(QStringLiteral("frei0r."))) {
420 hasFrei0r = true;
421 break;
422 }
423 }
424 delete filters;
425 if (!hasFrei0r) {
426 // Frei0r effects not found
427 qDebug() << "Missing Frei0r module";
428 m_warnings.append(
429 i18n("<li>Missing package: <b>Frei0r</b> effects (frei0r-plugins)<br/>provides many effects and transitions. Install recommended</li>"));
430 }
431
432 #if(!(defined(Q_OS_WIN)||defined(Q_OS_MAC)))
433 // Check that we have the breeze icon theme installed
434 const QStringList iconPaths = QIcon::themeSearchPaths();
435 bool hasBreeze = false;
436 for (const QString &path : iconPaths) {
437 QDir dir(path);
438 if (dir.exists(QStringLiteral("breeze"))) {
439 hasBreeze = true;
440 break;
441 }
442 }
443 if (!hasBreeze) {
444 // Breeze icons not found
445 qDebug() << "Missing Breeze icons";
446 m_warnings.append(
447 i18n("<li>Missing package: <b>Breeze</b> icons (breeze-icon-theme)<br/>provides many icons used in Kdenlive. Install recommended</li>"));
448 }
449 #endif
450
451 Mlt::Properties *consumers = pCore->getMltRepository()->consumers();
452 QStringList consumersItemList;
453 consumersItemList.reserve(consumers->count());
454 for (int i = 0; i < consumers->count(); ++i) {
455 consumersItemList << consumers->get_name(i);
456 }
457 delete consumers;
458 if (consumersItemList.contains(QStringLiteral("sdl2_audio"))) {
459 // MLT >= 6.6.0 and SDL2 module
460 KdenliveSettings::setSdlAudioBackend(QStringLiteral("sdl2_audio"));
461 KdenliveSettings::setAudiobackend(QStringLiteral("sdl2_audio"));
462 #if defined(Q_OS_WIN)
463 // Use wasapi by default on Windows
464 KdenliveSettings::setAudiodrivername(QStringLiteral("wasapi"));
465 #endif
466 } else if (consumersItemList.contains(QStringLiteral("sdl_audio"))) {
467 // MLT < 6.6.0
468 KdenliveSettings::setSdlAudioBackend(QStringLiteral("sdl_audio"));
469 KdenliveSettings::setAudiobackend(QStringLiteral("sdl_audio"));
470 } else if (consumersItemList.contains(QStringLiteral("rtaudio"))) {
471 KdenliveSettings::setSdlAudioBackend(QStringLiteral("sdl2_audio"));
472 KdenliveSettings::setAudiobackend(QStringLiteral("rtaudio"));
473 } else {
474 // SDL module
475 m_errors.append(i18n("<li>Missing MLT module: <b>sdl</b> or <b>rtaudio</b><br/>required for audio output</li>"));
476 m_systemCheckIsOk = false;
477 }
478
479 Mlt::Consumer *consumer = nullptr;
480 Mlt::Profile p;
481 // XML module
482 if (consumersItemList.contains(QStringLiteral("xml"))) {
483 consumer = new Mlt::Consumer(p, "xml");
484 }
485 if (consumer == nullptr || !consumer->is_valid()) {
486 qDebug() << "Missing XML MLT module";
487 m_errors.append(i18n("<li>Missing MLT module: <b>xml</b> <br/>required for audio/video</li>"));
488 m_systemCheckIsOk = true;
489 }
490 // AVformat module
491 consumer = nullptr;
492 if (consumersItemList.contains(QStringLiteral("avformat"))) {
493 consumer = new Mlt::Consumer(p, "avformat");
494 }
495 if (consumer == nullptr || !consumer->is_valid()) {
496 qDebug() << "Missing AVFORMAT MLT module";
497 m_warnings.append(i18n("<li>Missing MLT module: <b>avformat</b> (FFmpeg)<br/>required for audio/video</li>"));
498 m_brokenModule = true;
499 } else {
500 consumer->set("vcodec", "list");
501 consumer->set("acodec", "list");
502 consumer->set("f", "list");
503 consumer->start();
504 Mlt::Properties vcodecs(mlt_properties(consumer->get_data("vcodec")));
505 for (int i = 0; i < vcodecs.count(); ++i) {
506 vcodecsList << QString(vcodecs.get(i));
507 }
508 Mlt::Properties acodecs(mlt_properties(consumer->get_data("acodec")));
509 for (int i = 0; i < acodecs.count(); ++i) {
510 acodecsList << QString(acodecs.get(i));
511 }
512 checkMissingCodecs();
513 delete consumer;
514 }
515
516 // Image module
517 if (!producersItemList.contains(QStringLiteral("qimage")) && !producersItemList.contains(QStringLiteral("pixbuf"))) {
518 qDebug() << "Missing image MLT module";
519 m_warnings.append(i18n("<li>Missing MLT module: <b>qimage</b> or <b>pixbuf</b><br/>required for images and titles</li>"));
520 m_brokenModule = true;
521 }
522
523 // Titler module
524 if (!producersItemList.contains(QStringLiteral("kdenlivetitle"))) {
525 qDebug() << "Missing TITLER MLT module";
526 m_warnings.append(i18n("<li>Missing MLT module: <b>kdenlivetitle</b><br/>required to create titles</li>"));
527 KdenliveSettings::setHastitleproducer(false);
528 m_brokenModule = true;
529 } else {
530 KdenliveSettings::setHastitleproducer(true);
531 }
532 }
533 if (m_systemCheckIsOk && !m_brokenModule) {
534 // everything is ok
535 return;
536 }
537 if (!m_systemCheckIsOk || m_brokenModule) {
538 // Something is wrong with install
539 if (!m_systemCheckIsOk) {
540 // WARN
541 }
542 } else {
543 // OK
544 }
545 }
546
checkMissingCodecs()547 void Wizard::checkMissingCodecs()
548 {
549 bool replaceVorbisCodec = false;
550 if (acodecsList.contains(QStringLiteral("libvorbis"))) {
551 replaceVorbisCodec = true;
552 }
553 bool replaceLibfaacCodec = false;
554 if (!acodecsList.contains(QStringLiteral("aac")) && acodecsList.contains(QStringLiteral("libfaac"))) {
555 replaceLibfaacCodec = true;
556 }
557 QStringList profilesList;
558 profilesList << QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("export/profiles.xml"));
559 QDir directory = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/export/"));
560 QStringList filter;
561 filter << QStringLiteral("*.xml");
562 const QStringList fileList = directory.entryList(filter, QDir::Files);
563 for (const QString &filename : fileList) {
564 profilesList << directory.absoluteFilePath(filename);
565 }
566
567 // We should parse customprofiles.xml in last position, so that user profiles
568 // can also override profiles installed by KNewStuff
569 QStringList requiredACodecs;
570 QStringList requiredVCodecs;
571 for (const QString &filename : qAsConst(profilesList)) {
572 QDomDocument doc;
573 QFile file(filename);
574 doc.setContent(&file, false);
575 file.close();
576 QString std;
577 QString format;
578 QDomNodeList profiles = doc.elementsByTagName(QStringLiteral("profile"));
579 for (int i = 0; i < profiles.count(); ++i) {
580 std = profiles.at(i).toElement().attribute(QStringLiteral("args"));
581 format.clear();
582 if (std.startsWith(QLatin1String("acodec="))) {
583 format = std.section(QStringLiteral("acodec="), 1, 1);
584 } else if (std.contains(QStringLiteral(" acodec="))) {
585 format = std.section(QStringLiteral(" acodec="), 1, 1);
586 }
587 if (!format.isEmpty()) {
588 requiredACodecs << format.section(QLatin1Char(' '), 0, 0).toLower();
589 }
590 format.clear();
591 if (std.startsWith(QLatin1String("vcodec="))) {
592 format = std.section(QStringLiteral("vcodec="), 1, 1);
593 } else if (std.contains(QStringLiteral(" vcodec="))) {
594 format = std.section(QStringLiteral(" vcodec="), 1, 1);
595 }
596 if (!format.isEmpty()) {
597 requiredVCodecs << format.section(QLatin1Char(' '), 0, 0).toLower();
598 }
599 }
600 }
601 requiredACodecs.removeDuplicates();
602 requiredVCodecs.removeDuplicates();
603 if (replaceVorbisCodec) {
604 int ix = requiredACodecs.indexOf(QStringLiteral("vorbis"));
605 if (ix > -1) {
606 requiredACodecs.replace(ix, QStringLiteral("libvorbis"));
607 }
608 }
609 if (replaceLibfaacCodec) {
610 int ix = requiredACodecs.indexOf(QStringLiteral("aac"));
611 if (ix > -1) {
612 requiredACodecs.replace(ix, QStringLiteral("libfaac"));
613 }
614 }
615 for (int i = 0; i < acodecsList.count(); ++i) {
616 requiredACodecs.removeAll(acodecsList.at(i));
617 }
618 for (int i = 0; i < vcodecsList.count(); ++i) {
619 requiredVCodecs.removeAll(vcodecsList.at(i));
620 }
621 /*
622 * Info about missing codecs is given in render widget, no need to put this at first start
623 * if (!requiredACodecs.isEmpty() || !requiredVCodecs.isEmpty()) {
624 QString missing = requiredACodecs.join(QLatin1Char(','));
625 if (!missing.isEmpty() && !requiredVCodecs.isEmpty()) {
626 missing.append(',');
627 }
628 missing.append(requiredVCodecs.join(QLatin1Char(',')));
629 missing.prepend(i18n("The following codecs were not found on your system. Check our <a href=''>online manual</a> if you need them: "));
630 m_infos.append(QString("<li>%1</li>").arg(missing));
631 }*/
632 }
633
slotCheckPrograms(QString & infos,QString & warnings)634 void Wizard::slotCheckPrograms(QString &infos, QString &warnings)
635 {
636 // Check first in same folder as melt exec
637 const QStringList mltpath({QFileInfo(KdenliveSettings::rendererpath()).canonicalPath(), qApp->applicationDirPath()});
638 QString exepath;
639 if (KdenliveSettings::ffmpegpath().isEmpty() || !QFileInfo::exists(KdenliveSettings::ffmpegpath())) {
640 exepath = QStandardPaths::findExecutable(QString("ffmpeg%1").arg(FFMPEG_SUFFIX), mltpath);
641 if (exepath.isEmpty()) {
642 exepath = QStandardPaths::findExecutable(QString("ffmpeg%1").arg(FFMPEG_SUFFIX));
643 }
644 qDebug() << "Found FFMpeg binary: "<<exepath;
645 if (exepath.isEmpty()) {
646 // Check for libav version
647 exepath = QStandardPaths::findExecutable(QStringLiteral("avconv"));
648 if (exepath.isEmpty()) {
649 warnings.append(i18n("<li>Missing app: <b>ffmpeg</b><br/>required for proxy clips and transcoding</li>"));
650 }
651 }
652 }
653 QString playpath;
654 if (KdenliveSettings::ffplaypath().isEmpty() || !QFileInfo::exists(KdenliveSettings::ffplaypath())) {
655 playpath = QStandardPaths::findExecutable(QStringLiteral("ffplay%1").arg(FFMPEG_SUFFIX), mltpath);
656 if (playpath.isEmpty()) {
657 playpath = QStandardPaths::findExecutable(QStringLiteral("ffplay%1").arg(FFMPEG_SUFFIX));
658 }
659 if (playpath.isEmpty()) {
660 // Check for libav version
661 playpath = QStandardPaths::findExecutable(QStringLiteral("avplay"));
662 if (playpath.isEmpty()) {
663 infos.append(i18n("<li>Missing app: <b>ffplay</b><br/>recommended for some preview jobs</li>"));
664 }
665 }
666 }
667 QString probepath;
668 if (KdenliveSettings::ffprobepath().isEmpty() || !QFileInfo::exists(KdenliveSettings::ffprobepath())) {
669 probepath = QStandardPaths::findExecutable(QStringLiteral("ffprobe%1").arg(FFMPEG_SUFFIX), mltpath);
670 if (probepath.isEmpty()) {
671 probepath = QStandardPaths::findExecutable(QStringLiteral("ffprobe%1").arg(FFMPEG_SUFFIX));
672 }
673 if (probepath.isEmpty()) {
674 // Check for libav version
675 probepath = QStandardPaths::findExecutable(QStringLiteral("avprobe"));
676 if (probepath.isEmpty()) {
677 infos.append(i18n("<li>Missing app: <b>ffprobe</b><br/>recommended for extra clip analysis</li>"));
678 }
679 }
680 }
681
682 if (!exepath.isEmpty()) {
683 KdenliveSettings::setFfmpegpath(exepath);
684 }
685 if (!playpath.isEmpty()) {
686 KdenliveSettings::setFfplaypath(playpath);
687 }
688 if (!probepath.isEmpty()) {
689 KdenliveSettings::setFfprobepath(probepath);
690 }
691
692 // Deprecated
693 /*
694 #ifndef Q_WS_MAC
695 item = new QTreeWidgetItem(m_treeWidget, QStringList() << QString() << i18n("dvgrab"));
696 item->setData(1, Qt::UserRole, i18n("Required for firewire capture"));
697 item->setSizeHint(0, m_itemSize);
698 if (QStandardPaths::findExecutable(QStringLiteral("dvgrab")).isEmpty()) item->setIcon(0, m_badIcon);
699 else item->setIcon(0, m_okIcon);
700 #endif
701 */
702
703 // set up some default applications
704 QString program;
705 if (KdenliveSettings::defaultimageapp().isEmpty()) {
706 program = QStandardPaths::findExecutable(QStringLiteral("gimp"));
707 if (program.isEmpty()) {
708 program = QStandardPaths::findExecutable(QStringLiteral("krita"));
709 }
710 if (!program.isEmpty()) {
711 KdenliveSettings::setDefaultimageapp(program);
712 }
713 }
714 if (KdenliveSettings::defaultaudioapp().isEmpty()) {
715 program = QStandardPaths::findExecutable(QStringLiteral("audacity"));
716 if (program.isEmpty()) {
717 program = QStandardPaths::findExecutable(QStringLiteral("traverso"));
718 }
719 if (!program.isEmpty()) {
720 KdenliveSettings::setDefaultaudioapp(program);
721 }
722 }
723
724 if (KdenliveSettings::defaultaudioapp().isEmpty()) {
725 program = QStandardPaths::findExecutable(QStringLiteral("audacity"));
726 if (program.isEmpty()) {
727 program = QStandardPaths::findExecutable(QStringLiteral("traverso"));
728 }
729 if (!program.isEmpty()) {
730 KdenliveSettings::setDefaultaudioapp(program);
731 }
732 }
733
734 if (KdenliveSettings::mediainfopath().isEmpty() || !QFileInfo::exists(KdenliveSettings::mediainfopath())) {
735 program = QStandardPaths::findExecutable(QStringLiteral("mediainfo"));
736 #ifdef Q_OS_WIN
737 if (program.isEmpty()) {
738 program = QStandardPaths::findExecutable(QStringLiteral("mediainfo"), {"C:/Program Files/MediaInfo", "C:/Program Files (x86)/MediaInfo"});
739 }
740 #endif
741 if (program.isEmpty()) {
742 infos.append(i18n("<li>Missing app: <b>mediainfo</b><br/>optional for technical clip information</li>"));
743 } else {
744 KdenliveSettings::setMediainfopath(program);
745 }
746 }
747 }
748
installExtraMimes(const QString & baseName,const QStringList & globs)749 void Wizard::installExtraMimes(const QString &baseName, const QStringList &globs)
750 {
751 QMimeDatabase db;
752 QString mimefile = baseName;
753 mimefile.replace('/', '-');
754 QMimeType mime = db.mimeTypeForName(baseName);
755 QStringList missingGlobs;
756
757 for (const QString &glob : globs) {
758 QMimeType type = db.mimeTypeForFile(glob, QMimeDatabase::MatchExtension);
759 QString mimeName = type.name();
760 if (!mimeName.contains(QStringLiteral("audio")) && !mimeName.contains(QStringLiteral("video"))) {
761 missingGlobs << glob;
762 }
763 }
764 if (missingGlobs.isEmpty()) {
765 return;
766 }
767 if (!mime.isValid() || mime.isDefault()) {
768 qCDebug(KDENLIVE_LOG) << "MIME type " << baseName << " not found";
769 } else {
770 QStringList extensions = mime.globPatterns();
771 QString comment = mime.comment();
772 for (const QString &glob : qAsConst(missingGlobs)) {
773 if (!extensions.contains(glob)) {
774 extensions << glob;
775 }
776 }
777 // qCDebug(KDENLIVE_LOG) << "EXTS: " << extensions;
778 QDir mimeDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/mime/packages/"));
779 if (!mimeDir.exists()) {
780 mimeDir.mkpath(QStringLiteral("."));
781 }
782 QString packageFileName = mimeDir.absoluteFilePath(mimefile + QStringLiteral(".xml"));
783 // qCDebug(KDENLIVE_LOG) << "INSTALLING NEW MIME TO: " << packageFileName;
784 QFile packageFile(packageFileName);
785 if (!packageFile.open(QIODevice::WriteOnly)) {
786 qCCritical(KDENLIVE_LOG) << "Couldn't open" << packageFileName << "for writing";
787 return;
788 }
789 QXmlStreamWriter writer(&packageFile);
790 writer.setAutoFormatting(true);
791 writer.writeStartDocument();
792
793 const QString nsUri = QStringLiteral("http://www.freedesktop.org/standards/shared-mime-info");
794 writer.writeDefaultNamespace(nsUri);
795 writer.writeStartElement(QStringLiteral("mime-info"));
796 writer.writeStartElement(nsUri, QStringLiteral("mime-type"));
797 writer.writeAttribute(QStringLiteral("type"), baseName);
798
799 if (!comment.isEmpty()) {
800 writer.writeStartElement(nsUri, QStringLiteral("comment"));
801 writer.writeCharacters(comment);
802 writer.writeEndElement(); // comment
803 }
804
805 for (const QString &pattern : qAsConst(extensions)) {
806 writer.writeStartElement(nsUri, QStringLiteral("glob"));
807 writer.writeAttribute(QStringLiteral("pattern"), pattern);
808 writer.writeEndElement(); // glob
809 }
810
811 writer.writeEndElement(); // mime-info
812 writer.writeEndElement(); // mime-type
813 writer.writeEndDocument();
814 }
815 }
816
runUpdateMimeDatabase()817 void Wizard::runUpdateMimeDatabase()
818 {
819 const QString localPackageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/mime/");
820 // Q_ASSERT(!localPackageDir.isEmpty());
821 KProcess proc;
822 proc << QStringLiteral("update-mime-database");
823 proc << localPackageDir;
824 const int exitCode = proc.execute();
825 if (exitCode != 0) {
826 qCWarning(KDENLIVE_LOG) << proc.program() << "exited with error code" << exitCode;
827 }
828 }
829
slotCheckStandard()830 void Wizard::slotCheckStandard()
831 {
832 m_standard.profiles_list->clear();
833 QStringList profiles;
834 if (!m_standard.button_hdv->isChecked()) {
835 // DV standard
836 QMapIterator<QString, QString> i(m_dvProfiles);
837 while (i.hasNext()) {
838 i.next();
839 auto *item = new QListWidgetItem(i.key(), m_standard.profiles_list);
840 item->setData(Qt::UserRole, i.value());
841 }
842 }
843 if (!m_standard.button_dv->isChecked()) {
844 // HDV standard
845 QMapIterator<QString, QString> i(m_hdvProfiles);
846 while (i.hasNext()) {
847 i.next();
848 auto *item = new QListWidgetItem(i.key(), m_standard.profiles_list);
849 item->setData(Qt::UserRole, i.value());
850 }
851 }
852 if (m_standard.button_all->isChecked()) {
853 QMapIterator<QString, QString> i(m_otherProfiles);
854 while (i.hasNext()) {
855 i.next();
856 auto *item = new QListWidgetItem(i.key(), m_standard.profiles_list);
857 item->setData(Qt::UserRole, i.value());
858 }
859 // m_standard.profiles_list->sortItems();
860 }
861
862 for (int i = 0; i < m_standard.profiles_list->count(); ++i) {
863 QListWidgetItem *item = m_standard.profiles_list->item(i);
864
865 std::unique_ptr<ProfileModel> &curProfile = ProfileRepository::get()->getProfile(item->data(Qt::UserRole).toString());
866 const QString infoString =
867 QStringLiteral("<strong>") + i18n("Frame size:") +
868 QStringLiteral(" </strong>%1x%2<br /><strong>").arg(curProfile->width()).arg(curProfile->height()) + i18n("Frame rate:") +
869 QStringLiteral(" </strong>%1/%2<br /><strong>").arg(curProfile->frame_rate_num()).arg(curProfile->frame_rate_den()) + i18n("Pixel aspect ratio:") +
870 QStringLiteral("</strong>%1/%2<br /><strong>").arg(curProfile->sample_aspect_num()).arg(curProfile->sample_aspect_den()) +
871 i18n("Display aspect ratio:") + QStringLiteral(" </strong>%1/%2").arg(curProfile->display_aspect_num()).arg(curProfile->display_aspect_den());
872
873 /*const QString infoString = QStringLiteral("<strong>" + i18n("Frame size:") + QStringLiteral(" </strong>%1x%2<br /><strong>") + i18n("Frame rate:") +
874 QStringLiteral(" </strong>%3/%4<br /><strong>") + i18n("Pixel aspect ratio:") +
875 QStringLiteral("</strong>%5/%6<br /><strong>") + i18n("Display aspect ratio:") + QStringLiteral(" </strong>%7/%8"))
876 .arg(QString::number(curProfile->width()), QString::number(curProfile->height()),
877 QString::number(curProfile->frame_rate_num()), QString::number(curProfile->frame_rate_den()),
878 QString::number(curProfile->sample_aspect_num()), QString::number(curProfile->sample_aspect_den()),
879 QString::number(curProfile->display_aspect_num()), QString::number(curProfile->display_aspect_den()));*/
880 item->setToolTip(infoString);
881 }
882
883 m_standard.profiles_list->setSortingEnabled(true);
884 m_standard.profiles_list->setCurrentRow(0);
885 }
886
slotCheckSelectedItem()887 void Wizard::slotCheckSelectedItem()
888 {
889 // Make sure we always have an item highlighted
890 m_standard.profiles_list->setCurrentRow(m_standard.profiles_list->currentRow());
891 }
892
adjustSettings()893 void Wizard::adjustSettings()
894 {
895 // if (m_extra.installmimes->isChecked()) {
896 {
897 QStringList globs;
898
899 globs << QStringLiteral("*.mts") << QStringLiteral("*.m2t") << QStringLiteral("*.mod") << QStringLiteral("*.ts") << QStringLiteral("*.m2ts")
900 << QStringLiteral("*.m2v");
901 installExtraMimes(QStringLiteral("video/mpeg"), globs);
902 globs.clear();
903 globs << QStringLiteral("*.dv");
904 installExtraMimes(QStringLiteral("video/dv"), globs);
905 runUpdateMimeDatabase();
906 }
907 }
908
slotCheckMlt()909 void Wizard::slotCheckMlt()
910 {
911 QString errorMessage;
912 if (KdenliveSettings::rendererpath().isEmpty()) {
913 errorMessage.append(i18n("Your MLT installation cannot be found. Install MLT and restart Kdenlive.\n"));
914 }
915
916 if (!errorMessage.isEmpty()) {
917 errorMessage.prepend(QStringLiteral("<b>%1</b><br />").arg(i18n("Fatal Error")));
918 QLabel *pix = new QLabel();
919 pix->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(30));
920 QLabel *label = new QLabel(errorMessage);
921 label->setWordWrap(true);
922 m_startLayout->addSpacing(40);
923 m_startLayout->addWidget(pix);
924 m_startLayout->addWidget(label);
925 m_systemCheckIsOk = false;
926 // Warn
927 } else {
928 m_systemCheckIsOk = true;
929 }
930
931 if (m_systemCheckIsOk) {
932 checkMltComponents();
933 }
934 slotCheckPrograms(m_infos, m_warnings);
935 }
936
isOk() const937 bool Wizard::isOk() const
938 {
939 return m_systemCheckIsOk;
940 }
941
slotOpenManual()942 void Wizard::slotOpenManual()
943 {
944 #if KIO_VERSION >= QT_VERSION_CHECK(5,71,0)
945 auto *job = new KIO::OpenUrlJob(QUrl(QStringLiteral("https://docs.kdenlive.org/troubleshooting/installation_troubleshooting.html")));
946 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
947 // methods like setRunExecutables, setSuggestedFilename, setEnableExternalBrowser, setFollowRedirections
948 // exist in both classes
949 job->start();
950 //KIO::OpenUrlJob(QUrl(QStringLiteral("https://docs.kdenlive.org/troubleshooting/installation_troubleshooting.html")), QStringLiteral("text/html"));
951 #else
952 KRun::runUrl(QUrl(QStringLiteral("https://docs.kdenlive.org/troubleshooting/installation_troubleshooting.html")), QStringLiteral("text/html"), this, KRun::RunFlags());
953 #endif
954 }
955
slotSaveCaptureFormat()956 void Wizard::slotSaveCaptureFormat()
957 {
958 QStringList format = m_capture.v4l_formats->itemData(m_capture.v4l_formats->currentIndex()).toStringList();
959 if (format.isEmpty()) {
960 return;
961 }
962 std::unique_ptr<ProfileParam> profile(new ProfileParam(pCore->getCurrentProfile().get()));
963 profile->m_description = QStringLiteral("Video4Linux capture");
964 profile->m_colorspace = 601;
965 profile->m_width = format.at(1).toInt();
966 profile->m_height = format.at(2).toInt();
967 profile->m_sample_aspect_num = 1;
968 profile->m_sample_aspect_den = 1;
969 profile->m_display_aspect_num = format.at(1).toInt();
970 profile->m_display_aspect_den = format.at(2).toInt();
971 profile->m_frame_rate_num = format.at(3).toInt();
972 profile->m_frame_rate_den = format.at(4).toInt();
973 profile->m_progressive = true;
974 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/profiles/"));
975 if (!dir.exists()) {
976 dir.mkpath(QStringLiteral("."));
977 }
978 ProfileRepository::get()->saveProfile(profile.get(), dir.absoluteFilePath(QStringLiteral("video4linux")));
979 }
980
slotUpdateDecklinkDevice(uint captureCard)981 void Wizard::slotUpdateDecklinkDevice(uint captureCard)
982 {
983 KdenliveSettings::setDecklink_capturedevice(captureCard);
984 }
985
testHwEncoders()986 void Wizard::testHwEncoders()
987 {
988 QProcess hwEncoders;
989 // Testing vaapi support
990 QTemporaryFile tmp(QDir::temp().absoluteFilePath(QStringLiteral("XXXXXX.mp4")));
991 if (!tmp.open()) {
992 // Something went wrong
993 return;
994 }
995 tmp.close();
996
997 // VAAPI testing
998 QStringList args{"-hide_banner", "-y",
999 "-vaapi_device",
1000 "/dev/dri/renderD128",
1001 "-f",
1002 "lavfi",
1003 "-i",
1004 "smptebars=duration=5:size=1280x720:rate=25",
1005 "-vf",
1006 "format=nv12,hwupload",
1007 "-c:v",
1008 "h264_vaapi",
1009 "-an",
1010 "-f",
1011 "mp4",
1012 tmp.fileName()};
1013 qDebug() << "// FFMPEG ARGS: " << args;
1014 hwEncoders.start(KdenliveSettings::ffmpegpath(), args);
1015 bool vaapiSupported = false;
1016 if (hwEncoders.waitForFinished()) {
1017 if (hwEncoders.exitStatus() == QProcess::CrashExit) {
1018 qDebug() << "/// ++ VAAPI NOT SUPPORTED";
1019 } else {
1020 if (tmp.exists() && tmp.size() > 0) {
1021 qDebug() << "/// ++ VAAPI YES SUPPORTED ::::::";
1022 // vaapi support enabled
1023 vaapiSupported = true;
1024 } else {
1025 qDebug() << "/// ++ VAAPI FAILED ::::::";
1026 // vaapi support not enabled
1027 }
1028 }
1029 }
1030 KdenliveSettings::setVaapiEnabled(vaapiSupported);
1031
1032 // VAAPI with scaling support
1033 QStringList scaleargs{"-hide_banner", "-y"
1034 ,"-hwaccel"
1035 ,"vaapi"
1036 ,"-hwaccel_output_format"
1037 ,"vaapi"
1038 ,"/dev/dri/renderD128"
1039 ,"-f"
1040 ,"lavfi"
1041 ,"-i"
1042 ,"smptebars=duration=5:size=1280x720:rate=25"
1043 ,"-vf"
1044 ,"scale_vaapi=w=640:h=-2:format=nv12,hwupload"
1045 ,"-c:v"
1046 ,"h264_vaapi"
1047 ,"-an"
1048 ,"-f"
1049 ,"mp4"
1050 ,tmp.fileName()};
1051 qDebug() << "// FFMPEG ARGS: " << scaleargs;
1052 hwEncoders.start(KdenliveSettings::ffmpegpath(), scaleargs);
1053 bool vaapiScalingSupported = false;
1054 if (hwEncoders.waitForFinished()) {
1055 if (hwEncoders.exitStatus() == QProcess::CrashExit) {
1056 qDebug() << "/// ++ VAAPI NOT SUPPORTED";
1057 } else {
1058 if (tmp.exists() && tmp.size() > 0) {
1059 qDebug() << "/// ++ VAAPI YES SUPPORTED ::::::";
1060 // vaapi support enabled
1061 vaapiScalingSupported = true;
1062 } else {
1063 qDebug() << "/// ++ VAAPI FAILED ::::::";
1064 // vaapi support not enabled
1065 }
1066 }
1067 }
1068 KdenliveSettings::setVaapiScalingEnabled(vaapiScalingSupported);
1069 // NVIDIA testing
1070 QTemporaryFile tmp2(QDir::temp().absoluteFilePath(QStringLiteral("XXXXXX.mp4")));
1071 if (!tmp2.open()) {
1072 // Something went wrong
1073 return;
1074 }
1075 tmp2.close();
1076 QStringList args2{"-hide_banner", "-y", "-hwaccel", "cuvid", "-f", "lavfi", "-i", "smptebars=duration=5:size=1280x720:rate=25",
1077 "-c:v", "h264_nvenc", "-an", "-f", "mp4", tmp2.fileName()};
1078 qDebug() << "// FFMPEG ARGS: " << args2;
1079 hwEncoders.start(KdenliveSettings::ffmpegpath(), args2);
1080 bool nvencSupported = false;
1081 if (hwEncoders.waitForFinished()) {
1082 if (hwEncoders.exitStatus() == QProcess::CrashExit) {
1083 qDebug() << "/// ++ NVENC NOT SUPPORTED";
1084 } else {
1085 if (tmp2.exists() && tmp2.size() > 0) {
1086 qDebug() << "/// ++ NVENC YES SUPPORTED ::::::";
1087 // vaapi support enabled
1088 nvencSupported = true;
1089 } else {
1090 qDebug() << "/// ++ NVENC FAILED ::::::";
1091 // vaapi support not enabled
1092 }
1093 }
1094 }
1095 KdenliveSettings::setNvencEnabled(nvencSupported);
1096
1097 // Testing NVIDIA SCALER
1098 QStringList args3{"-hide_banner", "-filters"};
1099 qDebug() << "// FFMPEG ARGS: " << args3;
1100 hwEncoders.start(KdenliveSettings::ffmpegpath(), args3);
1101 bool nvScalingSupported = false;
1102 if (hwEncoders.waitForFinished()) {
1103 QByteArray output = hwEncoders.readAll();
1104 hwEncoders.close();
1105 if (output.contains(QByteArray("scale_npp"))) {
1106 qDebug() << "/// ++ SCALE_NPP YES SUPPORTED ::::::";
1107 nvScalingSupported = true;
1108 } else {
1109 qDebug() << "/// ++ SCALE_NPP NOT SUPPORTED";
1110 }
1111 }
1112 KdenliveSettings::setNvScalingEnabled(nvScalingSupported);
1113 }
1114
updateHwStatus()1115 void Wizard::updateHwStatus()
1116 {
1117 auto *statusLabel = new KMessageWidget(this);
1118 bool hwEnabled = KdenliveSettings::vaapiEnabled() || KdenliveSettings::nvencEnabled();
1119 statusLabel->setMessageType(hwEnabled ? KMessageWidget::Positive : KMessageWidget::Information);
1120 statusLabel->setWordWrap(true);
1121 QString statusMessage;
1122 if (!hwEnabled) {
1123 statusMessage = i18n("No hardware encoders found.");
1124 } else {
1125 if (KdenliveSettings::nvencEnabled()) {
1126 statusMessage += i18n("NVIDIA hardware encoders found and enabled.");
1127 }
1128 if (KdenliveSettings::vaapiEnabled()) {
1129 statusMessage += i18n("VAAPI hardware encoders found and enabled.");
1130 }
1131 }
1132 statusLabel->setText(statusMessage);
1133 statusLabel->setCloseButtonVisible(false);
1134 // errorLabel->setTimeout();
1135 m_startLayout->addWidget(statusLabel);
1136 statusLabel->animatedShow();
1137 QTimer::singleShot(3000, statusLabel, &KMessageWidget::animatedHide);
1138 }
1139