/* 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 <http://www.gnu.org/licenses/>.
*/
#include <QUrl>
#include "abstractmediaplayer.h"
#include "defs.h"
#include "utils.h"
QT_BEGIN_NAMESPACE
using namespace Phonon;
using namespace Phonon::MMF;
/*! \class MMF::AbstractMediaPlayer
\internal
*/
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
const int NullMaxVolume = -1;
//-----------------------------------------------------------------------------
// Constructor / destructor
//-----------------------------------------------------------------------------
MMF::AbstractMediaPlayer::AbstractMediaPlayer() :
m_playPending(false)
, m_tickTimer(new QTimer(this))
, m_mmfMaxVolume(NullMaxVolume)
{
connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick()));
}
MMF::AbstractMediaPlayer::AbstractMediaPlayer(const AbstractPlayer& player) :
AbstractPlayer(player)
, m_playPending(false)
, m_tickTimer(new QTimer(this))
, m_mmfMaxVolume(NullMaxVolume)
{
connect(m_tickTimer.data(), SIGNAL(timeout()), this, SLOT(tick()));
}
//-----------------------------------------------------------------------------
// MediaObjectInterface
//-----------------------------------------------------------------------------
void MMF::AbstractMediaPlayer::play()
{
TRACE_CONTEXT(AbstractMediaPlayer::play, EAudioApi);
TRACE_ENTRY("state %d", privateState());
switch (privateState()) {
case GroundState:
setError(NormalError);
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", privateState());
}
void MMF::AbstractMediaPlayer::pause()
{
TRACE_CONTEXT(AbstractMediaPlayer::pause, EAudioApi);
TRACE_ENTRY("state %d", privateState());
m_playPending = false;
switch (privateState()) {
case GroundState:
case LoadingState:
case PausedState:
case ErrorState:
// Do nothing
break;
case StoppedState:
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", privateState());
}
void MMF::AbstractMediaPlayer::stop()
{
TRACE_CONTEXT(AbstractMediaPlayer::stop, EAudioApi);
TRACE_ENTRY("state %d", privateState());
m_playPending = false;
switch (privateState()) {
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", privateState());
}
void MMF::AbstractMediaPlayer::seek(qint64 ms)
{
TRACE_CONTEXT(AbstractMediaPlayer::seek, EAudioApi);
TRACE_ENTRY("state %d pos %Ld", state(), ms);
switch (privateState()) {
// 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", privateState(), tickInterval(), interval);
m_tickTimer->setInterval(interval);
TRACE_EXIT_0();
}
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", privateState(), 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: {
symbianErr = openFile(file);
break;
}
case MediaSource::Url: {
const QUrl url(source.url());
if (url.scheme() == QLatin1String("file")) {
symbianErr = openFile(file);
}
else {
TRACE_0("Source type not supported");
// TODO: support network 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)
setError(NormalError);
}
TRACE_EXIT_0();
}
void MMF::AbstractMediaPlayer::setNextSource(const MediaSource &source)
{
TRACE_CONTEXT(AbstractMediaPlayer::setNextSource, EAudioApi);
TRACE_ENTRY("state %d", privateState());
// 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", privateState());
AbstractPlayer::volumeChanged(volume);
doVolumeChanged();
TRACE_EXIT_0();
}
void MMF::AbstractMediaPlayer::doVolumeChanged()
{
switch (privateState()) {
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) {
setError(NormalError);
}
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();
}
qint64 MMF::AbstractMediaPlayer::toMilliSeconds(const TTimeIntervalMicroSeconds &in)
{
return in.Int64() / 1000;
}
void MMF::AbstractMediaPlayer::changeState(PrivateState newState)
{
TRACE_CONTEXT(AbstractPlayer::changeState, EAudioInternal);
TRACE_ENTRY("state %d newState %d", privateState(), newState);
// TODO: add some invariants to check that the transition is valid
const Phonon::State oldPhononState = phononState(privateState());
const Phonon::State newPhononState = phononState(newState);
if (oldPhononState != newPhononState) {
TRACE("emit stateChanged(%d, %d)", newPhononState, oldPhononState);
emit stateChanged(newPhononState, oldPhononState);
}
setState(newState);
if (
LoadingState == oldPhononState
and StoppedState == newPhononState
) {
// Ensure initial volume is set on MMF API before starting playback
doVolumeChanged();
// Check whether play() was called while clip was being loaded. If so,
// playback should be started now
if (m_playPending) {
TRACE_0("play was called while loading; starting playback now");
m_playPending = false;
play();
}
}
TRACE_EXIT_0();
}
//-----------------------------------------------------------------------------
// Slots
//-----------------------------------------------------------------------------
void MMF::AbstractMediaPlayer::tick()
{
// For the MWC compiler, we need to qualify the base class.
emit MMF::AbstractPlayer::tick(currentTime());
}
QT_END_NAMESPACE