/* This file is part of the KDE project.
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 or 3 of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library. If not, see .
*/
#include "abstractmediaplayer.h"
#include "defs.h"
#include "utils.h"
QT_BEGIN_NAMESPACE
using namespace Phonon;
using namespace Phonon::MMF;
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
const int NullMaxVolume = -1;
//-----------------------------------------------------------------------------
// Constructor / destructor
//-----------------------------------------------------------------------------
MMF::AbstractMediaPlayer::AbstractMediaPlayer() :
m_state(GroundState)
, m_error(NoError)
, m_playPending(false)
, m_tickTimer(new QTimer(this))
, m_volume(InitialVolume)
, m_mmfMaxVolume(NullMaxVolume)
{
connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick()));
}
MMF::AbstractMediaPlayer::AbstractMediaPlayer(const AbstractPlayer& player) :
AbstractPlayer(player)
, m_state(GroundState)
, m_error(NoError)
, m_playPending(false)
, m_tickTimer(new QTimer(this))
, m_volume(InitialVolume)
, m_mmfMaxVolume(NullMaxVolume)
{
connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick()));
}
MMF::AbstractMediaPlayer::~AbstractMediaPlayer()
{
}
//-----------------------------------------------------------------------------
// MediaObjectInterface
//-----------------------------------------------------------------------------
void MMF::AbstractMediaPlayer::play()
{
TRACE_CONTEXT(AbstractMediaPlayer::play, EAudioApi);
TRACE_ENTRY("state %d", m_state);
switch (m_state) {
case GroundState:
// Is this the correct error? Really we want 'NotReadyError'
m_error = NormalError;
changeState(ErrorState);
break;
case LoadingState:
m_playPending = true;
break;
case StoppedState:
case PausedState:
doPlay();
startTickTimer();
changeState(PlayingState);
break;
case PlayingState:
case BufferingState:
case ErrorState:
// Do nothing
break;
// Protection against adding new states and forgetting to update this switch
default:
TRACE_PANIC(InvalidStatePanic);
}
TRACE_EXIT("state %d", m_state);
}
void MMF::AbstractMediaPlayer::pause()
{
TRACE_CONTEXT(AbstractMediaPlayer::pause, EAudioApi);
TRACE_ENTRY("state %d", m_state);
m_playPending = false;
switch (m_state) {
case GroundState:
case LoadingState:
case StoppedState:
case PausedState:
case ErrorState:
// Do nothing
break;
case PlayingState:
case BufferingState:
doPause();
stopTickTimer();
changeState(PausedState);
break;
// Protection against adding new states and forgetting to update this switch
default:
TRACE_PANIC(InvalidStatePanic);
}
TRACE_EXIT("state %d", m_state);
}
void MMF::AbstractMediaPlayer::stop()
{
TRACE_CONTEXT(AbstractMediaPlayer::stop, EAudioApi);
TRACE_ENTRY("state %d", m_state);
m_playPending = false;
switch (m_state) {
case GroundState:
case LoadingState:
case StoppedState:
case ErrorState:
// Do nothing
break;
case PlayingState:
case BufferingState:
case PausedState:
doStop();
stopTickTimer();
changeState(StoppedState);
break;
// Protection against adding new states and forgetting to update this switch
default:
TRACE_PANIC(InvalidStatePanic);
}
TRACE_EXIT("state %d", m_state);
}
void MMF::AbstractMediaPlayer::seek(qint64 ms)
{
TRACE_CONTEXT(AbstractMediaPlayer::seek, EAudioApi);
TRACE_ENTRY("state %d pos %Ld", state(), ms);
switch (m_state) {
// Fallthrough all these
case GroundState:
case StoppedState:
case PausedState:
case PlayingState:
case LoadingState:
{
const bool tickTimerWasRunning = m_tickTimer->isActive();
stopTickTimer();
doSeek(ms);
if (tickTimerWasRunning) {
startTickTimer();
}
break;
}
case BufferingState:
// Fallthrough
case ErrorState:
// Do nothing
break;
}
TRACE_EXIT_0();
}
bool MMF::AbstractMediaPlayer::isSeekable() const
{
return true;
}
void MMF::AbstractMediaPlayer::doSetTickInterval(qint32 interval)
{
TRACE_CONTEXT(AbstractMediaPlayer::doSetTickInterval, EAudioApi);
TRACE_ENTRY("state %d m_interval %d interval %d", m_state, tickInterval(), interval);
m_tickTimer->setInterval(interval);
TRACE_EXIT_0();
}
Phonon::ErrorType MMF::AbstractMediaPlayer::errorType() const
{
const Phonon::ErrorType result = (ErrorState == m_state)
? m_error : NoError;
return result;
}
QString MMF::AbstractMediaPlayer::errorString() const
{
// TODO: put in proper error strings
QString result;
return result;
}
Phonon::State MMF::AbstractMediaPlayer::state() const
{
return phononState(m_state);
}
MediaSource MMF::AbstractMediaPlayer::source() const
{
return m_source;
}
void MMF::AbstractMediaPlayer::setFileSource(const MediaSource &source, RFile& file)
{
TRACE_CONTEXT(AbstractMediaPlayer::setFileSource, EAudioApi);
TRACE_ENTRY("state %d source.type %d", m_state, source.type());
close();
changeState(GroundState);
// TODO: is it correct to assign even if the media type is not supported in
// the switch statement below?
m_source = source;
TInt symbianErr = KErrNone;
switch (m_source.type()) {
case MediaSource::LocalFile: {
// TODO: work out whose responsibility it is to ensure that paths
// are Symbian-style, i.e. have backslashes for path delimiters.
// Until then, use this utility function...
//const QHBufC filename = Utils::symbianFilename(m_source.fileName());
//TRAP(symbianErr, m_player->OpenFileL(*filename));
// Open using shared filehandle
// This is a temporary hack to work around KErrInUse from MMF
// client utility OpenFileL calls
//TRAP(symbianErr, m_player->OpenFileL(file));
symbianErr = openFile(file);
break;
}
case MediaSource::Url: {
TRACE_0("Source type not supported");
// TODO: support opening URLs
symbianErr = KErrNotSupported;
break;
}
case MediaSource::Invalid:
case MediaSource::Disc:
case MediaSource::Stream:
TRACE_0("Source type not supported");
symbianErr = KErrNotSupported;
break;
case MediaSource::Empty:
TRACE_0("Empty source - doing nothing");
TRACE_EXIT_0();
return;
// Protection against adding new media types and forgetting to update this switch
default:
TRACE_PANIC(InvalidMediaTypePanic);
}
if (KErrNone == symbianErr) {
changeState(LoadingState);
} else {
TRACE("error %d", symbianErr)
// TODO: do something with the value of symbianErr?
m_error = NormalError;
changeState(ErrorState);
}
TRACE_EXIT_0();
}
void MMF::AbstractMediaPlayer::setNextSource(const MediaSource &source)
{
TRACE_CONTEXT(AbstractMediaPlayer::setNextSource, EAudioApi);
TRACE_ENTRY("state %d", m_state);
// TODO: handle 'next source'
m_nextSource = source;
Q_UNUSED(source);
TRACE_EXIT_0();
}
//-----------------------------------------------------------------------------
// VolumeObserver
//-----------------------------------------------------------------------------
void MMF::AbstractMediaPlayer::volumeChanged(qreal volume)
{
TRACE_CONTEXT(AbstractMediaPlayer::volumeChanged, EAudioInternal);
TRACE_ENTRY("state %d", m_state);
m_volume = volume;
doVolumeChanged();
TRACE_EXIT_0();
}
void MMF::AbstractMediaPlayer::doVolumeChanged()
{
switch (m_state) {
case GroundState:
case LoadingState:
case ErrorState:
// Do nothing
break;
case StoppedState:
case PausedState:
case PlayingState:
case BufferingState: {
const int err = setDeviceVolume(m_volume * m_mmfMaxVolume);
if (KErrNone != err) {
m_error = NormalError;
changeState(ErrorState);
}
break;
}
// Protection against adding new states and forgetting to update this
// switch
default:
Utils::panic(InvalidStatePanic);
}
}
//-----------------------------------------------------------------------------
// Protected functions
//-----------------------------------------------------------------------------
void MMF::AbstractMediaPlayer::startTickTimer()
{
m_tickTimer->start(tickInterval());
}
void MMF::AbstractMediaPlayer::stopTickTimer()
{
m_tickTimer->stop();
}
void MMF::AbstractMediaPlayer::maxVolumeChanged(int mmfMaxVolume)
{
m_mmfMaxVolume = mmfMaxVolume;
doVolumeChanged();
}
Phonon::State MMF::AbstractMediaPlayer::phononState() const
{
return phononState(m_state);
}
Phonon::State MMF::AbstractMediaPlayer::phononState(PrivateState state)
{
const Phonon::State phononState =
GroundState == state
? Phonon::LoadingState
: static_cast(state);
return phononState;
}
void MMF::AbstractMediaPlayer::changeState(PrivateState newState)
{
TRACE_CONTEXT(AbstractMediaPlayer::changeState, EAudioInternal);
TRACE_ENTRY("state %d newState %d", m_state, newState);
// TODO: add some invariants to check that the transition is valid
const Phonon::State oldPhononState = phononState(m_state);
const Phonon::State newPhononState = phononState(newState);
if (oldPhononState != newPhononState) {
TRACE("emit stateChanged(%d, %d)", newPhononState, oldPhononState);
emit stateChanged(newPhononState, oldPhononState);
}
m_state = newState;
// Check whether play() was called while clip was being loaded. If so,
// playback should be started now
if (
LoadingState == oldPhononState
and StoppedState == newPhononState
and m_playPending
) {
TRACE_0("play was called while loading; starting playback now");
m_playPending = false;
play();
}
TRACE_EXIT_0();
}
void MMF::AbstractMediaPlayer::setError(Phonon::ErrorType error)
{
TRACE_CONTEXT(AbstractMediaPlayer::setError, EAudioInternal);
TRACE_ENTRY("state %d error %d", m_state, error);
m_error = error;
changeState(ErrorState);
TRACE_EXIT_0();
}
qint64 MMF::AbstractMediaPlayer::toMilliSeconds(const TTimeIntervalMicroSeconds &in)
{
return in.Int64() / 1000;
}
//-----------------------------------------------------------------------------
// Slots
//-----------------------------------------------------------------------------
void MMF::AbstractMediaPlayer::tick()
{
// For the MWC compiler, we need to qualify the base class.
emit MMF::AbstractPlayer::tick(currentTime());
}
QT_END_NAMESPACE