File indexing completed on 2025-02-09 05:31:53
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