File indexing completed on 2024-04-28 16:01:51

0001 /*
0002     Copyright (C) 2012 Trever Fischer <tdfischer@fedoraproject.org>
0003     Copyright (C) 2012 Harald Sitter <sitter@kde.org>
0004 
0005     This library is free software; you can redistribute it and/or
0006     modify it under the terms of the GNU Lesser General Public
0007     License as published by the Free Software Foundation; either
0008     version 2.1 of the License, or (at your option) version 3, or any
0009     later version accepted by the membership of KDE e.V. (or its
0010     successor approved by the membership of KDE e.V.), Nokia Corporation
0011     (or its successors, if any) and the KDE Free Qt Foundation, which shall
0012     act as a proxy defined in Section 6 of version 3 of the license.
0013 
0014     This library is distributed in the hope that it will be useful,
0015     but WITHOUT ANY WARRANTY; without even the implied warranty of
0016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017     Lesser General Public License for more details.
0018 
0019     You should have received a copy of the GNU Lesser General Public
0020     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 
0023 #include "statesvalidator_p.h"
0024 
0025 #include "mediaobject.h"
0026 #include "phononnamespace_p.h"
0027 
0028 #define P_INVALID_STATE(msg) Q_ASSERT_X(0, __FILE__, msg)
0029 
0030 namespace Phonon
0031 {
0032 
0033 StatesValidator::StatesValidator(MediaObject *parent)
0034     : QObject(parent)
0035     , m_mediaObject(parent)
0036     , m_prevState(Phonon::ErrorState)
0037     , m_sourceQueued(false)
0038     , m_aboutToFinishEmitted(false)
0039     , m_aboutToFinishBeforeSeek(false)
0040     , m_aboutToFinishPos(-1)
0041 {
0042     connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
0043             this, SLOT(validateStateChange(Phonon::State,Phonon::State)));
0044     connect(m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
0045             this, SLOT(validateSourceChange()));
0046     connect(m_mediaObject, SIGNAL(tick(qint64)), this, SLOT(validateTick(qint64)));
0047     connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(validateAboutToFinish()));
0048     connect(m_mediaObject, SIGNAL(finished()), this, SLOT(validateFinished()));
0049     connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(validateBufferStatus()));
0050 }
0051 
0052 StatesValidator::~StatesValidator()
0053 {
0054 }
0055 
0056 /**
0057  * The aboutToFinish signal is emitted when the queue is coming to an end.
0058  * This in particular means that it must not be emitted twice, unless no track
0059  * is in the queue and the user seeked back in time before finished
0060  * Since we track the frontend signal here, we only get this signal when the
0061  * queue is in fact empty in the frontend.
0062  * It can however happen that the frontend already delivered the last queue item,
0063  * then the user seeks and the backend forgets to use the already delivered item,
0064  * emitting a bogus aboutToFinish.
0065  */
0066 void StatesValidator::validateAboutToFinish()
0067 {
0068     if (m_aboutToFinishEmitted)
0069         P_INVALID_STATE("aboutToFinish emitted more than once!");
0070     m_aboutToFinishEmitted = true;
0071     m_aboutToFinishPos = m_pos;
0072 }
0073 
0074 void StatesValidator::validateFinished()
0075 {
0076     if (m_mediaObject->state() != Phonon::PlayingState)
0077         P_INVALID_STATE("Playback finished when we weren't playing!");
0078 }
0079 
0080 void StatesValidator::validateSourceChange()
0081 {
0082     if (m_mediaObject->state() != Phonon::StoppedState
0083             && m_mediaObject->state() != Phonon::PlayingState
0084             && m_mediaObject->state() != Phonon::PausedState
0085             && m_mediaObject->state() != Phonon::BufferingState) {
0086         P_INVALID_STATE("Source got changed outside a valid state");
0087     }
0088     m_sourceQueued = false; // Once we get the signal the backend internal one-source queue is definitely cleared.
0089     m_aboutToFinishEmitted = false;
0090     m_aboutToFinishBeforeSeek = false;
0091 }
0092 
0093 void StatesValidator::validateBufferStatus()
0094 {
0095     if (m_mediaObject->state() != Phonon::PlayingState
0096             && m_mediaObject->state() != Phonon::PausedState
0097             && m_mediaObject->state() != Phonon::BufferingState) {
0098         P_INVALID_STATE("Buffer status changed when we weren't supposed to be buffering");
0099     }
0100 }
0101 
0102 void StatesValidator::validateTick(qint64 pos)
0103 {
0104     // Mind that Buffering is a concurrent state, you may have been playing and
0105     // then start buffering for example for a seek.
0106     if (m_mediaObject->state() != Phonon::PlayingState
0107             && (m_prevState != Phonon::PlayingState
0108                 && m_mediaObject->state() != Phonon::BufferingState))
0109         P_INVALID_STATE("Received tick outside of Playing state.");
0110     // If and only if we did not queue a new source may a seek back in time
0111     // result in a reemission of the signal. It should not, but it is allowed.
0112     // Point being, if the API consumer did not set one the first time, they
0113     // likely will not care about it a second time either.
0114     if (m_aboutToFinishEmitted && (pos < m_aboutToFinishPos) && !m_sourceQueued)
0115         m_aboutToFinishEmitted = false;
0116     m_pos = pos;
0117 }
0118 
0119 void StatesValidator::validateStateChange(Phonon::State newstate, Phonon::State oldstate)
0120 {
0121     if (!validateStateTransition(newstate, oldstate)) {
0122         pDebug() << "Invalid state transition:" << oldstate << "->" << newstate;
0123         P_INVALID_STATE("Invalid state transition");
0124     } else {
0125         pDebug() << "Valid state transition:" << oldstate << "->" << newstate;
0126     }
0127     m_prevState = oldstate;
0128 }
0129 
0130 bool StatesValidator::validateStateTransition(Phonon::State newstate, Phonon::State oldstate)
0131 {
0132     // Non-playback states trigger a reset of aboutToFinish.
0133     switch (oldstate) {
0134     case Phonon::StoppedState:
0135         switch (newstate) {
0136         case Phonon::LoadingState:
0137         case Phonon::PlayingState:
0138         case Phonon::PausedState:
0139             return true;
0140         default:
0141             return false;
0142         }
0143         break;
0144     case Phonon::LoadingState:
0145         switch (newstate) {
0146         case Phonon::ErrorState:
0147         case Phonon::StoppedState:
0148             return true;
0149         default:
0150             return false;
0151         }
0152         break;
0153     case Phonon::ErrorState:
0154         switch (newstate) {
0155         case Phonon::LoadingState:
0156             return true;
0157         default:
0158             return false;
0159         }
0160         break;
0161     case Phonon::PlayingState:
0162         switch (newstate) {
0163         case Phonon::PausedState:
0164         case Phonon::BufferingState:
0165         case Phonon::ErrorState:
0166         case Phonon::StoppedState:
0167             return true;
0168         default:
0169             return false;
0170         }
0171         break;
0172     case Phonon::PausedState:
0173         switch (newstate) {
0174         case Phonon::PlayingState:
0175         case Phonon::BufferingState:
0176         case Phonon::ErrorState:
0177         case Phonon::StoppedState:
0178             return true;
0179         default:
0180             return false;
0181         }
0182         break;
0183     case Phonon::BufferingState:
0184         switch (newstate) {
0185         case Phonon::PlayingState:
0186         case Phonon::PausedState:
0187         case Phonon::ErrorState:
0188 // TODO: buffering state needs fixing, should not transit to stop
0189         case Phonon::StoppedState:
0190             return true;
0191         default:
0192             return false;
0193         }
0194     }
0195     return false;
0196 }
0197 
0198 } // namespace Phonon
0199