1/*
2 Copyright (C) 2012 Trever Fischer <tdfischer@fedoraproject.org>
3 Copyright (C) 2012 Harald Sitter <sitter@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), Nokia Corporation
11 (or its successors, if any) and the KDE Free Qt Foundation, which shall
12 act as a proxy defined in Section 6 of version 3 of the license.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "statesvalidator_p.h"
24
25#include "mediaobject.h"
26#include "phononnamespace_p.h"
27
28#define P_INVALID_STATE(msg) Q_ASSERT_X(0, __FILE__, msg)
29
30namespace Phonon
31{
32
33StatesValidator::StatesValidator(MediaObject *parent)
34 : QObject(parent)
35 , m_mediaObject(parent)
36 , m_prevState(Phonon::ErrorState)
37 , m_sourceQueued(false)
38 , m_aboutToFinishEmitted(false)
39 , m_aboutToFinishBeforeSeek(false)
40 , m_aboutToFinishPos(-1)
41{
42 connect(sender: m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
43 receiver: this, SLOT(validateStateChange(Phonon::State,Phonon::State)));
44 connect(sender: m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
45 receiver: this, SLOT(validateSourceChange()));
46 connect(sender: m_mediaObject, SIGNAL(tick(qint64)), receiver: this, SLOT(validateTick(qint64)));
47 connect(sender: m_mediaObject, SIGNAL(aboutToFinish()), receiver: this, SLOT(validateAboutToFinish()));
48 connect(sender: m_mediaObject, SIGNAL(finished()), receiver: this, SLOT(validateFinished()));
49 connect(sender: m_mediaObject, SIGNAL(bufferStatus(int)), receiver: this, SLOT(validateBufferStatus()));
50}
51
52StatesValidator::~StatesValidator()
53{
54}
55
56/**
57 * The aboutToFinish signal is emitted when the queue is coming to an end.
58 * This in particular means that it must not be emitted twice, unless no track
59 * is in the queue and the user seeked back in time before finished
60 * Since we track the frontend signal here, we only get this signal when the
61 * queue is in fact empty in the frontend.
62 * It can however happen that the frontend already delivered the last queue item,
63 * then the user seeks and the backend forgets to use the already delivered item,
64 * emitting a bogus aboutToFinish.
65 */
66void StatesValidator::validateAboutToFinish()
67{
68 if (m_aboutToFinishEmitted)
69 P_INVALID_STATE("aboutToFinish emitted more than once!");
70 m_aboutToFinishEmitted = true;
71 m_aboutToFinishPos = m_pos;
72}
73
74void StatesValidator::validateFinished()
75{
76 if (m_mediaObject->state() != Phonon::PlayingState)
77 P_INVALID_STATE("Playback finished when we weren't playing!");
78}
79
80void StatesValidator::validateSourceChange()
81{
82 if (m_mediaObject->state() != Phonon::StoppedState
83 && m_mediaObject->state() != Phonon::PlayingState
84 && m_mediaObject->state() != Phonon::PausedState
85 && m_mediaObject->state() != Phonon::BufferingState) {
86 P_INVALID_STATE("Source got changed outside a valid state");
87 }
88 m_sourceQueued = false; // Once we get the signal the backend internal one-source queue is definitely cleared.
89 m_aboutToFinishEmitted = false;
90 m_aboutToFinishBeforeSeek = false;
91}
92
93void StatesValidator::validateBufferStatus()
94{
95 if (m_mediaObject->state() != Phonon::PlayingState
96 && m_mediaObject->state() != Phonon::PausedState
97 && m_mediaObject->state() != Phonon::BufferingState) {
98 P_INVALID_STATE("Buffer status changed when we weren't supposed to be buffering");
99 }
100}
101
102void StatesValidator::validateTick(qint64 pos)
103{
104 // Mind that Buffering is a concurrent state, you may have been playing and
105 // then start buffering for example for a seek.
106 if (m_mediaObject->state() != Phonon::PlayingState
107 && (m_prevState != Phonon::PlayingState
108 && m_mediaObject->state() != Phonon::BufferingState))
109 P_INVALID_STATE("Received tick outside of Playing state.");
110 // If and only if we did not queue a new source may a seek back in time
111 // result in a reemission of the signal. It should not, but it is allowed.
112 // Point being, if the API consumer did not set one the first time, they
113 // likely will not care about it a second time either.
114 if (m_aboutToFinishEmitted && (pos < m_aboutToFinishPos) && !m_sourceQueued)
115 m_aboutToFinishEmitted = false;
116 m_pos = pos;
117}
118
119void StatesValidator::validateStateChange(Phonon::State newstate, Phonon::State oldstate)
120{
121 if (!validateStateTransition(newstate, oldstate)) {
122 pDebug() << "Invalid state transition:" << oldstate << "->" << newstate;
123 P_INVALID_STATE("Invalid state transition");
124 } else {
125 pDebug() << "Valid state transition:" << oldstate << "->" << newstate;
126 }
127 m_prevState = oldstate;
128}
129
130bool StatesValidator::validateStateTransition(Phonon::State newstate, Phonon::State oldstate)
131{
132 // Non-playback states trigger a reset of aboutToFinish.
133 switch (oldstate) {
134 case Phonon::StoppedState:
135 switch (newstate) {
136 case Phonon::LoadingState:
137 case Phonon::PlayingState:
138 case Phonon::PausedState:
139 return true;
140 default:
141 return false;
142 }
143 break;
144 case Phonon::LoadingState:
145 switch (newstate) {
146 case Phonon::ErrorState:
147 case Phonon::StoppedState:
148 return true;
149 default:
150 return false;
151 }
152 break;
153 case Phonon::ErrorState:
154 switch (newstate) {
155 case Phonon::LoadingState:
156 return true;
157 default:
158 return false;
159 }
160 break;
161 case Phonon::PlayingState:
162 switch (newstate) {
163 case Phonon::PausedState:
164 case Phonon::BufferingState:
165 case Phonon::ErrorState:
166 case Phonon::StoppedState:
167 return true;
168 default:
169 return false;
170 }
171 break;
172 case Phonon::PausedState:
173 switch (newstate) {
174 case Phonon::PlayingState:
175 case Phonon::BufferingState:
176 case Phonon::ErrorState:
177 case Phonon::StoppedState:
178 return true;
179 default:
180 return false;
181 }
182 break;
183 case Phonon::BufferingState:
184 switch (newstate) {
185 case Phonon::PlayingState:
186 case Phonon::PausedState:
187 case Phonon::ErrorState:
188// TODO: buffering state needs fixing, should not transit to stop
189 case Phonon::StoppedState:
190 return true;
191 default:
192 return false;
193 }
194 }
195 return false;
196}
197
198} // namespace Phonon
199
200

source code of phonon/phonon/statesvalidator.cpp