File indexing completed on 2025-03-09 03:52:06
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-09-14 0007 * Description : a presentation tool. 0008 * 0009 * SPDX-FileCopyrightText: 2008-2009 by Valerio Fuoglio <valerio dot fuoglio at gmail dot com> 0010 * SPDX-FileCopyrightText: 2009 by Andi Clemens <andi dot clemens at googlemail dot com> 0011 * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "presentationaudiowidget.h" 0018 0019 // Qt includes 0020 0021 #include <QTime> 0022 #include <QUrl> 0023 #include <QIcon> 0024 0025 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0026 0027 # include <QAudioOutput> 0028 0029 #endif 0030 0031 // Local includes 0032 0033 #include "presentationcontainer.h" 0034 #include "digikam_debug.h" 0035 0036 using namespace Digikam; 0037 0038 namespace DigikamGenericPresentationPlugin 0039 { 0040 0041 class Q_DECL_HIDDEN PresentationAudioWidget::Private 0042 { 0043 0044 public: 0045 0046 Private() = default; 0047 0048 PresentationContainer* sharedData = nullptr; 0049 QList<QUrl> urlList; 0050 int currIndex = 0; 0051 bool canHide = true; 0052 bool isZeroTime = false; 0053 bool playingNext = false; 0054 0055 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0056 0057 QMediaPlayer* mediaObject = nullptr; 0058 0059 #else 0060 0061 DAudioPlayer* mediaObject = nullptr; 0062 0063 #endif 0064 0065 }; 0066 0067 PresentationAudioWidget::PresentationAudioWidget(QWidget* const parent, const QList<QUrl>& urls, PresentationContainer* const sharedData) 0068 : QWidget(parent), 0069 d (new Private) 0070 { 0071 setupUi(this); 0072 0073 d->sharedData = sharedData; 0074 0075 m_soundLabel->setPixmap(QIcon::fromTheme(QLatin1String("speaker")).pixmap(64, 64)); 0076 0077 m_prevButton->setText(QString()); 0078 m_nextButton->setText(QString()); 0079 m_playButton->setText(QString()); 0080 m_stopButton->setText(QString()); 0081 0082 m_prevButton->setIcon(QIcon::fromTheme(QLatin1String("media-skip-backward"))); 0083 m_nextButton->setIcon(QIcon::fromTheme(QLatin1String("media-skip-forward"))); 0084 m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start"))); 0085 m_stopButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-stop"))); 0086 0087 connect(m_prevButton, SIGNAL(clicked()), 0088 this, SLOT(slotPrev())); 0089 0090 connect(m_nextButton, SIGNAL(clicked()), 0091 this, SLOT(slotNext())); 0092 0093 connect(m_playButton, SIGNAL(clicked()), 0094 this, SLOT(slotPlay())); 0095 0096 connect(m_stopButton, SIGNAL(clicked()), 0097 this, SLOT(slotStop())); 0098 0099 if (urls.isEmpty()) 0100 { 0101 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Tracks list is empty..."; 0102 setEnabled(false); 0103 return; 0104 } 0105 0106 // Waiting for files to be enqueued. 0107 0108 m_playButton->setEnabled(false); 0109 m_prevButton->setEnabled(false); 0110 0111 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0112 0113 d->mediaObject = new QMediaPlayer(this); 0114 d->mediaObject->setAudioOutput(new QAudioOutput); 0115 0116 connect(d->mediaObject, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), 0117 this, SLOT(slotMediaStateChanged(QMediaPlayer::MediaStatus))); 0118 0119 connect(d->mediaObject, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)), 0120 this, SLOT(slotPlayerStateChanged(QMediaPlayer::PlaybackState))); 0121 0122 connect(d->mediaObject, SIGNAL(errorOccurred(QMediaPlayer::Error,QString)), 0123 this, SLOT(slotPlayerError(QMediaPlayer::Error))); 0124 0125 connect(d->mediaObject, SIGNAL(positionChanged(qint64)), 0126 this, SLOT(slotTimeUpdaterTimeout(qint64))); 0127 0128 #else 0129 0130 d->mediaObject = new DAudioPlayer(this); 0131 0132 connect(d->mediaObject->player(), SIGNAL(mediaStatusChanged(QAVPlayer::MediaStatus)), 0133 this, SLOT(slotMediaStateChanged(QAVPlayer::MediaStatus))); 0134 0135 connect(d->mediaObject->player(), SIGNAL(stateChanged(QAVPlayer::State)), 0136 this, SLOT(slotPlayerStateChanged(QAVPlayer::State))); 0137 0138 connect(d->mediaObject->player(), SIGNAL(errorOccurred(QAVPlayer::Error,QString)), 0139 this, SLOT(slotPlayerError(QAVPlayer::Error,QString))); 0140 0141 connect(d->mediaObject, SIGNAL(positionChanged(qint64)), 0142 this, SLOT(slotTimeUpdaterTimeout(qint64))); 0143 0144 #endif 0145 0146 connect(m_volumeWidget, SIGNAL(valueChanged(int)), 0147 this, SLOT(slotSetVolume(int))); 0148 0149 enqueue(urls); 0150 0151 setZeroTime(); 0152 } 0153 0154 PresentationAudioWidget::~PresentationAudioWidget() 0155 { 0156 if (!d->urlList.isEmpty()) 0157 { 0158 d->mediaObject->stop(); 0159 } 0160 0161 delete d; 0162 } 0163 0164 void PresentationAudioWidget::slotSetVolume(int v) 0165 { 0166 if (d->mediaObject->audioOutput()) 0167 { 0168 0169 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0170 0171 d->mediaObject->audioOutput()->setVolume(v / 100.0); 0172 0173 #else 0174 0175 d->mediaObject->setVolume((qreal)v / 100.0); 0176 0177 #endif 0178 0179 } 0180 } 0181 0182 bool PresentationAudioWidget::canHide() const 0183 { 0184 return d->canHide; 0185 } 0186 0187 bool PresentationAudioWidget::isPaused() const 0188 { 0189 0190 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0191 0192 return (d->mediaObject->playbackState() == QMediaPlayer::PausedState); 0193 0194 #else 0195 0196 return (d->mediaObject->state() == QAVPlayer::PausedState); 0197 0198 #endif 0199 0200 } 0201 0202 void PresentationAudioWidget::checkSkip() 0203 { 0204 m_prevButton->setEnabled(true); 0205 m_nextButton->setEnabled(true); 0206 0207 if (!d->sharedData->soundtrackLoop) 0208 { 0209 if (d->currIndex == 0) 0210 { 0211 m_prevButton->setEnabled(false); 0212 } 0213 0214 if (d->currIndex == d->urlList.count() - 1) 0215 { 0216 m_nextButton->setEnabled(false); 0217 } 0218 } 0219 } 0220 0221 void PresentationAudioWidget::setZeroTime() 0222 { 0223 QTime zeroTime(0, 0, 0); 0224 m_elapsedTimeLabel->setText(zeroTime.toString(QLatin1String("H:mm:ss"))); 0225 m_totalTimeLabel->setText(zeroTime.toString(QLatin1String("H:mm:ss"))); 0226 d->isZeroTime = true; 0227 } 0228 0229 void PresentationAudioWidget::enqueue(const QList<QUrl>& urls) 0230 { 0231 d->urlList = urls; 0232 d->currIndex = 0; 0233 0234 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Tracks : " << d->urlList; 0235 0236 if (d->urlList.isEmpty()) 0237 { 0238 return; 0239 } 0240 0241 m_playButton->setEnabled(true); 0242 } 0243 0244 void PresentationAudioWidget::setPaused(bool val) 0245 { 0246 if (val == isPaused()) 0247 { 0248 return; 0249 } 0250 0251 slotPlay(); 0252 } 0253 0254 void PresentationAudioWidget::keyPressEvent(QKeyEvent* event) 0255 { 0256 switch (event->key()) 0257 { 0258 case (Qt::Key_Space): 0259 { 0260 m_playButton->animateClick(); 0261 break; 0262 } 0263 0264 case (Qt::Key_A): 0265 { 0266 if (m_prevButton->isEnabled()) 0267 { 0268 m_prevButton->animateClick(); 0269 } 0270 0271 break; 0272 } 0273 0274 case (Qt::Key_S): 0275 { 0276 if (m_nextButton->isEnabled()) 0277 { 0278 m_nextButton->animateClick(); 0279 } 0280 0281 break; 0282 } 0283 0284 case (Qt::Key_Escape): 0285 { 0286 if (m_stopButton->isEnabled()) 0287 { 0288 m_stopButton->animateClick(); 0289 } 0290 0291 break; 0292 } 0293 0294 default: 0295 { 0296 break; 0297 } 0298 } 0299 0300 event->accept(); 0301 } 0302 0303 void PresentationAudioWidget::slotPlay() 0304 { 0305 if (!d->mediaObject) 0306 { 0307 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Internal Media Object is null!"; 0308 0309 return; 0310 } 0311 0312 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0313 0314 if ((d->mediaObject->playbackState() != QMediaPlayer::PlayingState) || 0315 (d->mediaObject->playbackState() == QMediaPlayer::PausedState)) 0316 0317 #else 0318 0319 if ((d->mediaObject->state() != QAVPlayer::PlayingState) || 0320 (d->mediaObject->state() == QAVPlayer::PausedState)) 0321 0322 #endif 0323 0324 { 0325 0326 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0327 0328 if (d->mediaObject->playbackState() != QMediaPlayer::PlayingState) 0329 0330 #else 0331 0332 if (d->mediaObject->state() != QAVPlayer::PlayingState) 0333 0334 #endif 0335 0336 { 0337 d->mediaObject->setSource(d->urlList[d->currIndex]); 0338 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Playing:" << d->urlList[d->currIndex]; 0339 d->mediaObject->play(); 0340 setZeroTime(); 0341 } 0342 else 0343 { 0344 d->mediaObject->pause(); 0345 } 0346 0347 d->canHide = true; 0348 0349 Q_EMIT signalPlay(); 0350 } 0351 else 0352 { 0353 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Pausing:" << d->urlList[d->currIndex]; 0354 0355 d->mediaObject->pause(); 0356 d->canHide = false; 0357 0358 Q_EMIT signalPause(); 0359 } 0360 } 0361 0362 void PresentationAudioWidget::slotStop() 0363 { 0364 if (!d->mediaObject) 0365 { 0366 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Internal Media Object is null!"; 0367 0368 return; 0369 } 0370 0371 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "Stoping:" << d->urlList[d->currIndex]; 0372 d->playingNext = false; 0373 d->mediaObject->stop(); 0374 d->currIndex = 0; 0375 setZeroTime(); 0376 checkSkip(); 0377 } 0378 0379 void PresentationAudioWidget::slotPrev() 0380 { 0381 d->currIndex--; 0382 0383 if (d->currIndex < 0) 0384 { 0385 if (d->sharedData->soundtrackLoop) 0386 { 0387 d->currIndex = d->urlList.count() - 1; 0388 } 0389 else 0390 { 0391 d->currIndex = 0; 0392 return; 0393 } 0394 } 0395 0396 d->playingNext = false; 0397 d->mediaObject->stop(); 0398 slotPlay(); 0399 } 0400 0401 void PresentationAudioWidget::slotNext() 0402 { 0403 d->currIndex++; 0404 0405 if (d->currIndex >= d->urlList.count()) 0406 { 0407 if (d->sharedData->soundtrackLoop) 0408 { 0409 d->currIndex = 0; 0410 } 0411 else 0412 { 0413 d->currIndex = d->urlList.count() - 1; 0414 return; 0415 } 0416 } 0417 0418 d->playingNext = false; 0419 d->mediaObject->stop(); 0420 slotPlay(); 0421 } 0422 0423 void PresentationAudioWidget::slotTimeUpdaterTimeout(qint64 current) 0424 { 0425 0426 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0427 0428 if (d->mediaObject->error() != QMediaPlayer::NoError) 0429 0430 #else 0431 0432 if (d->mediaObject->mediaStatus() == QAVPlayer::InvalidMedia) 0433 0434 #endif 0435 0436 { 0437 slotError(); 0438 return; 0439 } 0440 0441 int hours = (int)(current / (qint64)(60 * 60 * 1000)); 0442 int mins = (int)((current / (qint64)(60 * 1000)) - (qint64)(hours * 60)); 0443 int secs = (int)((current / (qint64)1000) - (qint64)(hours * 60 + mins * 60)); 0444 QTime elapsedTime(hours, mins, secs); 0445 0446 if (d->isZeroTime && (d->mediaObject->duration() > 0)) 0447 { 0448 d->isZeroTime = false; 0449 qint64 total = d->mediaObject->duration(); 0450 hours = (int)(total / (qint64)(60 * 60 * 1000)); 0451 mins = (int)((total / (qint64)(60 * 1000)) - (qint64)(hours * 60)); 0452 secs = (int)((total / (qint64)1000) - (qint64)(hours * 60 + mins * 60)); 0453 QTime totalTime(hours, mins, secs); 0454 m_totalTimeLabel->setText(totalTime.toString(QLatin1String("H:mm:ss"))); 0455 } 0456 0457 m_elapsedTimeLabel->setText(elapsedTime.toString(QLatin1String("H:mm:ss"))); 0458 } 0459 0460 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && defined HAVE_QTMULTIMEDIA 0461 0462 void PresentationAudioWidget::slotMediaStateChanged(QMediaPlayer::MediaStatus status) 0463 { 0464 if (status == QMediaPlayer::EndOfMedia) 0465 { 0466 slotNext(); 0467 } 0468 } 0469 0470 void PresentationAudioWidget::slotPlayerError(QMediaPlayer::Error err) 0471 { 0472 if (err != QMediaPlayer::NoError) 0473 { 0474 qCDebug(DIGIKAM_GENERAL_LOG) << "An error as occured while playing (" << err << ")"; 0475 slotError(); 0476 } 0477 } 0478 0479 void PresentationAudioWidget::slotPlayerStateChanged(QMediaPlayer::PlaybackState state) 0480 { 0481 switch (state) 0482 { 0483 case QMediaPlayer::PausedState: 0484 case QMediaPlayer::StoppedState: 0485 { 0486 m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start"))); 0487 checkSkip(); 0488 break; 0489 } 0490 0491 case QMediaPlayer::PlayingState: 0492 { 0493 m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); 0494 d->playingNext = true; 0495 checkSkip(); 0496 break; 0497 } 0498 0499 default: 0500 { 0501 break; 0502 } 0503 } 0504 } 0505 0506 #else 0507 0508 void PresentationAudioWidget::slotMediaStateChanged(QAVPlayer::MediaStatus status) 0509 { 0510 if (status == QAVPlayer::EndOfMedia) 0511 { 0512 slotNext(); 0513 } 0514 } 0515 0516 void PresentationAudioWidget::slotPlayerError(QAVPlayer::Error err, const QString& message) 0517 { 0518 if (err != QAVPlayer::NoError) 0519 { 0520 qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << "An error as occurred while playing (" << message << ")"; 0521 slotError(); 0522 } 0523 } 0524 0525 void PresentationAudioWidget::slotPlayerStateChanged(QAVPlayer::State state) 0526 { 0527 switch (state) 0528 { 0529 case QAVPlayer::PausedState: 0530 case QAVPlayer::StoppedState: 0531 { 0532 m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-start"))); 0533 checkSkip(); 0534 break; 0535 } 0536 0537 case QAVPlayer::PlayingState: 0538 { 0539 m_playButton->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); 0540 d->playingNext = true; 0541 checkSkip(); 0542 break; 0543 } 0544 0545 default: 0546 { 0547 break; 0548 } 0549 } 0550 } 0551 0552 #endif 0553 0554 void PresentationAudioWidget::slotError() 0555 { 0556 qCWarning(DIGIKAM_DPLUGIN_GENERIC_LOG) << "An error as occurred!"; 0557 0558 /* TODO: 0559 * Display error on slideshow. 0560 * A QWidget pop-up could help 0561 */ 0562 0563 slotNext(); 0564 } 0565 0566 } // namespace DigikamGenericPresentationPlugin 0567 0568 #include "moc_presentationaudiowidget.cpp"