/* 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 "audiooutput.h" #include "audioplayer.h" #include "defs.h" #include "dummyplayer.h" #include "utils.h" #include "utils.h" #ifdef PHONON_MMF_VIDEO_SURFACES #include "videoplayer_surface.h" #else #include "videoplayer_dsa.h" #endif #include "videowidget.h" #include "mediaobject.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE using namespace Phonon; using namespace Phonon::MMF; /*! \class MMF::MediaObject \internal */ //----------------------------------------------------------------------------- // Constructor / destructor //----------------------------------------------------------------------------- MMF::MediaObject::MediaObject(QObject *parent) : MMF::MediaNode::MediaNode(parent) , m_recognizerOpened(false) , m_nextSourceSet(false) , m_file(0) , m_resource(0) { m_player.reset(new DummyPlayer()); TRACE_CONTEXT(MediaObject::MediaObject, EAudioApi); TRACE_ENTRY_0(); const int err = m_fileServer.Connect(); QT_TRAP_THROWING(User::LeaveIfError(err)); parent->installEventFilter(this); m_iap = KUseDefaultIap; TRACE_EXIT_0(); } MMF::MediaObject::~MediaObject() { TRACE_CONTEXT(MediaObject::~MediaObject, EAudioApi); TRACE_ENTRY_0(); parent()->removeEventFilter(this); delete m_resource; if (m_file) m_file->Close(); delete m_file; m_fileServer.Close(); m_recognizer.Close(); TRACE_EXIT_0(); } //----------------------------------------------------------------------------- // Recognizer //----------------------------------------------------------------------------- bool MMF::MediaObject::openRecognizer() { TRACE_CONTEXT(MediaObject::openRecognizer, EAudioInternal); if (!m_recognizerOpened) { TInt err = m_recognizer.Connect(); if (KErrNone != err) { TRACE("RApaLsSession::Connect error %d", err); return false; } // This must be called in order to be able to share file handles with // the recognizer server (see fileMediaType function). err = m_fileServer.ShareProtected(); if (KErrNone != err) { TRACE("RFs::ShareProtected error %d", err); return false; } m_recognizerOpened = true; } return true; } MMF::MediaType MMF::MediaObject::fileMediaType (const QString& fileName) { TRACE_CONTEXT(MediaObject::fileMediaType, EAudioInternal); MediaType result = MediaTypeUnknown; if (openRecognizer()) { TInt err = openFileHandle(fileName); const QHBufC nativeFileName(QDir::toNativeSeparators(fileName)); if (KErrNone == err) { TDataRecognitionResult recognizerResult; err = m_recognizer.RecognizeData(*m_file, recognizerResult); if (KErrNone == err) { const TPtrC mimeType = recognizerResult.iDataType.Des(); result = Utils::mimeTypeToMediaType(mimeType); } else { TRACE("RApaLsSession::RecognizeData filename %S error %d", nativeFileName.data(), err); } } else { TRACE("RFile::Open filename %S error %d", nativeFileName.data(), err); } } return result; } int MMF::MediaObject::openFileHandle(const QString &fileName) { TRACE_CONTEXT(MediaObject::openFileHandle, EAudioInternal); const QHBufC nativeFileName(QDir::toNativeSeparators(fileName)); TRACE_ENTRY("filename %S", nativeFileName.data()); if (m_file) m_file->Close(); delete m_file; m_file = 0; m_file = new RFile; TInt err = m_file->Open(m_fileServer, *nativeFileName, EFileRead | EFileShareReadersOrWriters); return err; } MMF::MediaType MMF::MediaObject::bufferMediaType(const uchar *data, qint64 size) { TRACE_CONTEXT(MediaObject::bufferMediaType, EAudioInternal); MediaType result = MediaTypeUnknown; if (openRecognizer()) { TDataRecognitionResult recognizerResult; const TPtrC8 des(data, size); const TInt err = m_recognizer.RecognizeData(KNullDesC, des, recognizerResult); if (KErrNone == err) { const TPtrC mimeType = recognizerResult.iDataType.Des(); result = Utils::mimeTypeToMediaType(mimeType); } else { TRACE("RApaLsSession::RecognizeData error %d", err); } } return result; } //----------------------------------------------------------------------------- // MediaObjectInterface //----------------------------------------------------------------------------- void MMF::MediaObject::play() { m_player->play(); } void MMF::MediaObject::pause() { m_player->pause(); } void MMF::MediaObject::stop() { m_player->stop(); } void MMF::MediaObject::seek(qint64 ms) { m_player->seek(ms); if (state() == PausedState or state() == PlayingState) { emit tick(currentTime()); } } qint32 MMF::MediaObject::tickInterval() const { return m_player->tickInterval(); } void MMF::MediaObject::setTickInterval(qint32 interval) { m_player->setTickInterval(interval); } bool MMF::MediaObject::hasVideo() const { return m_player->hasVideo(); } bool MMF::MediaObject::isSeekable() const { return m_player->isSeekable(); } Phonon::State MMF::MediaObject::state() const { return m_player->state(); } qint64 MMF::MediaObject::currentTime() const { return m_player->currentTime(); } QString MMF::MediaObject::errorString() const { return m_player->errorString(); } Phonon::ErrorType MMF::MediaObject::errorType() const { return m_player->errorType(); } qint64 MMF::MediaObject::totalTime() const { return m_player->totalTime(); } MediaSource MMF::MediaObject::source() const { return m_source; } void MMF::MediaObject::setSource(const MediaSource &source) { switchToSource(source); } void MMF::MediaObject::switchToSource(const MediaSource &source) { if (m_file) m_file->Close(); delete m_file; m_file = 0; delete m_resource; m_resource = 0; createPlayer(source); m_source = source; m_player->open(); emit currentSourceChanged(m_source); } void MMF::MediaObject::createPlayer(const MediaSource &source) { TRACE_CONTEXT(MediaObject::createPlayer, EAudioApi); TRACE_ENTRY("state %d source.type %d", state(), source.type()); TRACE_ENTRY("source.type %d", source.type()); MediaType mediaType = MediaTypeUnknown; AbstractPlayer* oldPlayer = m_player.data(); const bool oldPlayerHasVideo = oldPlayer->hasVideo(); const bool oldPlayerSeekable = oldPlayer->isSeekable(); QString errorMessage; // Determine media type switch (source.type()) { case MediaSource::LocalFile: mediaType = fileMediaType(source.fileName()); break; case MediaSource::Url: { const QUrl url(source.url()); if (url.scheme() == QLatin1String("file")) { mediaType = fileMediaType(url.toLocalFile()); } else { // Streaming playback is generally not supported by the implementation // of the audio player API, so we use CVideoPlayerUtility for both // audio and video streaming. mediaType = MediaTypeVideo; } } break; case MediaSource::Invalid: case MediaSource::Disc: errorMessage = tr("Error opening source: type not supported"); break; case MediaSource::Stream: { const QString fileName = source.url().toLocalFile(); if (fileName.startsWith(QLatin1String(":/")) || fileName.startsWith(QLatin1String("qrc://"))) { Q_ASSERT(!m_resource); m_resource = new QResource(fileName); if (m_resource->isValid()) { if (m_resource->isCompressed()) errorMessage = tr("Error opening source: resource is compressed"); else mediaType = bufferMediaType(m_resource->data(), m_resource->size()); } else { errorMessage = tr("Error opening source: resource not valid"); } } else { errorMessage = tr("Error opening source: type not supported"); } } break; case MediaSource::Empty: TRACE_0("Empty media source"); break; } if (oldPlayer) oldPlayer->close(); AbstractPlayer* newPlayer = 0; // Construct newPlayer using oldPlayer (if not 0) in order to copy // parameters (volume, prefinishMark, transitionTime) which may have // been set on oldPlayer. switch (mediaType) { case MediaTypeUnknown: TRACE_0("Media type could not be determined"); newPlayer = new DummyPlayer(oldPlayer); errorMessage = tr("Error opening source: media type could not be determined"); break; case MediaTypeAudio: newPlayer = new AudioPlayer(this, oldPlayer); break; case MediaTypeVideo: #ifdef PHONON_MMF_VIDEO_SURFACES newPlayer = SurfaceVideoPlayer::create(this, oldPlayer); #else newPlayer = DsaVideoPlayer::create(this, oldPlayer); #endif break; } if (oldPlayer) emit abstractPlayerChanged(0); m_player.reset(newPlayer); emit abstractPlayerChanged(newPlayer); if (oldPlayerHasVideo != hasVideo()) { emit hasVideoChanged(hasVideo()); } if (oldPlayerSeekable != isSeekable()) { emit seekableChanged(isSeekable()); } connect(m_player.data(), SIGNAL(totalTimeChanged(qint64)), SIGNAL(totalTimeChanged(qint64))); 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(bufferStatus(int)), SIGNAL(bufferStatus(int))); connect(m_player.data(), SIGNAL(metaDataChanged(QMultiMap)), SIGNAL(metaDataChanged(QMultiMap))); connect(m_player.data(), SIGNAL(aboutToFinish()), SIGNAL(aboutToFinish())); connect(m_player.data(), SIGNAL(prefinishMarkReached(qint32)), SIGNAL(prefinishMarkReached(qint32))); connect(m_player.data(), SIGNAL(prefinishMarkReached(qint32)), SLOT(handlePrefinishMarkReached(qint32))); connect(m_player.data(), SIGNAL(tick(qint64)), SIGNAL(tick(qint64))); // We need to call setError() after doing the connects, otherwise the // error won't be received. if (!errorMessage.isEmpty()) { Q_ASSERT(m_player); m_player->setError(errorMessage); } TRACE_EXIT_0(); } void MMF::MediaObject::setNextSource(const MediaSource &source) { m_nextSource = source; m_nextSourceSet = true; } qint32 MMF::MediaObject::prefinishMark() const { return m_player->prefinishMark(); } void MMF::MediaObject::setPrefinishMark(qint32 mark) { m_player->setPrefinishMark(mark); } qint32 MMF::MediaObject::transitionTime() const { return m_player->transitionTime(); } void MMF::MediaObject::setTransitionTime(qint32 time) { m_player->setTransitionTime(time); } void MMF::MediaObject::volumeChanged(qreal volume) { m_player->volumeChanged(volume); } RFile* MMF::MediaObject::file() const { return m_file; } QResource* MMF::MediaObject::resource() const { return m_resource; } //----------------------------------------------------------------------------- // MediaNode //----------------------------------------------------------------------------- void MMF::MediaObject::connectMediaObject(MediaObject * /*mediaObject*/) { // This function should never be called - see MediaNode::setMediaObject() Q_ASSERT_X(false, Q_FUNC_INFO, "Connection of MediaObject to MediaObject"); } void MMF::MediaObject::disconnectMediaObject(MediaObject * /*mediaObject*/) { // This function should never be called - see MediaNode::setMediaObject() Q_ASSERT_X(false, Q_FUNC_INFO, "Disconnection of MediaObject from MediaObject"); } //----------------------------------------------------------------------------- // Video output //----------------------------------------------------------------------------- void MMF::MediaObject::setVideoOutput(AbstractVideoOutput* videoOutput) { m_player->setVideoOutput(videoOutput); } AbstractPlayer *MMF::MediaObject::abstractPlayer() const { return m_player.data(); } //----------------------------------------------------------------------------- // Playlist support //----------------------------------------------------------------------------- void MMF::MediaObject::switchToNextSource() { if (m_nextSourceSet) { m_nextSourceSet = false; switchToSource(m_nextSource); play(); } else { emit finished(); } } //----------------------------------------------------------------------------- // IAP support //----------------------------------------------------------------------------- int MMF::MediaObject::currentIAP() const { return m_iap; } bool MMF::MediaObject::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::DynamicPropertyChange ) { QDynamicPropertyChangeEvent* dynamicEvent = static_cast(event); if (dynamicEvent->propertyName() == "InternetAccessPointName") { QVariant value = watched->property("InternetAccessPointName"); if (value.isValid()) { QString iapName = value.toString(); TRAPD(err, setIAPIdFromNameL(iapName)); if (err) m_player->setError(tr("Failed to set requested IAP"), err); } } } return false; } void MMF::MediaObject::setIAPIdFromNameL(const QString& iapString) { TRACE_CONTEXT(MediaObject::getIapIdFromName, EVideoInternal); TBuf iapDes = qt_QString2TPtrC(iapString); CCommsDatabase *commsDb = CCommsDatabase::NewL(EDatabaseTypeIAP); CleanupStack::PushL(commsDb); commsDb->ShowHiddenRecords(); CCommsDbTableView *view = commsDb->OpenTableLC(TPtrC(IAP)); for (TInt l = view->GotoFirstRecord(); l != KErrNotFound; l = view->GotoNextRecord()) { TBuf iapName; view->ReadTextL(TPtrC(COMMDB_NAME), iapName); TRACE("found IAP %S", &iapName); if (iapName.CompareF(iapDes) == 0) { TUint32 uiap; view->ReadUintL(TPtrC(COMMDB_ID), uiap); TRACE("matched IAP %S, setting m_iap %d", &iapName, uiap); m_iap = uiap; break; } } CleanupStack::PopAndDestroy(2); // commsDb, view } //----------------------------------------------------------------------------- // Other private functions //----------------------------------------------------------------------------- void MMF::MediaObject::handlePrefinishMarkReached(qint32 time) { emit tick(time); } QT_END_NAMESPACE