diff options
author | Gareth Stockwell <gareth.stockwell@sosco.com> | 2009-08-18 08:12:47 (GMT) |
---|---|---|
committer | Gareth Stockwell <gareth.stockwell@sosco.com> | 2009-08-18 08:15:29 (GMT) |
commit | 008d464ea90a0a33f16bd6ce29563e7dd206b91f (patch) | |
tree | 7156f7124e8b520b1559e4cce1f0f2476cf075e6 /src/3rdparty/phonon/mmf/mediaobject.cpp | |
parent | 427c415d876d57042a55e8b66730888512ac0ed1 (diff) | |
parent | 6b7603a0a2fed844b1264d8deb0ce78b110858c4 (diff) | |
download | Qt-008d464ea90a0a33f16bd6ce29563e7dd206b91f.zip Qt-008d464ea90a0a33f16bd6ce29563e7dd206b91f.tar.gz Qt-008d464ea90a0a33f16bd6ce29563e7dd206b91f.tar.bz2 |
Merged recognizer changes with abstraction for audio / video playback.
Audio playback now working to the same extent as prior to the abstraction, with one regression: the initial volume level in the UI is set to zero, although playback is audible.
Some cleanup is required:
- Functionality common to AudioPlayer and VideoPlayer (e.g. tick timer, changeState function) should be moved into AbstractPlayer.
- Files may be opened by multiple instances of MediaObject at at time. For example, the musicplayer example app uses one instance to read file metadata, and one for the actual playback. In order to avoid KErrInUse errors from the file server, files must be opened with an EShare* flag and passed around by handle. At present this is done in a slightly hacky way (i.e. AbstractPlayer::setSource is renamed to setFileSource).
- The pointer held by MediaObject::m_player must be checked for nullness in many of the public API calls. This could be made cleaner by implementing a stub derivation of AbstractPlayer, which returns sensible default values. Note that, if functionality such as tick timer handling is going to be pushed upwards from AudioPlayer / VideoPlayer, we should add an intermediate class to the hierarchy so that the overhead of constructing DummyPlayer objects is minimised.
At present, media type (audio / video) is only recognised from file streams - this needs to be extended to include HTTP streaming aswell.
Diffstat (limited to 'src/3rdparty/phonon/mmf/mediaobject.cpp')
-rw-r--r-- | src/3rdparty/phonon/mmf/mediaobject.cpp | 697 |
1 files changed, 215 insertions, 482 deletions
diff --git a/src/3rdparty/phonon/mmf/mediaobject.cpp b/src/3rdparty/phonon/mmf/mediaobject.cpp index 6bf15e6..62dc903 100644 --- a/src/3rdparty/phonon/mmf/mediaobject.cpp +++ b/src/3rdparty/phonon/mmf/mediaobject.cpp @@ -16,37 +16,20 @@ along with this library. If not, see <http://www.gnu.org/licenses/>. */ -#include <QUrl> -#include <QTimer> -#include <private/qcore_symbian_p.h> - +#include "audioplayer.h" #include "mediaobject.h" -#include "audiooutput.h" #include "utils.h" +#include "videoplayer.h" + using namespace Phonon; using namespace Phonon::MMF; //----------------------------------------------------------------------------- -// 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(GroundState) - , m_tickInterval(DefaultTickInterval) - , m_tickTimer(NULL) - , m_volume(0.0) - , m_maxVolume(NullMaxVolume) +MMF::MediaObject::MediaObject(QObject *parent) : QObject::QObject(parent) { TRACE_CONTEXT(MediaObject::MediaObject, EAudioApi); TRACE_ENTRY_0(); @@ -56,9 +39,11 @@ MMF::MediaObject::MediaObject(QObject *parent) : m_player(NULL) TInt err = m_recognizer.Connect(); err = m_fileServer.Connect(); // TODO: handle this error - - // TODO: should leaves be trapped in the constructor? - m_player = CPlayerType::NewL(*this, 0, EMdaPriorityPreferenceNone); + + // This must be called in order to be able to share file handles with + // the recognizer server (see fileMediaType function). + err = m_fileServer.ShareProtected(); + // TODO: handle this error m_tickTimer = new QTimer(this); connect(m_tickTimer, SIGNAL(timeout()), this, SLOT(tick())); @@ -72,8 +57,8 @@ MMF::MediaObject::~MediaObject() TRACE_ENTRY_0(); delete m_tickTimer; - delete m_player; + m_file.Close(); m_fileServer.Close(); m_recognizer.Close(); @@ -111,21 +96,20 @@ MMF::MediaObject::MediaType MMF::MediaObject::fileMediaType { MediaType result = MediaTypeUnknown; - QHBufC fileNameSymbian = Utils::symbianFilename(m_mediaSource.fileName()); - RFile file; - TInt err = file.Open(m_fileServer, *fileNameSymbian, EFileRead); + QHBufC fileNameSymbian = Utils::symbianFilename(fileName); + + m_file.Close(); + TInt err = m_file.Open(m_fileServer, *fileNameSymbian, EFileRead|EFileShareReadersOnly); if(KErrNone == err) { TDataRecognitionResult recognizerResult; - err = m_recognizer.RecognizeData(file, recognizerResult); + err = m_recognizer.RecognizeData(m_file, recognizerResult); if(KErrNone == err) { const TPtrC mimeType = recognizerResult.iDataType.Des(); result = mimeTypeToMediaType(mimeType); } - - file.Close(); } return result; } @@ -137,442 +121,272 @@ MMF::MediaObject::MediaType MMF::MediaObject::fileMediaType void MMF::MediaObject::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); + if(!m_player.isNull()) + { + m_player->play(); + } } void MMF::MediaObject::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); + if(!m_player.isNull()) + { + m_player->pause(); + } } void MMF::MediaObject::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); + if(!m_player.isNull()) + { + m_player->stop(); + } } void MMF::MediaObject::seek(qint64 ms) { - TRACE_CONTEXT(MediaObject::seek, EAudioApi); - TRACE_ENTRY("state %d pos %Ld", m_state, ms); - - m_player->SetPosition(TTimeIntervalMicroSeconds(ms)); - - TRACE_EXIT_0(); + if(!m_player.isNull()) + { + m_player->seek(ms); + } } qint32 MMF::MediaObject::tickInterval() const { - TRACE_CONTEXT(MediaObject::tickInterval, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - TRACE_RETURN("%d", m_tickInterval); + qint32 result = 0; + if(!m_player.isNull()) + { + result = m_player->tickInterval(); + } + return result; } void MMF::MediaObject::setTickInterval(qint32 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(); + if(!m_player.isNull()) + { + m_player->setTickInterval(interval); + } } bool MMF::MediaObject::hasVideo() const { - 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 result = false; + if(!m_player.isNull()) + { + result = m_player->hasVideo(); + } + return result; } bool MMF::MediaObject::isSeekable() const { - TRACE_CONTEXT(MediaObject::isSeekable, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - TRACE_RETURN("%d", true); + bool result = false; + if(!m_player.isNull()) + { + result = m_player->isSeekable(); + } + return result; } 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); + Phonon::State result = Phonon::StoppedState; + if(!m_player.isNull()) + { + result = m_player->state(); + } + return result; } qint64 MMF::MediaObject::currentTime() const { - TRACE_CONTEXT(MediaObject::currentTime, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - TTimeIntervalMicroSeconds us; - const TInt err = m_player->GetPosition(us); - - qint64 result = -1; - - if(KErrNone == err) { - result = toMilliSeconds(us); - } - - TRACE_RETURN("%Ld", result); + qint64 result = 0; + if(!m_player.isNull()) + { + result = m_player->currentTime(); + } + return result; } QString MMF::MediaObject::errorString() const { - TRACE_CONTEXT(MediaObject::errorString, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - TRACE_EXIT_0(); - // TODO: put in proper error strings - QString result; - return result; + QString result; + if(!m_player.isNull()) + { + result = m_player->errorString(); + } + return result; } Phonon::ErrorType MMF::MediaObject::errorType() const { - TRACE_CONTEXT(MediaObject::errorType, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - const Phonon::ErrorType result = (ErrorState == m_state) - ? m_error : NoError; - - TRACE_RETURN("%d", result); + Phonon::ErrorType result = Phonon::NoError; + if(!m_player.isNull()) + { + result = m_player->errorType(); + } + return result; } qint64 MMF::MediaObject::totalTime() const { - TRACE_CONTEXT(MediaObject::totalTime, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - const qint64 result = toMilliSeconds(m_player->Duration()); - - TRACE_RETURN("%Ld", result); + qint64 result = 0; + if(!m_player.isNull()) + { + result = m_player->totalTime(); + } + return result; } MediaSource MMF::MediaObject::source() const { - TRACE_CONTEXT(MediaObject::source, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - TRACE_EXIT_0(); - return m_mediaSource; + MediaSource result; + if(!m_player.isNull()) + { + result = m_player->source(); + } + return result; } void MMF::MediaObject::setSource(const MediaSource &source) { - 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()) + loadPlayer(source); + if(!m_player.isNull()) { - 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_mediaSource.fileName()); - TRAP(symbianErr, m_player->OpenFileL(*filename)); - break; - } - - case MediaSource::Url: - { - const QHBufC filename(m_mediaSource.url().toString()); - TRAP(symbianErr, m_player->OpenUrlL(*filename)); - break; - } - - case MediaSource::Invalid: - case MediaSource::Disc: - case MediaSource::Stream: - symbianErr = KErrNotSupported; - break; - - case MediaSource::Empty: - TRACE_EXIT_0(); - return; - - // Protection against adding new media types and forgetting to update this switch - default: - TRACE_PANIC(InvalidMediaTypePanic); + //m_player->setSource(source); + + // This is a hack to work around KErrInUse from MMF client utility + // OpenFileL calls + m_player->setFileSource(source, m_file); } +} - 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 +void MMF::MediaObject::loadPlayer(const MediaSource &source) +{ + TRACE_CONTEXT(AudioPlayer::loadPlayer, EAudioApi); + //TRACE_ENTRY("state %d source.type %d", m_state, source.type()); + // TODO: log state + TRACE_ENTRY("source.type %d", source.type()); + + // Destroy old player object + if(!m_player.isNull()) + { + disconnect(m_player.data(), 0, this, 0); + m_player.reset(NULL); + } + + MediaType mediaType = MediaTypeUnknown; + + // Determine media type + switch(source.type()) + { + case MediaSource::LocalFile: + mediaType = fileMediaType(source.fileName()); + break; + + case MediaSource::Url: + // TODO: support detection of media type from HTTP streams + TRACE_0("Network streaming not supported yet"); + + /* + * TODO: handle error + * + m_error = NormalError; + changeState(ErrorState); + break; + */ + + case MediaSource::Invalid: + case MediaSource::Disc: + case MediaSource::Stream: + TRACE_0("Unsupported media type"); + /* + * TODO: handle error + * + m_error = NormalError; + changeState(ErrorState); + */ + break; + + case MediaSource::Empty: + TRACE_EXIT_0(); + return; + } + + switch(mediaType) + { + case MediaTypeUnknown: + TRACE_0("Media type could not be determined"); + /* + * TODO: handle error + * + m_error = NormalError; + changeState(ErrorState); + */ + break; + + case MediaTypeAudio: + m_player.reset(new AudioPlayer()); + break; + + case MediaTypeVideo: + m_player.reset(new VideoPlayer()); + break; + } + + if(!m_player.isNull()) { - // TODO: do something with the value of symbianErr? - m_error = NormalError; - changeState(ErrorState); + connect(m_player.data(), SIGNAL(totalTimeChanged()), SIGNAL(totalTimeChanged())); + connect(m_player.data(), SIGNAL(stateChanged(Phonon::State, Phonon::State)), SIGNAL(stateChanged(Phonon::State, Phonon::State))); + connect(m_player.data(), SIGNAL(finished()), SIGNAL(finished())); + connect(m_player.data(), SIGNAL(tick(qint64)), SIGNAL(tick(qint64))); } - - 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(); + if(!m_player.isNull()) + { + m_player->setNextSource(source); + } } qint32 MMF::MediaObject::prefinishMark() const { - TRACE_CONTEXT(MediaObject::prefinishMark, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - // TODO: implement prefinish mark - const qint32 result = 0; - TRACE_RETURN("%d", result); + qint32 result = 0; + if(!m_player.isNull()) + { + result = m_player->prefinishMark(); + } + return result; } 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(); + if(!m_player.isNull()) + { + m_player->setPrefinishMark(mark); + } } qint32 MMF::MediaObject::transitionTime() const { - TRACE_CONTEXT(MediaObject::transitionTime, EAudioApi); - TRACE_ENTRY("state %d", m_state); - - // TODO: implement transition time - const qint32 result = 0; - TRACE_RETURN("%d", result); + qint32 result = 0; + if(!m_player.isNull()) + { + result = m_player->transitionTime(); + } + return result; } 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 -{ - 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; - changeState(ErrorState); - } - - 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(); - } else { - setSource(m_nextSource); - m_nextSource = MediaSource(); - } - - changeState(StoppedState); - } - else { - m_error = NormalError; - changeState(ErrorState); - } -*/ - - TRACE_EXIT_0(); -} - -#ifdef QT_PHONON_MMF_AUDIO_DRM -void MMF::MediaObject::MaloLoadingStarted() -{ - -} - -void MMF::MediaObject::MaloLoadingComplete() -{ - + if(!m_player.isNull()) + { + m_player->setTransitionTime(time); + } } -#endif // QT_PHONON_MMF_AUDIO_DRM - //----------------------------------------------------------------------------- // Volume @@ -580,110 +394,29 @@ void MMF::MediaObject::MaloLoadingComplete() qreal MMF::MediaObject::volume() const { - return m_volume; + qreal result = 0.0; + if(!m_player.isNull()) + { + m_player->volume(); + } + return result; } bool MMF::MediaObject::setVolume(qreal volume) { - 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); + bool result = false; + if(!m_player.isNull()) + { + result = m_player->setVolume(volume); + } + return result; } void MMF::MediaObject::setAudioOutput(AudioOutput* audioOutput) { - m_audioOutput = audioOutput; -} - - -//----------------------------------------------------------------------------- -// Private functions -//----------------------------------------------------------------------------- - -qint64 MMF::MediaObject::toMilliSeconds(const TTimeIntervalMicroSeconds &in) -{ - 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(); + if(!m_player.isNull()) + { + m_player->setAudioOutput(audioOutput); + } } - - - |