34 //----------------------------------------------------------------------------- |
35 //----------------------------------------------------------------------------- |
35 // Constants |
36 // Constants |
36 //----------------------------------------------------------------------------- |
37 //----------------------------------------------------------------------------- |
37 |
38 |
38 const int NullMaxVolume = -1; |
39 const int NullMaxVolume = -1; |
|
40 const int BufferStatusTimerInterval = 100; // ms |
39 |
41 |
40 |
42 |
41 //----------------------------------------------------------------------------- |
43 //----------------------------------------------------------------------------- |
42 // Constructor / destructor |
44 // Constructor / destructor |
43 //----------------------------------------------------------------------------- |
45 //----------------------------------------------------------------------------- |
44 |
46 |
45 MMF::AbstractMediaPlayer::AbstractMediaPlayer() : |
47 MMF::AbstractMediaPlayer::AbstractMediaPlayer |
46 m_playPending(false) |
48 (MediaObject *parent, const AbstractPlayer *player) |
47 , m_tickTimer(new QTimer(this)) |
49 : AbstractPlayer(player) |
|
50 , m_parent(parent) |
|
51 , m_playPending(false) |
|
52 , m_positionTimer(new QTimer(this)) |
|
53 , m_bufferStatusTimer(new QTimer(this)) |
48 , m_mmfMaxVolume(NullMaxVolume) |
54 , m_mmfMaxVolume(NullMaxVolume) |
49 { |
55 , m_prefinishMarkSent(false) |
50 connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick())); |
56 , m_aboutToFinishSent(false) |
51 } |
57 { |
52 |
58 connect(m_positionTimer.data(), SIGNAL(timeout()), this, SLOT(positionTick())); |
53 MMF::AbstractMediaPlayer::AbstractMediaPlayer(const AbstractPlayer& player) : |
59 connect(m_bufferStatusTimer.data(), SIGNAL(timeout()), this, SLOT(bufferStatusTick())); |
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 } |
60 } |
61 |
61 |
62 //----------------------------------------------------------------------------- |
62 //----------------------------------------------------------------------------- |
63 // MediaObjectInterface |
63 // MediaObjectInterface |
64 //----------------------------------------------------------------------------- |
64 //----------------------------------------------------------------------------- |
201 void MMF::AbstractMediaPlayer::doSetTickInterval(qint32 interval) |
209 void MMF::AbstractMediaPlayer::doSetTickInterval(qint32 interval) |
202 { |
210 { |
203 TRACE_CONTEXT(AbstractMediaPlayer::doSetTickInterval, EAudioApi); |
211 TRACE_CONTEXT(AbstractMediaPlayer::doSetTickInterval, EAudioApi); |
204 TRACE_ENTRY("state %d m_interval %d interval %d", privateState(), tickInterval(), interval); |
212 TRACE_ENTRY("state %d m_interval %d interval %d", privateState(), tickInterval(), interval); |
205 |
213 |
206 m_tickTimer->setInterval(interval); |
214 m_positionTimer->setInterval(interval); |
207 |
215 |
208 TRACE_EXIT_0(); |
216 TRACE_EXIT_0(); |
209 } |
217 } |
210 |
218 |
211 MediaSource MMF::AbstractMediaPlayer::source() const |
219 void MMF::AbstractMediaPlayer::open(const MediaSource &source, RFile& file) |
212 { |
|
213 return m_source; |
|
214 } |
|
215 |
|
216 void MMF::AbstractMediaPlayer::setFileSource(const MediaSource &source, RFile& file) |
|
217 { |
220 { |
218 TRACE_CONTEXT(AbstractMediaPlayer::setFileSource, EAudioApi); |
221 TRACE_CONTEXT(AbstractMediaPlayer::setFileSource, EAudioApi); |
219 TRACE_ENTRY("state %d source.type %d", privateState(), source.type()); |
222 TRACE_ENTRY("state %d source.type %d", privateState(), source.type()); |
220 |
223 |
221 close(); |
224 close(); |
222 changeState(GroundState); |
225 changeState(GroundState); |
223 |
226 |
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; |
227 TInt symbianErr = KErrNone; |
229 |
228 QString errorMessage; |
230 switch (m_source.type()) { |
229 |
|
230 switch (source.type()) { |
231 case MediaSource::LocalFile: { |
231 case MediaSource::LocalFile: { |
232 symbianErr = openFile(file); |
232 symbianErr = openFile(file); |
|
233 if (KErrNone != symbianErr) |
|
234 errorMessage = tr("Error opening file"); |
233 break; |
235 break; |
234 } |
236 } |
235 |
237 |
236 case MediaSource::Url: { |
238 case MediaSource::Url: { |
237 const QUrl url(source.url()); |
239 const QUrl url(source.url()); |
238 |
240 |
239 if (url.scheme() == QLatin1String("file")) { |
241 if (url.scheme() == QLatin1String("file")) { |
240 symbianErr = openFile(file); |
242 symbianErr = openFile(file); |
241 } |
243 if (KErrNone != symbianErr) |
242 else { |
244 errorMessage = tr("Error opening file"); |
243 TRACE_0("Source type not supported"); |
245 } else { |
244 // TODO: support network URLs |
246 symbianErr = openUrl(url.toString()); |
245 symbianErr = KErrNotSupported; |
247 if (KErrNone != symbianErr) |
246 } |
248 errorMessage = tr("Error opening URL"); |
247 |
249 } |
248 break; |
250 |
249 } |
251 break; |
250 |
252 } |
251 case MediaSource::Invalid: |
253 |
252 case MediaSource::Disc: |
254 // Other source types are handled in MediaObject::createPlayer |
253 case MediaSource::Stream: |
255 |
254 TRACE_0("Source type not supported"); |
256 // Protection against adding new media types and forgetting to update this switch |
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: |
257 default: |
265 TRACE_PANIC(InvalidMediaTypePanic); |
258 TRACE_PANIC(InvalidMediaTypePanic); |
266 } |
259 } |
267 |
260 |
268 if (KErrNone == symbianErr) { |
261 if (errorMessage.isEmpty()) { |
269 changeState(LoadingState); |
262 changeState(LoadingState); |
270 } else { |
263 } else { |
271 TRACE("error %d", symbianErr) |
264 if (symbianErr) |
272 setError(NormalError); |
265 setError(errorMessage, symbianErr); |
|
266 else |
|
267 setError(errorMessage); |
273 } |
268 } |
274 |
269 |
275 TRACE_EXIT_0(); |
270 TRACE_EXIT_0(); |
276 } |
271 } |
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 |
272 |
292 void MMF::AbstractMediaPlayer::volumeChanged(qreal volume) |
273 void MMF::AbstractMediaPlayer::volumeChanged(qreal volume) |
293 { |
274 { |
294 TRACE_CONTEXT(AbstractMediaPlayer::volumeChanged, EAudioInternal); |
275 TRACE_CONTEXT(AbstractMediaPlayer::volumeChanged, EAudioInternal); |
295 TRACE_ENTRY("state %d", privateState()); |
276 TRACE_ENTRY("state %d", privateState()); |
328 default: |
338 default: |
329 Utils::panic(InvalidStatePanic); |
339 Utils::panic(InvalidStatePanic); |
330 } |
340 } |
331 } |
341 } |
332 |
342 |
333 |
|
334 //----------------------------------------------------------------------------- |
343 //----------------------------------------------------------------------------- |
335 // Protected functions |
344 // Protected functions |
336 //----------------------------------------------------------------------------- |
345 //----------------------------------------------------------------------------- |
337 |
346 |
338 void MMF::AbstractMediaPlayer::startTickTimer() |
347 void MMF::AbstractMediaPlayer::bufferingStarted() |
339 { |
348 { |
340 m_tickTimer->start(tickInterval()); |
349 m_stateBeforeBuffering = privateState(); |
341 } |
350 changeState(BufferingState); |
342 |
351 bufferStatusTick(); |
343 void MMF::AbstractMediaPlayer::stopTickTimer() |
352 startBufferStatusTimer(); |
344 { |
353 } |
345 m_tickTimer->stop(); |
354 |
|
355 void MMF::AbstractMediaPlayer::bufferingComplete() |
|
356 { |
|
357 stopBufferStatusTimer(); |
|
358 emit MMF::AbstractPlayer::bufferStatus(100); |
|
359 changeState(m_stateBeforeBuffering); |
346 } |
360 } |
347 |
361 |
348 void MMF::AbstractMediaPlayer::maxVolumeChanged(int mmfMaxVolume) |
362 void MMF::AbstractMediaPlayer::maxVolumeChanged(int mmfMaxVolume) |
349 { |
363 { |
350 m_mmfMaxVolume = mmfMaxVolume; |
364 m_mmfMaxVolume = mmfMaxVolume; |
351 doVolumeChanged(); |
365 doVolumeChanged(); |
352 } |
366 } |
353 |
367 |
|
368 void MMF::AbstractMediaPlayer::playbackComplete(int error) |
|
369 { |
|
370 stopTimers(); |
|
371 |
|
372 if (KErrNone == error) { |
|
373 changeState(StoppedState); |
|
374 |
|
375 // MediaObject::switchToNextSource deletes the current player, so we |
|
376 // call it via delayed slot invokation to ensure that this object does |
|
377 // not get deleted during execution of a member function. |
|
378 QMetaObject::invokeMethod(m_parent, "switchToNextSource", Qt::QueuedConnection); |
|
379 } |
|
380 else { |
|
381 setError(tr("Playback complete"), error); |
|
382 } |
|
383 } |
|
384 |
354 qint64 MMF::AbstractMediaPlayer::toMilliSeconds(const TTimeIntervalMicroSeconds &in) |
385 qint64 MMF::AbstractMediaPlayer::toMilliSeconds(const TTimeIntervalMicroSeconds &in) |
355 { |
386 { |
356 return in.Int64() / 1000; |
387 return in.Int64() / 1000; |
357 } |
388 } |
358 |
389 |
359 //----------------------------------------------------------------------------- |
390 //----------------------------------------------------------------------------- |
360 // Slots |
391 // Slots |
361 //----------------------------------------------------------------------------- |
392 //----------------------------------------------------------------------------- |
362 |
393 |
363 void MMF::AbstractMediaPlayer::tick() |
394 void MMF::AbstractMediaPlayer::positionTick() |
364 { |
395 { |
365 // For the MWC compiler, we need to qualify the base class. |
396 emitMarksIfReached(); |
366 emit MMF::AbstractPlayer::tick(currentTime()); |
397 |
|
398 const qint64 current = currentTime(); |
|
399 emit MMF::AbstractPlayer::tick(current); |
|
400 } |
|
401 |
|
402 void MMF::AbstractMediaPlayer::emitMarksIfReached() |
|
403 { |
|
404 const qint64 current = currentTime(); |
|
405 const qint64 total = totalTime(); |
|
406 const qint64 remaining = total - current; |
|
407 |
|
408 if (prefinishMark() && !m_prefinishMarkSent) { |
|
409 if (remaining < (prefinishMark() + tickInterval()/2)) { |
|
410 m_prefinishMarkSent = true; |
|
411 emit prefinishMarkReached(remaining); |
|
412 } |
|
413 } |
|
414 |
|
415 if (!m_aboutToFinishSent) { |
|
416 if (remaining < tickInterval()) { |
|
417 m_aboutToFinishSent = true; |
|
418 emit aboutToFinish(); |
|
419 } |
|
420 } |
|
421 } |
|
422 |
|
423 void MMF::AbstractMediaPlayer::resetMarksIfRewound() |
|
424 { |
|
425 const qint64 current = currentTime(); |
|
426 const qint64 total = totalTime(); |
|
427 const qint64 remaining = total - current; |
|
428 |
|
429 if (prefinishMark() && m_prefinishMarkSent) |
|
430 if (remaining >= (prefinishMark() + tickInterval()/2)) |
|
431 m_prefinishMarkSent = false; |
|
432 |
|
433 if (m_aboutToFinishSent) |
|
434 if (remaining >= tickInterval()) |
|
435 m_aboutToFinishSent = false; |
|
436 } |
|
437 |
|
438 void MMF::AbstractMediaPlayer::bufferStatusTick() |
|
439 { |
|
440 emit MMF::AbstractPlayer::bufferStatus(bufferStatus()); |
367 } |
441 } |
368 |
442 |
369 void MMF::AbstractMediaPlayer::changeState(PrivateState newState) |
443 void MMF::AbstractMediaPlayer::changeState(PrivateState newState) |
370 { |
444 { |
371 TRACE_CONTEXT(AbstractMediaPlayer::changeState, EAudioInternal); |
445 TRACE_CONTEXT(AbstractMediaPlayer::changeState, EAudioInternal); |