diff options
author | Frans Englich <frans.englich@nokia.com> | 2009-08-12 14:11:00 (GMT) |
---|---|---|
committer | Frans Englich <frans.englich@nokia.com> | 2009-08-12 14:16:51 (GMT) |
commit | 44265f30fb40baaecd4cda1b5aca0efeed6abc20 (patch) | |
tree | 64d08be182c0d99ec4218408f97f01b0d6a2d351 /src/3rdparty/phonon/mmf/mediaobject.cpp | |
parent | 4cd7180ad3365c0e8eef8a22c825f3289a799ab0 (diff) | |
download | Qt-44265f30fb40baaecd4cda1b5aca0efeed6abc20.zip Qt-44265f30fb40baaecd4cda1b5aca0efeed6abc20.tar.gz Qt-44265f30fb40baaecd4cda1b5aca0efeed6abc20.tar.bz2 |
Commit patch by Gareth.
The patch originally contained all changed done to MMF Phonon, but this
commit contains the changes only Gareth did.
Diffstat (limited to 'src/3rdparty/phonon/mmf/mediaobject.cpp')
-rw-r--r-- | src/3rdparty/phonon/mmf/mediaobject.cpp | 523 |
1 files changed, 453 insertions, 70 deletions
diff --git a/src/3rdparty/phonon/mmf/mediaobject.cpp b/src/3rdparty/phonon/mmf/mediaobject.cpp index 0763c42..8a4b76e 100644 --- a/src/3rdparty/phonon/mmf/mediaobject.cpp +++ b/src/3rdparty/phonon/mmf/mediaobject.cpp @@ -17,188 +17,473 @@ along with this library. If not, see <http://www.gnu.org/licenses/>. */ #include <QUrl> +#include <QTimer> #include <private/qcore_symbian_p.h> #include "mediaobject.h" +#include "audiooutput.h" +#include "utils.h" using namespace Phonon; using namespace Phonon::MMF; -MMF::MediaObject::MediaObject(QObject *parent) : m_player(0) +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +const qint32 DefaultTickInterval = 20; +const int NullMaxVolume = -1; + + +//----------------------------------------------------------------------------- +// Constructor / destructor +//----------------------------------------------------------------------------- + +MMF::MediaObject::MediaObject(QObject *parent) : m_player(NULL) + , m_audioOutput(NULL) , m_error(NoError) - , m_state(StoppedState) + , m_state(GroundState) + , m_tickInterval(DefaultTickInterval) + , m_tickTimer(NULL) + , m_volume(0.0) + , m_maxVolume(NullMaxVolume) { + TRACE_CONTEXT(MediaObject::MediaObject, EAudioApi); + TRACE_ENTRY_0(); + Q_UNUSED(parent); - m_player = CDrmPlayerUtility::NewL(*this, 0, EMdaPriorityPreferenceNone); - m_player->RegisterForAudioLoadingNotification(*this); + // TODO: should leaves be trapped in the constructor? + m_player = CPlayerType::NewL(*this, 0, EMdaPriorityPreferenceNone); + + m_tickTimer = new QTimer(this); + connect(m_tickTimer, SIGNAL(timeout()), this, SLOT(tick())); + + TRACE_EXIT_0(); } MMF::MediaObject::~MediaObject() { + TRACE_CONTEXT(MediaObject::~MediaObject, EAudioApi); + TRACE_ENTRY_0(); + + delete m_tickTimer; delete m_player; + + TRACE_EXIT_0(); } +//----------------------------------------------------------------------------- +// MediaObjectInterface +//----------------------------------------------------------------------------- + void MMF::MediaObject::play() { - transitTo(PlayingState); - m_player->Play(); + TRACE_CONTEXT(MediaObject::play, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + switch(m_state) + { + case GroundState: + case LoadingState: + // Is this the correct error? Really we want 'NotReadyError' + m_error = NormalError; + changeState(ErrorState); + break; + + case StoppedState: + case PausedState: + m_player->Play(); + m_tickTimer->start(m_tickInterval); + 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::MediaObject::pause() { - transitTo(PausedState); - m_player->Pause(); + TRACE_CONTEXT(MediaObject::pause, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + switch(m_state) + { + case GroundState: + case LoadingState: + case StoppedState: + case PausedState: + case ErrorState: + // Do nothing + break; + + case PlayingState: + case BufferingState: + m_player->Pause(); + m_tickTimer->stop(); + 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::MediaObject::stop() { - transitTo(StoppedState); - m_player->Stop(); + TRACE_CONTEXT(MediaObject::stop, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + switch(m_state) + { + case GroundState: + case LoadingState: + case StoppedState: + case ErrorState: + // Do nothing + break; + + case PlayingState: + case BufferingState: + case PausedState: + m_player->Stop(); + m_tickTimer->stop(); + 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::MediaObject::seek(qint64 milliseconds) +void MMF::MediaObject::seek(qint64 ms) { - m_player->SetPosition(toMicroSeconds(milliseconds)); + TRACE_CONTEXT(MediaObject::seek, EAudioApi); + TRACE_ENTRY("state %d pos %Ld", m_state, ms); + + m_player->SetPosition(TTimeIntervalMicroSeconds(ms)); + + TRACE_EXIT_0(); } qint32 MMF::MediaObject::tickInterval() const { - return 0; + TRACE_CONTEXT(MediaObject::tickInterval, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + TRACE_RETURN("%d", m_tickInterval); } void MMF::MediaObject::setTickInterval(qint32 interval) { - Q_UNUSED(interval); + TRACE_CONTEXT(MediaObject::setTickInterval, EAudioApi); + TRACE_ENTRY("state %d m_interval %d interval %d", m_state, m_tickInterval, interval); + + m_tickInterval = interval; + m_tickTimer->setInterval(interval); + + TRACE_EXIT_0(); } bool MMF::MediaObject::hasVideo() const { - return false; + TRACE_CONTEXT(MediaObject::hasVideo, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + // TODO: re-factor this class so that audio playback and video playback are + // delegated to separate classes, which internally hoat a + // CMdaAudioPlayerUtility / CVideoPlayerUtility instance as appropriate. + + TRACE_RETURN("%d", false); } bool MMF::MediaObject::isSeekable() const { - return false; + TRACE_CONTEXT(MediaObject::isSeekable, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + TRACE_RETURN("%d", true); +} + +Phonon::State MMF::MediaObject::state() const +{ + TRACE_CONTEXT(MediaObject::state, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + const Phonon::State result = phononState(m_state); + + TRACE_RETURN("%d", result); } qint64 MMF::MediaObject::currentTime() const { - TTimeIntervalMicroSeconds mss; - const TInt retcode = m_player->GetPosition(mss); + TRACE_CONTEXT(MediaObject::currentTime, EAudioApi); + TRACE_ENTRY("state %d", m_state); - if(retcode == KErrNone) - return toMilliSeconds(m_player->Duration()); - else { - // TODO Should we enter/emit error state? Tricky - // since we're in a const function. - return -1; + TTimeIntervalMicroSeconds us; + const TInt err = m_player->GetPosition(us); + + qint64 result = -1; + + if(KErrNone == err) { + result = toMilliSeconds(us); } + + TRACE_RETURN("%Ld", result); } QString MMF::MediaObject::errorString() const { - return QString(); + TRACE_CONTEXT(MediaObject::errorString, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + TRACE_EXIT_0(); + // TODO: put in proper error strings + QString result; + return result; } Phonon::ErrorType MMF::MediaObject::errorType() const { - return m_error; -} + TRACE_CONTEXT(MediaObject::errorType, EAudioApi); + TRACE_ENTRY("state %d", m_state); -qint64 MMF::MediaObject::toMilliSeconds(const TTimeIntervalMicroSeconds &in) -{ - return in.Int64() / 1000; -} + const Phonon::ErrorType result = (ErrorState == m_state) + ? m_error : NoError; -TTimeIntervalMicroSeconds MMF::MediaObject::toMicroSeconds(qint64 ms) -{ - return TTimeIntervalMicroSeconds(TInt64(ms)); + TRACE_RETURN("%d", result); } qint64 MMF::MediaObject::totalTime() const { - return toMilliSeconds(m_player->Duration()); + TRACE_CONTEXT(MediaObject::totalTime, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + const qint64 result = toMilliSeconds(m_player->Duration()); + + TRACE_RETURN("%Ld", result); } MediaSource MMF::MediaObject::source() const { + TRACE_CONTEXT(MediaObject::source, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + TRACE_EXIT_0(); return m_mediaSource; } void MMF::MediaObject::setSource(const MediaSource &source) { - stop(); + TRACE_CONTEXT(MediaObject::setSource, EAudioApi); + TRACE_ENTRY("state %d source.type %d", m_state, source.type()); + + m_player->Close(); + changeState(GroundState); + + // TODO: is it correct to assign even if the media type is not supported in + // the switch statement below? m_mediaSource = source; + TInt symbianErr = KErrNone; + switch(m_mediaSource.type()) { case MediaSource::LocalFile: { - const QHBufC filename(source.fileName()); - m_player->OpenFileL(*filename); + // 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_mediaSource.fileName()); + TRAP(symbianErr, m_player->OpenFileL(*filename)); break; } + case MediaSource::Url: { - const QHBufC filename(source.url().toString()); - m_player->OpenUrlL(*filename); + const QHBufC filename(m_mediaSource.url().toString()); + TRAP(symbianErr, m_player->OpenUrlL(*filename)); break; } + case MediaSource::Invalid: - /* Fallthrough. */ case MediaSource::Disc: - /* Fallthrough. */ case MediaSource::Stream: - /* Fallthrough. */ + symbianErr = KErrNotSupported; + break; + case MediaSource::Empty: - { - transitTo(ErrorState); + TRACE_EXIT_0(); return; - } + + // Protection against adding new media types and forgetting to update this switch + default: + TRACE_PANIC(InvalidMediaTypePanic); + } + + if(KErrNone == symbianErr) + { +#ifdef QT_PHONON_MMF_AUDIO_DRM + // There appears to be a bug in the CDrmPlayerUtility implementation (at least + // in S60 5.x) whereby the player does not check whether the loading observer + // pointer is null before dereferencing it. Therefore we must register for + // loading notification, even though we do nothing in the callback functions. + m_player->RegisterForAudioLoadingNotification(*this); +#endif + changeState(LoadingState); + } + else + { + // TODO: do something with the value of symbianErr? + m_error = NormalError; + changeState(ErrorState); } - transitTo(LoadingState); + TRACE_EXIT_0(); } void MMF::MediaObject::setNextSource(const MediaSource &source) { + TRACE_CONTEXT(MediaObject::setNextSource, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + // TODO: handle 'next source' + m_nextSource = source; Q_UNUSED(source); + + TRACE_EXIT_0(); } qint32 MMF::MediaObject::prefinishMark() const { - return 0; + TRACE_CONTEXT(MediaObject::prefinishMark, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + // TODO: implement prefinish mark + const qint32 result = 0; + TRACE_RETURN("%d", result); } -void MMF::MediaObject::setPrefinishMark(qint32) +void MMF::MediaObject::setPrefinishMark(qint32 mark) { + TRACE_CONTEXT(MediaObject::setPrefinishMark, EAudioApi); + TRACE_ENTRY("state %d mark %d", m_state, mark); + Q_UNUSED(mark); // to silence warnings in release builds + + // TODO: implement prefinish mark + + TRACE_EXIT_0(); } qint32 MMF::MediaObject::transitionTime() const { - return 0; + TRACE_CONTEXT(MediaObject::transitionTime, EAudioApi); + TRACE_ENTRY("state %d", m_state); + + // TODO: implement transition time + const qint32 result = 0; + TRACE_RETURN("%d", result); } -void MMF::MediaObject::setTransitionTime(qint32) +void MMF::MediaObject::setTransitionTime(qint32 time) { + TRACE_CONTEXT(MediaObject::setTransitionTime, EAudioApi); + TRACE_ENTRY("state %d time %d", m_state, time); + Q_UNUSED(time); // to silence warnings in release builds + + // TODO: implement transition time + + TRACE_EXIT_0(); } + +//----------------------------------------------------------------------------- +// Symbian multimedia client observer callbacks +//----------------------------------------------------------------------------- + +#ifdef QT_PHONON_MMF_AUDIO_DRM void MMF::MediaObject::MdapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds &) +#else +void MMF::MediaObject::MapcInitComplete(TInt aError, + const TTimeIntervalMicroSeconds &) +#endif { - if(aError == KErrNone) { + TRACE_CONTEXT(MediaObject::MapcInitComplete, EAudioInternal); + TRACE_ENTRY("state %d error %d", m_state, aError); + + __ASSERT_ALWAYS(LoadingState == m_state, Utils::panic(InvalidStatePanic)); + + if(KErrNone == aError) + { + TInt volume = 0; + aError = m_player->GetVolume(volume); + if(KErrNone == aError) + { + m_maxVolume = m_player->MaxVolume(); + m_volume = static_cast<qreal>(volume) / m_maxVolume; + + if(m_audioOutput) + { + // Trigger AudioOutput signal + m_audioOutput->triggerVolumeChanged(m_volume); + } + + emit totalTimeChanged(); + changeState(StoppedState); + } + } + else + { + // TODO: set different error states according to value of aError? m_error = NormalError; - m_state = ErrorState; + changeState(ErrorState); } - emit totalTimeChanged(); - transitTo(StoppedState); + TRACE_EXIT_0(); } +#ifdef QT_PHONON_MMF_AUDIO_DRM void MMF::MediaObject::MdapcPlayComplete(TInt aError) +#else +void MMF::MediaObject::MapcPlayComplete(TInt aError) +#endif { + TRACE_CONTEXT(MediaObject::MapcPlayComplete, EAudioInternal); + TRACE_ENTRY("state %d error %d", m_state, aError); + + m_tickTimer->stop(); + + if(KErrNone == aError) + { + changeState(StoppedState); + // TODO: move on to m_nextSource + } + else + { + // TODO: do something with aError? + m_error = NormalError; + changeState(ErrorState); + } + +/* if(aError == KErrNone) { if(m_nextSource.type() == MediaSource::Empty) { emit finished(); @@ -207,42 +492,140 @@ void MMF::MediaObject::MdapcPlayComplete(TInt aError) m_nextSource = MediaSource(); } - transitTo(StoppedState); + changeState(StoppedState); } else { m_error = NormalError; - transitTo(ErrorState); + changeState(ErrorState); } +*/ + + TRACE_EXIT_0(); } -void MMF::MediaObject::transitTo(Phonon::State newState) +#ifdef QT_PHONON_MMF_AUDIO_DRM +void MMF::MediaObject::MaloLoadingStarted() { - emit stateChanged(m_state, newState); - m_state = newState; + } -Phonon::State MMF::MediaObject::state() const +void MMF::MediaObject::MaloLoadingComplete() { - return m_state; + } +#endif // QT_PHONON_MMF_AUDIO_DRM -void MMF::MediaObject::MaloLoadingComplete() + +//----------------------------------------------------------------------------- +// Volume +//----------------------------------------------------------------------------- + +qreal MMF::MediaObject::volume() const { - transitTo(StoppedState); + return m_volume; } -void MMF::MediaObject::MaloLoadingStarted() +bool MMF::MediaObject::setVolume(qreal volume) { - transitTo(LoadingState); + TRACE_CONTEXT(MediaObject::setVolume, EAudioInternal); + TRACE_ENTRY("state %d", m_state); + + bool volumeChanged = false; + + switch(m_state) + { + case GroundState: + case LoadingState: + case ErrorState: + // Do nothing + break; + + case StoppedState: + case PausedState: + case PlayingState: + case BufferingState: + { + if(volume != m_volume) + { + const int err = m_player->SetVolume(volume * m_maxVolume); + if(KErrNone == err) + { + m_volume = volume; + volumeChanged = true; + } + else + { + m_error = NormalError; + changeState(ErrorState); + } + } + break; + } + + // Protection against adding new states and forgetting to update this + // switch + default: + TRACE_PANIC(InvalidStatePanic); + } + + TRACE_RETURN("%d", volumeChanged); } -void MMF::MediaObject::MvloLoadingComplete() +void MMF::MediaObject::setAudioOutput(AudioOutput* audioOutput) { - transitTo(StoppedState); + m_audioOutput = audioOutput; } -void MMF::MediaObject::MvloLoadingStarted() + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +qint64 MMF::MediaObject::toMilliSeconds(const TTimeIntervalMicroSeconds &in) { - transitTo(LoadingState); + return in.Int64() / 1000; } +Phonon::State MMF::MediaObject::phononState(PrivateState state) +{ + const Phonon::State phononState = + GroundState == state + ? Phonon::StoppedState + : static_cast<Phonon::State>(state); + + return phononState; +} + +void MMF::MediaObject::changeState(PrivateState newState) +{ + TRACE_CONTEXT(MediaObject::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 currentPhononState = phononState(m_state); + const Phonon::State newPhononState = phononState(newState); + if(currentPhononState != newPhononState) + { + TRACE("emit stateChanged(%d, %d)", newPhononState, currentPhononState); + emit stateChanged(newPhononState, currentPhononState); + } + + m_state = newState; + + TRACE_EXIT_0(); +} + +void MMF::MediaObject::tick() +{ + TRACE_CONTEXT(MediaObject::tick, EAudioInternal); + TRACE_ENTRY("state %d", m_state); + + emit tick(currentTime()); + + TRACE_EXIT_0(); +} + + + + |