File indexing completed on 2025-03-09 03:52:04

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2017-05-25
0007  * Description : a tool to generate video slideshow from images.
0008  *
0009  * SPDX-FileCopyrightText: 2017-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "vidslidevideopage.h"
0016 
0017 // Qt includes
0018 
0019 #include <QIcon>
0020 #include <QLabel>
0021 #include <QSpinBox>
0022 #include <QUrl>
0023 #include <QWidget>
0024 #include <QApplication>
0025 #include <QStyle>
0026 #include <QComboBox>
0027 #include <QGridLayout>
0028 #include <QGroupBox>
0029 
0030 // KDE includes
0031 
0032 #include <klocalizedstring.h>
0033 
0034 // Local includes
0035 
0036 #include "vidslidewizard.h"
0037 #include "transitionpreview.h"
0038 #include "effectpreview.h"
0039 #include "digikam_debug.h"
0040 
0041 namespace DigikamGenericVideoSlideShowPlugin
0042 {
0043 
0044 class Q_DECL_HIDDEN VidSlideVideoPage::Private
0045 {
0046 public:
0047 
0048     explicit Private(QWizard* const dialog)
0049     {
0050         wizard = dynamic_cast<VidSlideWizard*>(dialog);
0051 
0052         if (wizard)
0053         {
0054             settings = wizard->settings();
0055         }
0056     }
0057 
0058     QSpinBox*          framesVal    = nullptr;
0059     QComboBox*         typeVal      = nullptr;
0060     QComboBox*         bitrateVal   = nullptr;
0061     QComboBox*         stdVal       = nullptr;
0062     QComboBox*         codecVal     = nullptr;
0063     QComboBox*         transVal     = nullptr;
0064     QComboBox*         effVal       = nullptr;
0065     QLabel*            duration     = nullptr;
0066     VidSlideWizard*    wizard       = nullptr;
0067     VidSlideSettings*  settings     = nullptr;
0068     TransitionPreview* transPreview = nullptr;
0069     EffectPreview*     effPreview   = nullptr;
0070 };
0071 
0072 VidSlideVideoPage::VidSlideVideoPage(QWizard* const dialog, const QString& title)
0073     : DWizardPage(dialog, title),
0074       d          (new Private(dialog))
0075 {
0076     setObjectName(QLatin1String("VideoPage"));
0077 
0078     QWidget* const main       = new QWidget(this);
0079 
0080     // --------------------
0081 
0082     QLabel* const framesLabel = new QLabel(main);
0083     framesLabel->setWordWrap(false);
0084     framesLabel->setText(i18n("Number of Frames by Image:"));
0085     d->framesVal              = new QSpinBox(main);
0086     d->framesVal->setRange(1, 15000);
0087     d->framesVal->setToolTip(i18n("This value repeat one image in the sequence.\nWhile creating timelapse; it's usual to use 1.\n"
0088                                   "Typically, the number of frames will determine\nthe time to show the same frame in the video.\n"
0089                                   "The result will depend of the number of frames\nper second used to encode the media."));
0090     framesLabel->setBuddy(d->framesVal);
0091 
0092     // --------------------
0093 
0094     QLabel* const stdLabel = new QLabel(main);
0095     stdLabel->setWordWrap(false);
0096     stdLabel->setText(i18n("Video Standard:"));
0097     d->stdVal              = new QComboBox(main);
0098     d->stdVal->setEditable(false);
0099 
0100     QMap<VidSlideSettings::VidStd, QString> map3                = VidSlideSettings::videoStdNames();
0101     QMap<VidSlideSettings::VidStd, QString>::const_iterator it3 = map3.constBegin();
0102 
0103     while (it3 != map3.constEnd())
0104     {
0105         d->stdVal->addItem(it3.value(), (int)it3.key());
0106         ++it3;
0107     }
0108 
0109     stdLabel->setBuddy(d->stdVal);
0110 
0111     // --------------------
0112 
0113     QLabel* const typeLabel = new QLabel(main);
0114     typeLabel->setWordWrap(false);
0115     typeLabel->setText(i18n("Video Type:"));
0116     d->typeVal              = new QComboBox(main);
0117     d->typeVal->setEditable(false);
0118 
0119     QMap<VidSlideSettings::VidType, QString> map                = VidSlideSettings::videoTypeNames();
0120     QMap<VidSlideSettings::VidType, QString>::const_iterator it = map.constBegin();
0121 
0122     while (it != map.constEnd())
0123     {
0124         if (VidSlideSettings::isVideoTVFormat(it.key()))
0125         {
0126             d->typeVal->addItem(it.value(), (int)it.key());
0127         }
0128 
0129         ++it;
0130     }
0131 
0132     typeLabel->setBuddy(d->typeVal);
0133 
0134     // --------------------
0135 
0136     QLabel* const bitrateLabel = new QLabel(main);
0137     bitrateLabel->setWordWrap(false);
0138     bitrateLabel->setText(i18n("Video Bit Rate:"));
0139     d->bitrateVal              = new QComboBox(main);
0140     d->bitrateVal->setEditable(false);
0141 
0142     QMap<VidSlideSettings::VidBitRate, QString> map2                = VidSlideSettings::videoBitRateNames();
0143     QMap<VidSlideSettings::VidBitRate, QString>::const_iterator it2 = map2.constBegin();
0144 
0145     while (it2 != map2.constEnd())
0146     {
0147         d->bitrateVal->addItem(it2.value(), (int)it2.key());
0148         ++it2;
0149     }
0150 
0151     bitrateLabel->setBuddy(d->bitrateVal);
0152 
0153     // --------------------
0154 
0155     QLabel* const codecLabel = new QLabel(main);
0156     codecLabel->setWordWrap(false);
0157     codecLabel->setText(i18n("Video Codec:"));
0158     d->codecVal              = new QComboBox(main);
0159     d->codecVal->setEditable(false);
0160     codecLabel->setBuddy(d->codecVal);
0161 
0162     // --------------------
0163 
0164     QGroupBox* const effGrp = new QGroupBox(i18n("Effect While Displaying Images"), main);
0165     QLabel* const effLabel  = new QLabel(effGrp);
0166     effLabel->setWordWrap(false);
0167     effLabel->setText(i18n("Type:"));
0168     d->effVal               = new QComboBox(effGrp);
0169     d->effVal->setEditable(false);
0170 
0171     QMap<EffectMngr::EffectType, QString> map6                = EffectMngr::effectNames();
0172     QMap<EffectMngr::EffectType, QString>::const_iterator it6 = map6.constBegin();
0173 
0174     while (it6 != map6.constEnd())
0175     {
0176         d->effVal->insertItem((int)it6.key(), it6.value(), (int)it6.key());
0177         ++it6;
0178     }
0179 
0180     effLabel->setBuddy(d->effVal);
0181 
0182     QLabel* const effNote  = new QLabel(effGrp);
0183     effNote->setWordWrap(true);
0184     effNote->setText(i18n("<i>An effect is an visual panning or zooming applied while an image "
0185                           "is displayed. The effect duration will follow the number of frames used "
0186                           "to render the image on video stream. For a timelapse, let this setting to None.</i>"));
0187 
0188     d->effPreview              = new EffectPreview(effGrp);
0189     QGridLayout* const effGrid = new QGridLayout(effGrp);
0190     effGrid->setSpacing(qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing),
0191                              QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)));
0192     effGrid->addWidget(effLabel,      0, 0, 1, 1);
0193     effGrid->addWidget(d->effVal,     0, 1, 1, 1);
0194     effGrid->addWidget(effNote,       1, 0, 1, 2);
0195     effGrid->addWidget(d->effPreview, 0, 2, 2, 1);
0196     effGrid->setColumnStretch(1, 10);
0197     effGrid->setRowStretch(1, 10);
0198 
0199     // --------------------
0200 
0201     QGroupBox* const transGrp = new QGroupBox(i18n("Transition Between Images"), main);
0202     QLabel* const transLabel  = new QLabel(transGrp);
0203     transLabel->setWordWrap(false);
0204     transLabel->setText(i18n("Type:"));
0205     d->transVal               = new QComboBox(transGrp);
0206     d->transVal->setEditable(false);
0207 
0208     QMap<TransitionMngr::TransType, QString> map4                = TransitionMngr::transitionNames();
0209     QMap<TransitionMngr::TransType, QString>::const_iterator it4 = map4.constBegin();
0210 
0211     while (it4 != map4.constEnd())
0212     {
0213         d->transVal->addItem(it4.value(), (int)it4.key());
0214         ++it4;
0215     }
0216 
0217     transLabel->setBuddy(d->transVal);
0218 
0219     QLabel* const transNote  = new QLabel(transGrp);
0220     transNote->setWordWrap(true);
0221     transNote->setText(i18n("<i>A transition is an visual effect applied between two images. "
0222                             "For some effects, the duration can depend of random values and "
0223                             "can change while the slideshow. For a timelapse, let this setting to None.</i>"));
0224 
0225     d->transPreview              = new TransitionPreview(transGrp);
0226     QGridLayout* const transGrid = new QGridLayout(transGrp);
0227     transGrid->setSpacing(qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing),
0228                              QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)));
0229     transGrid->addWidget(transLabel,      0, 0, 1, 1);
0230     transGrid->addWidget(d->transVal,     0, 1, 1, 1);
0231     transGrid->addWidget(transNote,       1, 0, 1, 2);
0232     transGrid->addWidget(d->transPreview, 0, 2, 2, 1);
0233     transGrid->setColumnStretch(1, 10);
0234     transGrid->setRowStretch(1, 10);
0235 
0236     // --------------------
0237 
0238     d->duration     = new QLabel(main);
0239 
0240     // --------------------
0241 
0242     QGridLayout* const grid = new QGridLayout(main);
0243     grid->setSpacing(qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing),
0244                              QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)));
0245     grid->addWidget(framesLabel,     0, 0, 1, 1);
0246     grid->addWidget(d->framesVal,    0, 1, 1, 1);
0247     grid->addWidget(stdLabel,        1, 0, 1, 1);
0248     grid->addWidget(d->stdVal,       1, 1, 1, 1);
0249     grid->addWidget(typeLabel,       2, 0, 1, 1);
0250     grid->addWidget(d->typeVal,      2, 1, 1, 1);
0251     grid->addWidget(bitrateLabel,    3, 0, 1, 1);
0252     grid->addWidget(d->bitrateVal,   3, 1, 1, 1);
0253     grid->addWidget(codecLabel,      4, 0, 1, 1);
0254     grid->addWidget(d->codecVal,     4, 1, 1, 1);
0255     grid->addWidget(effGrp,          5, 0, 1, 2);
0256     grid->addWidget(transGrp,        6, 0, 1, 2);
0257     grid->addWidget(d->duration,     7, 0, 1, 2);
0258     grid->setRowStretch(8, 10);
0259 
0260     setPageWidget(main);
0261     setLeftBottomPix(QIcon::fromTheme(QLatin1String("video-mp4")));
0262 
0263     // --------------------
0264 
0265     connect(d->framesVal, SIGNAL(valueChanged(int)),
0266             this, SLOT(slotSlideDuration()));
0267 
0268     connect(d->stdVal, SIGNAL(currentIndexChanged(int)),
0269             this, SLOT(slotSlideDuration()));
0270 
0271     connect(d->transVal, SIGNAL(currentIndexChanged(int)),
0272             this, SLOT(slotTransitionChanged()));
0273 
0274     connect(d->effVal, SIGNAL(currentIndexChanged(int)),
0275             this, SLOT(slotEffectChanged()));
0276 }
0277 
0278 VidSlideVideoPage::~VidSlideVideoPage()
0279 {
0280     delete d;
0281 }
0282 
0283 void VidSlideVideoPage::slotTransitionChanged()
0284 {
0285     d->transPreview->stopPreview();
0286     d->transPreview->startPreview((TransitionMngr::TransType)d->transVal->currentIndex());
0287 }
0288 
0289 void VidSlideVideoPage::slotEffectChanged()
0290 {
0291     d->effPreview->stopPreview();
0292     d->effPreview->startPreview((EffectMngr::EffectType)d->effVal->currentIndex());
0293 }
0294 
0295 void VidSlideVideoPage::slotSlideDuration()
0296 {
0297     VidSlideSettings tmp;
0298     tmp.imgFrames = d->framesVal->value();
0299     tmp.vStandard = (VidSlideSettings::VidStd)d->stdVal->currentIndex();
0300     qreal titem   = tmp.imgFrames / tmp.videoFrameRate();
0301     qreal ttotal  = titem * d->settings->inputImages.count();
0302     d->duration->setText(i18n("Duration : %1 seconds by item, total %2 seconds (without transitions)",
0303                               titem, ttotal));
0304 }
0305 
0306 void VidSlideVideoPage::initializePage()
0307 {
0308     // Populate Codecs List
0309 
0310     QMap<VidSlideSettings::VidCodec, QString> map5                = VidSlideSettings::videoCodecNames();
0311     QMap<VidSlideSettings::VidCodec, QString>::const_iterator it5 = map5.constBegin();
0312 
0313     QStringList codecs = d->settings->ffmpegCodecs.keys();
0314     int currentCodec   = d->settings->vCodec;
0315 
0316     while (it5 != map5.constEnd())
0317     {
0318         d->codecVal->insertItem((int)it5.key(), it5.value(), (int)it5.key());
0319 
0320         // Disable entry if FFmpeg codec is not available.
0321 
0322         VidSlideSettings tmp;
0323         tmp.vCodec = (VidSlideSettings::VidCodec)it5.key();
0324 
0325         if (!codecs.contains(tmp.videoCodec()))
0326         {
0327             d->codecVal->setItemData((int)it5.key(), false, Qt::UserRole - 1);
0328         }
0329         else
0330         {
0331             if ((int)it5.key() == currentCodec)
0332             {
0333                 d->codecVal->setCurrentIndex(currentCodec);
0334             }
0335         }
0336 
0337         ++it5;
0338     }
0339 
0340     d->framesVal->setValue(d->settings->imgFrames);
0341     d->typeVal->setCurrentIndex(d->typeVal->findData(d->settings->vType));
0342     d->bitrateVal->setCurrentIndex(d->settings->vbitRate);
0343     d->stdVal->setCurrentIndex(d->settings->vStandard);
0344     d->effVal->setCurrentIndex(d->settings->vEffect);
0345     d->transVal->setCurrentIndex(d->settings->transition);
0346     d->transPreview->setImagesList(d->settings->inputImages);
0347     d->effPreview->setImagesList(d->settings->inputImages);
0348     slotSlideDuration();
0349 }
0350 
0351 bool VidSlideVideoPage::validatePage()
0352 {
0353     d->transPreview->stopPreview();
0354     d->effPreview->stopPreview();
0355     d->settings->imgFrames  = d->framesVal->value();
0356     d->settings->vType      = (VidSlideSettings::VidType)d->typeVal->currentData().toInt();
0357     d->settings->vbitRate   = (VidSlideSettings::VidBitRate)d->bitrateVal->currentIndex();
0358     d->settings->vStandard  = (VidSlideSettings::VidStd)d->stdVal->currentIndex();
0359     d->settings->vCodec     = (VidSlideSettings::VidCodec)d->codecVal->currentData().toInt();
0360     d->settings->vEffect    = (EffectMngr::EffectType)d->effVal->currentIndex();
0361     d->settings->transition = (TransitionMngr::TransType)d->transVal->currentIndex();
0362 
0363     return true;
0364 }
0365 
0366 } // namespace DigikamGenericVideoSlideShowPlugin
0367 
0368 #include "moc_vidslidevideopage.cpp"