|
1 /* This file is part of the KDE project. |
|
2 |
|
3 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 |
|
5 This library is free software: you can redistribute it and/or modify |
|
6 it under the terms of the GNU Lesser General Public License as published by |
|
7 the Free Software Foundation, either version 2.1 or 3 of the License. |
|
8 |
|
9 This library is distributed in the hope that it will be useful, |
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 GNU Lesser General Public License for more details. |
|
13 |
|
14 You should have received a copy of the GNU Lesser General Public License |
|
15 along with this library. If not, see <http://www.gnu.org/licenses/>. |
|
16 |
|
17 */ |
|
18 |
|
19 #include <QUrl> |
|
20 |
|
21 #include "abstractmediaplayer.h" |
|
22 #include "defs.h" |
|
23 #include "utils.h" |
|
24 |
|
25 QT_BEGIN_NAMESPACE |
|
26 |
|
27 using namespace Phonon; |
|
28 using namespace Phonon::MMF; |
|
29 |
|
30 /*! \class MMF::AbstractMediaPlayer |
|
31 \internal |
|
32 */ |
|
33 |
|
34 //----------------------------------------------------------------------------- |
|
35 // Constants |
|
36 //----------------------------------------------------------------------------- |
|
37 |
|
38 const int NullMaxVolume = -1; |
|
39 |
|
40 |
|
41 //----------------------------------------------------------------------------- |
|
42 // Constructor / destructor |
|
43 //----------------------------------------------------------------------------- |
|
44 |
|
45 MMF::AbstractMediaPlayer::AbstractMediaPlayer() : |
|
46 m_playPending(false) |
|
47 , m_tickTimer(new QTimer(this)) |
|
48 , m_mmfMaxVolume(NullMaxVolume) |
|
49 { |
|
50 connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick())); |
|
51 } |
|
52 |
|
53 MMF::AbstractMediaPlayer::AbstractMediaPlayer(const AbstractPlayer& player) : |
|
54 AbstractPlayer(player) |
|
55 , m_playPending(false) |
|
56 , m_tickTimer(new QTimer(this)) |
|
57 , m_mmfMaxVolume(NullMaxVolume) |
|
58 { |
|
59 connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick())); |
|
60 } |
|
61 |
|
62 //----------------------------------------------------------------------------- |
|
63 // MediaObjectInterface |
|
64 //----------------------------------------------------------------------------- |
|
65 |
|
66 void MMF::AbstractMediaPlayer::play() |
|
67 { |
|
68 TRACE_CONTEXT(AbstractMediaPlayer::play, EAudioApi); |
|
69 TRACE_ENTRY("state %d", privateState()); |
|
70 |
|
71 switch (privateState()) { |
|
72 case GroundState: |
|
73 setError(NormalError); |
|
74 break; |
|
75 |
|
76 case LoadingState: |
|
77 m_playPending = true; |
|
78 break; |
|
79 |
|
80 case StoppedState: |
|
81 case PausedState: |
|
82 doPlay(); |
|
83 startTickTimer(); |
|
84 changeState(PlayingState); |
|
85 break; |
|
86 |
|
87 case PlayingState: |
|
88 case BufferingState: |
|
89 case ErrorState: |
|
90 // Do nothing |
|
91 break; |
|
92 |
|
93 // Protection against adding new states and forgetting to update this switch |
|
94 default: |
|
95 TRACE_PANIC(InvalidStatePanic); |
|
96 } |
|
97 |
|
98 TRACE_EXIT("state %d", privateState()); |
|
99 } |
|
100 |
|
101 void MMF::AbstractMediaPlayer::pause() |
|
102 { |
|
103 TRACE_CONTEXT(AbstractMediaPlayer::pause, EAudioApi); |
|
104 TRACE_ENTRY("state %d", privateState()); |
|
105 |
|
106 m_playPending = false; |
|
107 |
|
108 switch (privateState()) { |
|
109 case GroundState: |
|
110 case LoadingState: |
|
111 case PausedState: |
|
112 case ErrorState: |
|
113 // Do nothing |
|
114 break; |
|
115 |
|
116 case StoppedState: |
|
117 case PlayingState: |
|
118 case BufferingState: |
|
119 doPause(); |
|
120 stopTickTimer(); |
|
121 changeState(PausedState); |
|
122 break; |
|
123 |
|
124 // Protection against adding new states and forgetting to update this switch |
|
125 default: |
|
126 TRACE_PANIC(InvalidStatePanic); |
|
127 } |
|
128 |
|
129 TRACE_EXIT("state %d", privateState()); |
|
130 } |
|
131 |
|
132 void MMF::AbstractMediaPlayer::stop() |
|
133 { |
|
134 TRACE_CONTEXT(AbstractMediaPlayer::stop, EAudioApi); |
|
135 TRACE_ENTRY("state %d", privateState()); |
|
136 |
|
137 m_playPending = false; |
|
138 |
|
139 switch (privateState()) { |
|
140 case GroundState: |
|
141 case LoadingState: |
|
142 case StoppedState: |
|
143 case ErrorState: |
|
144 // Do nothing |
|
145 break; |
|
146 |
|
147 case PlayingState: |
|
148 case BufferingState: |
|
149 case PausedState: |
|
150 doStop(); |
|
151 stopTickTimer(); |
|
152 changeState(StoppedState); |
|
153 break; |
|
154 |
|
155 // Protection against adding new states and forgetting to update this switch |
|
156 default: |
|
157 TRACE_PANIC(InvalidStatePanic); |
|
158 } |
|
159 |
|
160 TRACE_EXIT("state %d", privateState()); |
|
161 } |
|
162 |
|
163 void MMF::AbstractMediaPlayer::seek(qint64 ms) |
|
164 { |
|
165 TRACE_CONTEXT(AbstractMediaPlayer::seek, EAudioApi); |
|
166 TRACE_ENTRY("state %d pos %Ld", state(), ms); |
|
167 |
|
168 switch (privateState()) { |
|
169 // Fallthrough all these |
|
170 case GroundState: |
|
171 case StoppedState: |
|
172 case PausedState: |
|
173 case PlayingState: |
|
174 case LoadingState: |
|
175 { |
|
176 const bool tickTimerWasRunning = m_tickTimer->isActive(); |
|
177 stopTickTimer(); |
|
178 |
|
179 doSeek(ms); |
|
180 |
|
181 if (tickTimerWasRunning) { |
|
182 startTickTimer(); |
|
183 } |
|
184 break; |
|
185 } |
|
186 case BufferingState: |
|
187 // Fallthrough |
|
188 case ErrorState: |
|
189 // Do nothing |
|
190 break; |
|
191 } |
|
192 |
|
193 TRACE_EXIT_0(); |
|
194 } |
|
195 |
|
196 bool MMF::AbstractMediaPlayer::isSeekable() const |
|
197 { |
|
198 return true; |
|
199 } |
|
200 |
|
201 void MMF::AbstractMediaPlayer::doSetTickInterval(qint32 interval) |
|
202 { |
|
203 TRACE_CONTEXT(AbstractMediaPlayer::doSetTickInterval, EAudioApi); |
|
204 TRACE_ENTRY("state %d m_interval %d interval %d", privateState(), tickInterval(), interval); |
|
205 |
|
206 m_tickTimer->setInterval(interval); |
|
207 |
|
208 TRACE_EXIT_0(); |
|
209 } |
|
210 |
|
211 MediaSource MMF::AbstractMediaPlayer::source() const |
|
212 { |
|
213 return m_source; |
|
214 } |
|
215 |
|
216 void MMF::AbstractMediaPlayer::setFileSource(const MediaSource &source, RFile& file) |
|
217 { |
|
218 TRACE_CONTEXT(AbstractMediaPlayer::setFileSource, EAudioApi); |
|
219 TRACE_ENTRY("state %d source.type %d", privateState(), source.type()); |
|
220 |
|
221 close(); |
|
222 changeState(GroundState); |
|
223 |
|
224 // TODO: is it correct to assign even if the media type is not supported in |
|
225 // the switch statement below? |
|
226 m_source = source; |
|
227 |
|
228 TInt symbianErr = KErrNone; |
|
229 |
|
230 switch (m_source.type()) { |
|
231 case MediaSource::LocalFile: { |
|
232 symbianErr = openFile(file); |
|
233 break; |
|
234 } |
|
235 |
|
236 case MediaSource::Url: { |
|
237 const QUrl url(source.url()); |
|
238 |
|
239 if (url.scheme() == QLatin1String("file")) { |
|
240 symbianErr = openFile(file); |
|
241 } |
|
242 else { |
|
243 TRACE_0("Source type not supported"); |
|
244 // TODO: support network URLs |
|
245 symbianErr = KErrNotSupported; |
|
246 } |
|
247 |
|
248 break; |
|
249 } |
|
250 |
|
251 case MediaSource::Invalid: |
|
252 case MediaSource::Disc: |
|
253 case MediaSource::Stream: |
|
254 TRACE_0("Source type not supported"); |
|
255 symbianErr = KErrNotSupported; |
|
256 break; |
|
257 |
|
258 case MediaSource::Empty: |
|
259 TRACE_0("Empty source - doing nothing"); |
|
260 TRACE_EXIT_0(); |
|
261 return; |
|
262 |
|
263 // Protection against adding new media types and forgetting to update this switch |
|
264 default: |
|
265 TRACE_PANIC(InvalidMediaTypePanic); |
|
266 } |
|
267 |
|
268 if (KErrNone == symbianErr) { |
|
269 changeState(LoadingState); |
|
270 } else { |
|
271 TRACE("error %d", symbianErr) |
|
272 setError(NormalError); |
|
273 } |
|
274 |
|
275 TRACE_EXIT_0(); |
|
276 } |
|
277 |
|
278 void MMF::AbstractMediaPlayer::setNextSource(const MediaSource &source) |
|
279 { |
|
280 TRACE_CONTEXT(AbstractMediaPlayer::setNextSource, EAudioApi); |
|
281 TRACE_ENTRY("state %d", privateState()); |
|
282 |
|
283 // TODO: handle 'next source' |
|
284 |
|
285 m_nextSource = source; |
|
286 Q_UNUSED(source); |
|
287 |
|
288 TRACE_EXIT_0(); |
|
289 } |
|
290 |
|
291 |
|
292 //----------------------------------------------------------------------------- |
|
293 // VolumeObserver |
|
294 //----------------------------------------------------------------------------- |
|
295 |
|
296 void MMF::AbstractMediaPlayer::volumeChanged(qreal volume) |
|
297 { |
|
298 TRACE_CONTEXT(AbstractMediaPlayer::volumeChanged, EAudioInternal); |
|
299 TRACE_ENTRY("state %d", privateState()); |
|
300 |
|
301 AbstractPlayer::volumeChanged(volume); |
|
302 doVolumeChanged(); |
|
303 |
|
304 TRACE_EXIT_0(); |
|
305 } |
|
306 |
|
307 |
|
308 void MMF::AbstractMediaPlayer::doVolumeChanged() |
|
309 { |
|
310 switch (privateState()) { |
|
311 case GroundState: |
|
312 case LoadingState: |
|
313 case ErrorState: |
|
314 // Do nothing |
|
315 break; |
|
316 |
|
317 case StoppedState: |
|
318 case PausedState: |
|
319 case PlayingState: |
|
320 case BufferingState: { |
|
321 const int err = setDeviceVolume(m_volume * m_mmfMaxVolume); |
|
322 |
|
323 if (KErrNone != err) { |
|
324 setError(NormalError); |
|
325 } |
|
326 break; |
|
327 } |
|
328 |
|
329 // Protection against adding new states and forgetting to update this |
|
330 // switch |
|
331 default: |
|
332 Utils::panic(InvalidStatePanic); |
|
333 } |
|
334 } |
|
335 |
|
336 |
|
337 //----------------------------------------------------------------------------- |
|
338 // Protected functions |
|
339 //----------------------------------------------------------------------------- |
|
340 |
|
341 void MMF::AbstractMediaPlayer::startTickTimer() |
|
342 { |
|
343 m_tickTimer->start(tickInterval()); |
|
344 } |
|
345 |
|
346 void MMF::AbstractMediaPlayer::stopTickTimer() |
|
347 { |
|
348 m_tickTimer->stop(); |
|
349 } |
|
350 |
|
351 void MMF::AbstractMediaPlayer::maxVolumeChanged(int mmfMaxVolume) |
|
352 { |
|
353 m_mmfMaxVolume = mmfMaxVolume; |
|
354 doVolumeChanged(); |
|
355 } |
|
356 |
|
357 qint64 MMF::AbstractMediaPlayer::toMilliSeconds(const TTimeIntervalMicroSeconds &in) |
|
358 { |
|
359 return in.Int64() / 1000; |
|
360 } |
|
361 |
|
362 void MMF::AbstractMediaPlayer::changeState(PrivateState newState) |
|
363 { |
|
364 TRACE_CONTEXT(AbstractPlayer::changeState, EAudioInternal); |
|
365 TRACE_ENTRY("state %d newState %d", privateState(), newState); |
|
366 |
|
367 // TODO: add some invariants to check that the transition is valid |
|
368 |
|
369 const Phonon::State oldPhononState = phononState(privateState()); |
|
370 const Phonon::State newPhononState = phononState(newState); |
|
371 if (oldPhononState != newPhononState) { |
|
372 TRACE("emit stateChanged(%d, %d)", newPhononState, oldPhononState); |
|
373 emit stateChanged(newPhononState, oldPhononState); |
|
374 } |
|
375 |
|
376 setState(newState); |
|
377 |
|
378 if ( |
|
379 LoadingState == oldPhononState |
|
380 and StoppedState == newPhononState |
|
381 ) { |
|
382 // Ensure initial volume is set on MMF API before starting playback |
|
383 doVolumeChanged(); |
|
384 |
|
385 // Check whether play() was called while clip was being loaded. If so, |
|
386 // playback should be started now |
|
387 if (m_playPending) { |
|
388 TRACE_0("play was called while loading; starting playback now"); |
|
389 m_playPending = false; |
|
390 play(); |
|
391 } |
|
392 } |
|
393 |
|
394 TRACE_EXIT_0(); |
|
395 } |
|
396 |
|
397 //----------------------------------------------------------------------------- |
|
398 // Slots |
|
399 //----------------------------------------------------------------------------- |
|
400 |
|
401 void MMF::AbstractMediaPlayer::tick() |
|
402 { |
|
403 // For the MWC compiler, we need to qualify the base class. |
|
404 emit MMF::AbstractPlayer::tick(currentTime()); |
|
405 } |
|
406 |
|
407 QT_END_NAMESPACE |
|
408 |