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"