File indexing completed on 2024-12-22 04:40:20

0001 /*
0002     SPDX-FileCopyrightText: 2007-2009 Sergio Pistone <sergio_pistone@yahoo.com.ar>
0003     SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "videoplayer.h"
0009 #include "scconfig.h"
0010 
0011 #include "videoplayer/backend/glrenderer.h"
0012 
0013 #include <math.h>
0014 
0015 #include <QTimer>
0016 #include <QFileInfo>
0017 #include <QApplication>
0018 #include <QEvent>
0019 
0020 #include <QDebug>
0021 
0022 #include <KLocalizedString>
0023 
0024 #define VOLUME_MULTIPLIER (double(qMax(1, SCConfig::volumeAmplification())) / 3.)
0025 
0026 using namespace SubtitleComposer;
0027 
0028 
0029 VideoPlayer::VideoPlayer()
0030     : m_player(new FFPlayer(this)),
0031       m_videoWidget(nullptr),
0032       m_filePath(),
0033       m_state(Initialized),
0034       m_duration(-1.0),
0035       m_fps(-1.0),
0036       m_playSpeed(.0),
0037       m_textStreams(),
0038       m_activeAudioStream(-2),
0039       m_audioStreams(),
0040       m_muted(false),
0041       m_volume(100.0)
0042 {
0043     m_player->renderer()->setOverlay(&m_subOverlay);
0044 
0045     setupNotifications();
0046 }
0047 
0048 VideoPlayer::~VideoPlayer()
0049 {
0050 }
0051 
0052 VideoPlayer *
0053 VideoPlayer::instance()
0054 {
0055     static VideoPlayer *player = nullptr;
0056     if(!player)
0057         player = new VideoPlayer();
0058     return player;
0059 }
0060 
0061 bool
0062 VideoPlayer::init(QWidget *videoContainer)
0063 {
0064     if(!m_videoWidget) {
0065         m_videoWidget = new VideoWidget(videoContainer);
0066         m_videoWidget->setVideoLayer(m_player->renderer());
0067 
0068         connect(m_videoWidget, &VideoWidget::doubleClicked, this, &VideoPlayer::doubleClicked);
0069         connect(m_videoWidget, &VideoWidget::rightClicked, this, &VideoPlayer::rightClicked);
0070         connect(m_videoWidget, &VideoWidget::leftClicked, this, &VideoPlayer::leftClicked);
0071         connect(m_videoWidget, &VideoWidget::wheelUp, this, &VideoPlayer::wheelUp);
0072         connect(m_videoWidget, &VideoWidget::wheelDown, this, &VideoPlayer::wheelDown);
0073     } else {
0074         m_videoWidget->setParent(videoContainer);
0075     }
0076     reset();
0077 
0078     return true;
0079 }
0080 
0081 void
0082 VideoPlayer::reset()
0083 {
0084     m_filePath.clear();
0085 
0086     m_duration = -1.0;
0087     m_fps = -1.0;
0088 
0089     m_activeAudioStream = -2;
0090     m_textStreams.clear();
0091     m_audioStreams.clear();
0092 
0093     m_state = Initialized;
0094 
0095     if(m_videoWidget)
0096         m_videoWidget->videoLayer()->hide();
0097 }
0098 
0099 bool
0100 VideoPlayer::playOnLoad()
0101 {
0102     const QWidget *topLevel = m_videoWidget->topLevelWidget();
0103     const QWidget *dockWaveform = topLevel->findChild<QWidget *>(QStringLiteral("waveform_dock"));
0104     const QWidget *dockVideo = topLevel->findChild<QWidget *>(QStringLiteral("player_dock"));
0105     return SCConfig::videoAutoPlay() && (dockVideo->isVisible() || dockWaveform->isVisible());
0106 }
0107 
0108 bool
0109 VideoPlayer::openFile(const QString &filePath)
0110 {
0111     Q_ASSERT(m_state == Initialized || m_state == Stopped);
0112 
0113     QFileInfo fileInfo(filePath);
0114     if(!fileInfo.exists() || !fileInfo.isFile() || !fileInfo.isReadable()) {
0115         emit fileOpenError(filePath, i18n("File does not exist."));   // operation will never succeed
0116         return false;
0117     }
0118 
0119     m_filePath = filePath;
0120     m_state = Opening;
0121 
0122     if(!m_player->open(fileInfo.absoluteFilePath().toUtf8()))
0123         return false;
0124 
0125     if(m_player->paused() == playOnLoad())
0126         m_player->pauseToggle();
0127 
0128     return true;
0129 }
0130 
0131 void
0132 VideoPlayer::closeFile()
0133 {
0134     if(m_state < Opening)
0135         return;
0136     m_player->close();
0137     reset();
0138     emit fileClosed();
0139 }
0140 
0141 void
0142 VideoPlayer::play()
0143 {
0144     if(m_state <= Opening || m_state == Playing)
0145         return;
0146     if(m_state < Playing)
0147         openFile(m_filePath);
0148     if(m_state == Paused)
0149         m_player->pauseToggle();
0150 }
0151 
0152 void
0153 VideoPlayer::pause()
0154 {
0155     if(m_state <= Opening || m_state == Paused)
0156         return;
0157     m_player->pauseToggle();
0158 }
0159 
0160 void
0161 VideoPlayer::togglePlayPaused()
0162 {
0163     if(m_state == Playing)
0164         pause();
0165     else if(m_state > Opening)
0166         play();
0167 }
0168 
0169 bool
0170 VideoPlayer::seek(double seconds)
0171 {
0172     if(m_state <= Stopped)
0173         return false;
0174     m_player->seek(qBound(0., seconds, m_duration));
0175     return true;
0176 }
0177 
0178 bool
0179 VideoPlayer::step(int frameOffset)
0180 {
0181     if(m_state <= Stopped)
0182         return false;
0183     m_player->stepFrame(frameOffset);
0184     return true;
0185 }
0186 
0187 bool
0188 VideoPlayer::stop()
0189 {
0190     if(m_state <= Stopped)
0191         return false;
0192     m_player->close();
0193     return true;
0194 }
0195 
0196 bool
0197 VideoPlayer::selectAudioStream(int streamIndex)
0198 {
0199     if(m_state <= VideoPlayer::Opening || streamIndex < 0 || streamIndex >= m_audioStreams.size())
0200         return false;
0201 
0202     if(m_activeAudioStream != streamIndex) {
0203         m_activeAudioStream = streamIndex;
0204         m_player->activeAudioStream(streamIndex);
0205         emit activeAudioStreamChanged(streamIndex);
0206     }
0207     return true;
0208 }
0209 
0210 void
0211 VideoPlayer::playSpeed(double newRate)
0212 {
0213     if(m_state <= Opening || newRate < .05 || newRate > 8.)
0214         return;
0215 
0216     m_player->setSpeed(newRate);
0217 }
0218 
0219 void
0220 VideoPlayer::increaseVolume(double amount)
0221 {
0222     setVolume(m_volume + amount);
0223     setMuted(false);
0224 }
0225 
0226 void
0227 VideoPlayer::decreaseVolume(double amount)
0228 {
0229     setVolume(m_volume - amount);
0230 }
0231 
0232 void
0233 VideoPlayer::setVolume(double volume)
0234 {
0235     volume = qBound(0., volume, 100.);
0236     m_player->setVolume(VOLUME_MULTIPLIER * volume / 100.);
0237 
0238     if(m_volume != volume)
0239         emit volumeChanged(m_volume = volume);
0240 }
0241 
0242 void
0243 VideoPlayer::setMuted(bool muted)
0244 {
0245     m_player->setMuted(muted);
0246 
0247     if(m_muted == muted)
0248         return;
0249 
0250     m_muted = muted;
0251 
0252     emit muteChanged(m_muted);
0253 }
0254 
0255 void
0256 VideoPlayer::setupNotifications()
0257 {
0258     connect(m_player, &FFPlayer::mediaLoaded, this, [this](){
0259         m_player->renderer()->reset();
0260         emit fileOpened(m_filePath);
0261         m_fps = m_player->videoFPS();
0262         emit fpsChanged(m_fps);
0263         m_videoWidget->videoLayer()->show();
0264     });
0265     connect(m_player, &FFPlayer::stateChanged, this, [this](FFPlayer::State ffs){
0266         if(m_state == Initialized) // video file is closed don't notify play/pause/stop
0267             return;
0268         static const QMap<FFPlayer::State, State> stateMap
0269             {{ FFPlayer::Stopped, Stopped }, { FFPlayer::Playing, Playing }, { FFPlayer::Paused, Paused }};
0270         const State state = stateMap[ffs];
0271         if(m_state != state) switch(m_state = state) {
0272         case Stopped: emit stopped(); break;
0273         case Playing: emit playing(); break;
0274         case Paused: emit paused(); break;
0275         default: break; // not possible
0276         }
0277     });
0278     connect(m_player->renderer(), &GLRenderer::resolutionChanged, this, [this](){
0279         m_videoWidget->setVideoResolution(m_player->videoWidth(), m_player->videoHeight(), m_player->videoSAR());
0280         m_videoWidget->videoLayer()->show();
0281     });
0282 
0283     connect(m_player, &FFPlayer::positionChanged, this, &VideoPlayer::positionChanged);
0284 
0285     connect(m_player, &FFPlayer::durationChanged, this, [this](double dur){
0286         if(m_duration != dur) emit durationChanged(m_duration = dur);
0287     });
0288     connect(m_player, &FFPlayer::speedChanged, this, [this](double speed){
0289         if(m_playSpeed != speed) emit playSpeedChanged(m_playSpeed = speed);
0290     });
0291 
0292     connect(m_player, &FFPlayer::volumeChanged, this, [this](double volume){
0293         if(m_volume != (volume = volume * 100. / VOLUME_MULTIPLIER)) {
0294             m_volume = volume;
0295             if(!m_muted) emit volumeChanged(m_volume);
0296         }
0297     });
0298     connect(m_player, &FFPlayer::muteChanged, this, [this](bool muted){
0299         if(m_muted != muted) emit muteChanged(m_muted = muted);
0300     });
0301 
0302     //connect(m_player, &FFPlayer::videoStreamsChanged, this, [this](const QStringList &streams){});
0303     connect(m_player, &FFPlayer::audioStreamsChanged, this, [this](const QStringList &streams){
0304         if(m_activeAudioStream >= 0)
0305             m_player->activeAudioStream(m_activeAudioStream);
0306         emit audioStreamsChanged(m_audioStreams = streams);
0307         emit activeAudioStreamChanged(m_activeAudioStream = m_player->activeAudioStream());
0308     });
0309     connect(m_player, &FFPlayer::subtitleStreamsChanged, this, [this](const QStringList &streams){
0310         emit textStreamsChanged(m_textStreams = streams);
0311     });
0312 }