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