From 2c034beab5a8d72a148c47e85e020dc64c58068f Mon Sep 17 00:00:00 2001 From: Lev Zelenskiy Date: Fri, 9 Mar 2012 09:53:21 +1000 Subject: QMediaPlayer frontend: changes to playlist support. Allow to load playlists using setMedia(). Use QNetworkMediaPlaylistProvider for playlist parsing. Updated unit tests. Change-Id: If4dba07be8b2e8a9e9549d5bed58e552dfb958b7 Reviewed-by: Dmytro Poplavskiy --- src/multimedia/playback/playback.pri | 10 +- .../playback/qlocalmediaplaylistprovider.cpp | 194 ---------- .../playback/qlocalmediaplaylistprovider_p.h | 98 ----- src/multimedia/playback/qmediaplayer.cpp | 403 ++++++++++++++++----- src/multimedia/playback/qmediaplayer.h | 8 +- src/multimedia/playback/qmediaplaylist.cpp | 6 +- src/multimedia/playback/qmediaplaylist_p.h | 16 +- 7 files changed, 346 insertions(+), 389 deletions(-) delete mode 100644 src/multimedia/playback/qlocalmediaplaylistprovider.cpp delete mode 100644 src/multimedia/playback/qlocalmediaplaylistprovider_p.h (limited to 'src') diff --git a/src/multimedia/playback/playback.pri b/src/multimedia/playback/playback.pri index 284c9f8e6..327b63b32 100644 --- a/src/multimedia/playback/playback.pri +++ b/src/multimedia/playback/playback.pri @@ -11,16 +11,18 @@ PRIVATE_HEADERS += \ playback/qmediaplaylist_p.h \ playback/qmediaplaylistprovider_p.h \ playback/qmediaplaylistioplugin_p.h \ - playback/qlocalmediaplaylistprovider_p.h \ - playback/qmediaplaylistnavigator_p.h + playback/qmediaplaylistnavigator_p.h \ + playback/qmedianetworkplaylistprovider_p.h \ + playback/playlistfileparser_p.h SOURCES += \ playback/qaudioendpointselector.cpp \ - playback/qlocalmediaplaylistprovider.cpp \ + playback/qmedianetworkplaylistprovider.cpp \ playback/qmediacontent.cpp \ playback/qmediaplayer.cpp \ playback/qmediaplaylist.cpp \ playback/qmediaplaylistioplugin.cpp \ playback/qmediaplaylistnavigator.cpp \ playback/qmediaplaylistprovider.cpp \ - playback/qmediaresource.cpp + playback/qmediaresource.cpp \ + playback/playlistfileparser.cpp diff --git a/src/multimedia/playback/qlocalmediaplaylistprovider.cpp b/src/multimedia/playback/qlocalmediaplaylistprovider.cpp deleted file mode 100644 index 051f76a9b..000000000 --- a/src/multimedia/playback/qlocalmediaplaylistprovider.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qlocalmediaplaylistprovider_p.h" -#include "qmediaplaylistprovider_p.h" -#include "qmediacontent.h" - -QT_BEGIN_NAMESPACE - -class QLocalMediaPlaylistProviderPrivate: public QMediaPlaylistProviderPrivate -{ -public: - QList resources; -}; - -QLocalMediaPlaylistProvider::QLocalMediaPlaylistProvider(QObject *parent) - :QMediaPlaylistProvider(*new QLocalMediaPlaylistProviderPrivate, parent) -{ -} - -QLocalMediaPlaylistProvider::~QLocalMediaPlaylistProvider() -{ -} - -bool QLocalMediaPlaylistProvider::isReadOnly() const -{ - return false; -} - -int QLocalMediaPlaylistProvider::mediaCount() const -{ - return d_func()->resources.size(); -} - -QMediaContent QLocalMediaPlaylistProvider::media(int pos) const -{ - return d_func()->resources.value(pos); -} - -bool QLocalMediaPlaylistProvider::addMedia(const QMediaContent &content) -{ - Q_D(QLocalMediaPlaylistProvider); - - int pos = d->resources.count(); - - emit mediaAboutToBeInserted(pos, pos); - d->resources.append(content); - emit mediaInserted(pos, pos); - - return true; -} - -bool QLocalMediaPlaylistProvider::addMedia(const QList &items) -{ - Q_D(QLocalMediaPlaylistProvider); - - if (items.isEmpty()) - return true; - - int pos = d->resources.count(); - int end = pos+items.count()-1; - - emit mediaAboutToBeInserted(pos, end); - d->resources.append(items); - emit mediaInserted(pos, end); - - return true; -} - - -bool QLocalMediaPlaylistProvider::insertMedia(int pos, const QMediaContent &content) -{ - Q_D(QLocalMediaPlaylistProvider); - - emit mediaAboutToBeInserted(pos, pos); - d->resources.insert(pos, content); - emit mediaInserted(pos,pos); - - return true; -} - -bool QLocalMediaPlaylistProvider::insertMedia(int pos, const QList &items) -{ - Q_D(QLocalMediaPlaylistProvider); - - if (items.isEmpty()) - return true; - - const int last = pos+items.count()-1; - - emit mediaAboutToBeInserted(pos, last); - for (int i=0; iresources.insert(pos+i, items.at(i)); - emit mediaInserted(pos, last); - - return true; -} - -bool QLocalMediaPlaylistProvider::removeMedia(int fromPos, int toPos) -{ - Q_D(QLocalMediaPlaylistProvider); - - Q_ASSERT(fromPos >= 0); - Q_ASSERT(fromPos <= toPos); - Q_ASSERT(toPos < mediaCount()); - - emit mediaAboutToBeRemoved(fromPos, toPos); - d->resources.erase(d->resources.begin()+fromPos, d->resources.begin()+toPos+1); - emit mediaRemoved(fromPos, toPos); - - return true; -} - -bool QLocalMediaPlaylistProvider::removeMedia(int pos) -{ - Q_D(QLocalMediaPlaylistProvider); - - emit mediaAboutToBeRemoved(pos, pos); - d->resources.removeAt(pos); - emit mediaRemoved(pos, pos); - - return true; -} - -bool QLocalMediaPlaylistProvider::clear() -{ - Q_D(QLocalMediaPlaylistProvider); - if (!d->resources.isEmpty()) { - int lastPos = mediaCount()-1; - emit mediaAboutToBeRemoved(0, lastPos); - d->resources.clear(); - emit mediaRemoved(0, lastPos); - } - - return true; -} - -void QLocalMediaPlaylistProvider::shuffle() -{ - Q_D(QLocalMediaPlaylistProvider); - if (!d->resources.isEmpty()) { - QList resources; - - while (!d->resources.isEmpty()) { - resources.append(d->resources.takeAt(qrand() % d->resources.size())); - } - - d->resources = resources; - emit mediaChanged(0, mediaCount()-1); - } - -} - -#include "moc_qlocalmediaplaylistprovider_p.cpp" -QT_END_NAMESPACE - diff --git a/src/multimedia/playback/qlocalmediaplaylistprovider_p.h b/src/multimedia/playback/qlocalmediaplaylistprovider_p.h deleted file mode 100644 index 879e8f715..000000000 --- a/src/multimedia/playback/qlocalmediaplaylistprovider_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QLOCALMEDIAPAYLISTPROVIDER_P_H -#define QLOCALMEDIAPAYLISTPROVIDER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qmediaplaylistprovider_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Multimedia) - - -class QLocalMediaPlaylistProviderPrivate; -class Q_MULTIMEDIA_EXPORT QLocalMediaPlaylistProvider : public QMediaPlaylistProvider -{ - Q_OBJECT -public: - QLocalMediaPlaylistProvider(QObject *parent=0); - virtual ~QLocalMediaPlaylistProvider(); - - virtual int mediaCount() const; - virtual QMediaContent media(int pos) const; - - virtual bool isReadOnly() const; - - virtual bool addMedia(const QMediaContent &content); - virtual bool addMedia(const QList &items); - virtual bool insertMedia(int pos, const QMediaContent &content); - virtual bool insertMedia(int pos, const QList &items); - virtual bool removeMedia(int pos); - virtual bool removeMedia(int start, int end); - virtual bool clear(); - -public Q_SLOTS: - virtual void shuffle(); - -private: - Q_DECLARE_PRIVATE(QLocalMediaPlaylistProvider) -}; - -QT_END_NAMESPACE - -QT_END_HEADER - - -#endif // QLOCALMEDIAPAYLISTSOURCE_P_H diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index 0ff341438..a699e281f 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -49,7 +49,6 @@ #include #include #include - #include #include @@ -97,6 +96,8 @@ public: } _registerPlayerMetaTypes; } +#define MAX_NESTED_PLAYLISTS 16 + class QMediaPlayerPrivate : public QMediaObjectPrivate { Q_DECLARE_NON_CONST_PUBLIC(QMediaPlayer) @@ -105,47 +106,86 @@ public: QMediaPlayerPrivate() : provider(0) , control(0) - , playlistSourceControl(0) , state(QMediaPlayer::StoppedState) , error(QMediaPlayer::NoError) - , filterStates(false) , playlist(0) , networkAccessControl(0) + , nestedPlaylists(0) {} QMediaServiceProvider *provider; QMediaPlayerControl* control; - QMediaPlaylistSourceControl* playlistSourceControl; QMediaPlayer::State state; QMediaPlayer::Error error; QString errorString; - bool filterStates; QPointer videoOutput; QMediaPlaylist *playlist; QMediaNetworkAccessControl *networkAccessControl; QVideoSurfaceOutput surfaceOutput; + QMediaContent rootMedia; + QMediaContent pendingPlaylist; + QMediaPlaylist *parentPlaylist(QMediaPlaylist *pls); + bool isInChain(QUrl url); + int nestedPlaylists; + + void setPlaylist(QMediaPlaylist *playlist); + void setPlaylistMedia(); + void loadPlaylist(); + void disconnectPlaylist(); + void connectPlaylist(); + void _q_stateChanged(QMediaPlayer::State state); void _q_mediaStatusChanged(QMediaPlayer::MediaStatus status); void _q_error(int error, const QString &errorString); void _q_updateMedia(const QMediaContent&); void _q_playlistDestroyed(); + void _q_handlePlaylistLoaded(); + void _q_handlePlaylistLoadFailed(); }; +QMediaPlaylist *QMediaPlayerPrivate::parentPlaylist(QMediaPlaylist *pls) +{ + // This function finds a parent playlist for an item in the active chain of playlists. + // Every item in the chain comes from currentMedia() of its parent. + // We don't need to travers the whole tree of playlists, + // but only the subtree of active ones. + for (QMediaPlaylist *current = rootMedia.playlist(); current && current != pls; current = current->currentMedia().playlist()) + if (current->currentMedia().playlist() == pls) + return current; + return 0; +} + +bool QMediaPlayerPrivate::isInChain(QUrl url) +{ + // Check whether a URL is already in the chain of playlists. + // Also see a comment in parentPlaylist(). + for (QMediaPlaylist *current = rootMedia.playlist(); current && current != playlist; current = current->currentMedia().playlist()) + if (current->currentMedia().canonicalUrl() == url) { + return true; + } + return false; +} + void QMediaPlayerPrivate::_q_stateChanged(QMediaPlayer::State ps) { Q_Q(QMediaPlayer); - if (filterStates) - return; - - if (playlist - && ps != state && ps == QMediaPlayer::StoppedState - && (control->mediaStatus() == QMediaPlayer::EndOfMedia || - control->mediaStatus() == QMediaPlayer::InvalidMedia)) { - playlist->next(); - ps = control->state(); + // Backend switches into stopped state every time new media is about to be loaded. + // If media player has a playlist loaded make sure player doesn' stop. + if (playlist && playlist->currentIndex() != -1 && ps != state && ps == QMediaPlayer::StoppedState) { + if (control->mediaStatus() == QMediaPlayer::EndOfMedia || + control->mediaStatus() == QMediaPlayer::InvalidMedia) { + // if media player is not stopped, and + // we have finished playback for the current media, + // advance to the next item in the playlist + Q_ASSERT(state != QMediaPlayer::StoppedState); + playlist->next(); + return; + } else if (control->mediaStatus() == QMediaPlayer::LoadingMedia) { + return; + } } if (ps != state) { @@ -182,10 +222,16 @@ void QMediaPlayerPrivate::_q_error(int error, const QString &errorString) { Q_Q(QMediaPlayer); - this->error = QMediaPlayer::Error(error); - this->errorString = errorString; + if (error == int(QMediaPlayer::MediaIsPlaylist)) { + loadPlaylist(); + } else { + this->error = QMediaPlayer::Error(error); + this->errorString = errorString; + emit q->error(this->error); - emit q->error(this->error); + if (playlist) + playlist->next(); + } } void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) @@ -195,9 +241,45 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) if (!control) return; + // check if the current playlist is a top-level playlist + Q_ASSERT(playlist); + if (media.isNull() && playlist != rootMedia.playlist()) { + // switch back to parent playlist + QMediaPlaylist *pls = parentPlaylist(playlist); + Q_ASSERT(pls); + disconnectPlaylist(); + playlist = pls; + connectPlaylist(); + + Q_ASSERT(!pendingPlaylist.playlist()); + nestedPlaylists--; + Q_ASSERT(nestedPlaylists >= 0); + + playlist->next(); + return; + } + + if (media.playlist()) { + if (nestedPlaylists < MAX_NESTED_PLAYLISTS) { + nestedPlaylists++; + Q_ASSERT(!pendingPlaylist.playlist()); + + // disconnect current playlist + disconnectPlaylist(); + // new playlist signals are connected + // in the call to setPlaylist() in _q_handlePlaylistLoaded() + playlist = media.playlist(); + emit q->currentMediaChanged(media); + _q_handlePlaylistLoaded(); + return; + } else if (playlist) { + playlist->next(); + } + return; + } + const QMediaPlayer::State currentState = state; - filterStates = true; control->setMedia(media, 0); if (!media.isNull()) { @@ -212,18 +294,8 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) break; } } - filterStates = false; - state = control->state(); - - if (state != currentState) { - if (state == QMediaPlayer::PlayingState) - q->addPropertyWatch("position"); - else - q->removePropertyWatch("position"); - - emit q->stateChanged(state); - } + _q_stateChanged(control->state()); } void QMediaPlayerPrivate::_q_playlistDestroyed() @@ -233,12 +305,151 @@ void QMediaPlayerPrivate::_q_playlistDestroyed() if (!control) return; - if (playlistSourceControl) - playlistSourceControl->setPlaylist(0); - control->setMedia(QMediaContent(), 0); } +void QMediaPlayerPrivate::setPlaylist(QMediaPlaylist *pls) +{ + disconnectPlaylist(); + playlist = pls; + + setPlaylistMedia(); +} + +void QMediaPlayerPrivate::setPlaylistMedia() +{ + // This function loads current playlist media into backend. + // If current media is a playlist, the function recursively + // loads media from the playlist. + // It also makes sure the correct playlist signals are connected. + Q_Q(QMediaPlayer); + + if (playlist) { + connectPlaylist(); + if (playlist->currentMedia().playlist()) { + if (nestedPlaylists < MAX_NESTED_PLAYLISTS) { + emit q->currentMediaChanged(playlist->currentMedia()); + // rewind nested playlist to start + playlist->currentMedia().playlist()->setCurrentIndex(0); + nestedPlaylists++; + setPlaylist(playlist->currentMedia().playlist()); + } else { + playlist->next(); + } + return; + } else if (control != 0) { + // If we've just switched to a new playlist, + // then last emited currentMediaChanged was a playlist. + // Make sure we emit currentMediaChanged if new playlist has + // the same media as the previous one: + // sample.m3u + // test.wav -- processed by backend + // nested.m3u -- processed by frontend + // test.wav -- processed by backend, + // media is not changed, + // frontend needs to emit currentMediaChanged + bool isSameMedia = (control->media() == playlist->currentMedia()); + control->setMedia(playlist->currentMedia(), 0); + if (isSameMedia) { + emit q->currentMediaChanged(control->media()); + } + } + } else { + q->setMedia(QMediaContent(), 0); + } +} + +void QMediaPlayerPrivate::loadPlaylist() +{ + Q_Q(QMediaPlayer); + Q_ASSERT(pendingPlaylist.isNull()); + + // Do not load a playlist if there are more than MAX_NESTED_PLAYLISTS in the chain already, + // or if the playlist URL is already in the chain, i.e. do not allow recursive playlists and loops. + if (nestedPlaylists < MAX_NESTED_PLAYLISTS && !q->currentMedia().canonicalUrl().isEmpty() && !isInChain(q->currentMedia().canonicalUrl())) { + pendingPlaylist = QMediaContent(new QMediaPlaylist, q->currentMedia().canonicalUrl(), true); + QObject::connect(pendingPlaylist.playlist(), SIGNAL(loaded()), q, SLOT(_q_handlePlaylistLoaded())); + QObject::connect(pendingPlaylist.playlist(), SIGNAL(loadFailed()), q, SLOT(_q_handlePlaylistLoadFailed())); + pendingPlaylist.playlist()->load(pendingPlaylist.canonicalUrl()); + } else if (playlist) { + playlist->next(); + } +} + +void QMediaPlayerPrivate::disconnectPlaylist() +{ + Q_Q(QMediaPlayer); + if (playlist) { + QObject::disconnect(playlist, SIGNAL(currentMediaChanged(QMediaContent)), + q, SLOT(_q_updateMedia(QMediaContent))); + QObject::disconnect(playlist, SIGNAL(destroyed()), q, SLOT(_q_playlistDestroyed())); + } +} + +void QMediaPlayerPrivate::connectPlaylist() +{ + Q_Q(QMediaPlayer); + if (playlist) { + QObject::connect(playlist, SIGNAL(currentMediaChanged(QMediaContent)), + q, SLOT(_q_updateMedia(QMediaContent))); + QObject::connect(playlist, SIGNAL(destroyed()), q, SLOT(_q_playlistDestroyed())); + } +} + +void QMediaPlayerPrivate::_q_handlePlaylistLoaded() +{ + Q_Q(QMediaPlayer); + + QMediaPlaylist *oldPlaylist = 0; + if (pendingPlaylist.playlist()) { + Q_ASSERT(!q->currentMedia().playlist()); + // if there is an active playlist + if (playlist) { + Q_ASSERT(playlist->currentIndex() >= 0); + oldPlaylist = playlist; + disconnectPlaylist(); + playlist->insertMedia(playlist->currentIndex() + 1, pendingPlaylist); + playlist->removeMedia(playlist->currentIndex()); + nestedPlaylists++; + } else { + Q_ASSERT(!rootMedia.playlist()); + rootMedia = pendingPlaylist; + emit q->mediaChanged(rootMedia); + } + + playlist = pendingPlaylist.playlist(); + emit q->currentMediaChanged(pendingPlaylist); + } + pendingPlaylist = QMediaContent(); + + playlist->next(); + setPlaylistMedia(); + + switch (state) { + case QMediaPlayer::PausedState: + control->pause(); + break; + case QMediaPlayer::PlayingState: + control->play(); + break; + case QMediaPlayer::StoppedState: + break; + } +} + +void QMediaPlayerPrivate::_q_handlePlaylistLoadFailed() +{ + pendingPlaylist = QMediaContent(); + + if (!control) + return; + + if (playlist) + playlist->next(); + else + control->setMedia(QMediaContent(), 0); +} + static QMediaService *playerService(QMediaPlayer::Flags flags) { QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider(); @@ -277,10 +488,9 @@ QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags): d->error = ServiceMissingError; } else { d->control = qobject_cast(d->service->requestControl(QMediaPlayerControl_iid)); - d->playlistSourceControl = qobject_cast(d->service->requestControl(QMediaPlaylistSourceControl_iid)); d->networkAccessControl = qobject_cast(d->service->requestControl(QMediaNetworkAccessControl_iid)); if (d->control != 0) { - connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SIGNAL(mediaChanged(QMediaContent))); + connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SIGNAL(currentMediaChanged(QMediaContent))); connect(d->control, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(_q_stateChanged(QMediaPlayer::State))); connect(d->control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), SLOT(_q_mediaStatusChanged(QMediaPlayer::MediaStatus))); @@ -330,10 +540,7 @@ QMediaContent QMediaPlayer::media() const { Q_D(const QMediaPlayer); - if (d->control != 0) - return d->control->media(); - - return QMediaContent(); + return d->rootMedia; } /*! @@ -356,44 +563,25 @@ const QIODevice *QMediaPlayer::mediaStream() const QMediaPlaylist *QMediaPlayer::playlist() const { - return d_func()->playlistSourceControl ? - d_func()->playlistSourceControl->playlist() : - d_func()->playlist; + Q_D(const QMediaPlayer); + + return d->rootMedia.playlist(); } -void QMediaPlayer::setPlaylist(QMediaPlaylist *playlist) +QMediaContent QMediaPlayer::currentMedia() const { - Q_D(QMediaPlayer); - - if (d->playlistSourceControl) { - if (d->playlistSourceControl->playlist()) - disconnect(d->playlist, SIGNAL(destroyed()), this, SLOT(_q_playlistDestroyed())); - - d->playlistSourceControl->setPlaylist(playlist); - - if (playlist) - connect(d->playlist, SIGNAL(destroyed()), this, SLOT(_q_playlistDestroyed())); - } else { - if (d->playlist) { - disconnect(d->playlist, SIGNAL(currentMediaChanged(QMediaContent)), - this, SLOT(_q_updateMedia(QMediaContent))); - disconnect(d->playlist, SIGNAL(destroyed()), this, SLOT(_q_playlistDestroyed())); - } - - d->playlist = playlist; + Q_D(const QMediaPlayer); - if (d->playlist) { - connect(d->playlist, SIGNAL(currentMediaChanged(QMediaContent)), - this, SLOT(_q_updateMedia(QMediaContent))); - connect(d->playlist, SIGNAL(destroyed()), this, SLOT(_q_playlistDestroyed())); + if (d->control != 0) + return d->control->media(); - if (d->control != 0) - d->control->setMedia(playlist->currentMedia(), 0); - } else { - setMedia(QMediaContent(), 0); - } + return QMediaContent(); +} - } +void QMediaPlayer::setPlaylist(QMediaPlaylist *playlist) +{ + QMediaContent m(playlist, QUrl(), false); + setMedia(m); } /*! @@ -563,8 +751,18 @@ void QMediaPlayer::play() } //if playlist control is available, the service should advance itself - if (d->playlist && d->playlist->currentIndex() == -1 && !d->playlist->isEmpty()) + if (d->rootMedia.playlist() && d->rootMedia.playlist()->currentIndex() == -1 && !d->rootMedia.playlist()->isEmpty()) { + + // switch to playing state + if (d->state != QMediaPlayer::PlayingState) + d->_q_stateChanged(QMediaPlayer::PlayingState); + + if (d->playlist != d->rootMedia.playlist()) + d->setPlaylist(d->rootMedia.playlist()); + Q_ASSERT(d->playlist == d->rootMedia.playlist()); + emit currentMediaChanged(d->rootMedia); d->playlist->setCurrentIndex(0); + } // Reset error conditions d->error = NoError; @@ -595,6 +793,17 @@ void QMediaPlayer::stop() if (d->control != 0) d->control->stop(); + + // If media player didn't stop in response to control. + // This happens if we have an active playlist and control + // media status is + // QMediaPlayer::LoadingMedia, QMediaPlayer::InvalidMedia, or QMediaPlayer::EndOfMedia + // see QMediaPlayerPrivate::_q_stateChanged() + if (d->playlist && d->state != QMediaPlayer::StoppedState) { + d->state = QMediaPlayer::StoppedState; + removePropertyWatch("position"); + emit stateChanged(QMediaPlayer::StoppedState); + } } void QMediaPlayer::setPosition(qint64 position) @@ -654,12 +863,24 @@ void QMediaPlayer::setPlaybackRate(qreal rate) void QMediaPlayer::setMedia(const QMediaContent &media, QIODevice *stream) { Q_D(QMediaPlayer); - - if (playlist() && playlist()->currentMedia() != media) - setPlaylist(0); - - if (d->control != 0) - d_func()->control->setMedia(media, stream); + stop(); + + QMediaContent oldMedia = d->rootMedia; + d->disconnectPlaylist(); + d->playlist = 0; + d->rootMedia = media; + d->nestedPlaylists = 0; + + if (oldMedia != media) + emit mediaChanged(d->rootMedia); + + if (media.playlist()) { + // reset playlist to the 1st item + media.playlist()->setCurrentIndex(0); + d->setPlaylist(media.playlist()); + } else if (d->control != 0) { + d->control->setMedia(media, stream); + } } /*! @@ -796,6 +1017,7 @@ QtMultimedia::AvailabilityError QMediaPlayer::availabilityError() const return QMediaObject::availabilityError(); } + // Enums /*! \enum QMediaPlayer::State @@ -841,6 +1063,7 @@ QtMultimedia::AvailabilityError QMediaPlayer::availabilityError() const \value NetworkError A network error occurred. \value AccessDeniedError There are not the appropriate permissions to play a media resource. \value ServiceMissingError A valid playback service was not found, playback cannot proceed. + \omitvalue MediaIsPlaylist */ // Signals @@ -869,9 +1092,17 @@ QtMultimedia::AvailabilityError QMediaPlayer::availabilityError() const /*! \fn void QMediaPlayer::mediaChanged(const QMediaContent &media); - Signals that the current playing content will be obtained from \a media. + Signals that the media source has been changed to \a media. + + \sa media(), currentMediaChanged() +*/ + +/*! + \fn void QMediaPlayer::currentMediaChanged(const QMediaContent &media); + + Signals that the current playing content has been changed to \a media. - \sa media() + \sa currentMedia(), mediaChanged() */ /*! @@ -916,7 +1147,17 @@ QtMultimedia::AvailabilityError QMediaPlayer::availabilityError() const information relating to the current media source and to cease all I/O operations related to that media. - \sa QMediaContent + \sa QMediaContent, currentMedia() +*/ + +/*! + \property QMediaPlayer::currentMedia + \brief the current active media content being played by the player object. + This value could be different from QMediaPlayer::media property if a playlist is used. + In this case currentMedia indicates the current media content being processed + by the player, while QMediaPlayer::media property contains the original playlist. + + \sa QMediaContent, media() */ /*! @@ -928,7 +1169,7 @@ QtMultimedia::AvailabilityError QMediaPlayer::availabilityError() const By default this property is set to null. - If the media playlist is used as a source, QMediaPlayer::media is updated with + If the media playlist is used as a source, QMediaPlayer::currentMedia is updated with a current playlist item. The current source should be selected with QMediaPlaylist::setCurrentIndex(int) instead of QMediaPlayer::setMedia(), otherwise the current playlist will be discarded. diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index eed09b566..b6d154bc2 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -65,6 +65,7 @@ class Q_MULTIMEDIA_EXPORT QMediaPlayer : public QMediaObject { Q_OBJECT Q_PROPERTY(QMediaContent media READ media WRITE setMedia NOTIFY mediaChanged) + Q_PROPERTY(QMediaContent currentMedia READ currentMedia NOTIFY currentMediaChanged) Q_PROPERTY(QMediaPlaylist * playlist READ playlist WRITE setPlaylist) Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) Q_PROPERTY(qint64 position READ position WRITE setPosition NOTIFY positionChanged) @@ -118,7 +119,8 @@ public: FormatError, NetworkError, AccessDeniedError, - ServiceMissingError + ServiceMissingError, + MediaIsPlaylist }; QMediaPlayer(QObject *parent = 0, Flags flags = 0); @@ -136,6 +138,7 @@ public: QMediaContent media() const; const QIODevice *mediaStream() const; QMediaPlaylist *playlist() const; + QMediaContent currentMedia() const; State state() const; MediaStatus mediaStatus() const; @@ -178,6 +181,7 @@ public Q_SLOTS: Q_SIGNALS: void mediaChanged(const QMediaContent &media); + void currentMediaChanged(const QMediaContent &media); void stateChanged(QMediaPlayer::State newState); void mediaStatusChanged(QMediaPlayer::MediaStatus status); @@ -210,6 +214,8 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_error(int, const QString &)) Q_PRIVATE_SLOT(d_func(), void _q_updateMedia(const QMediaContent&)) Q_PRIVATE_SLOT(d_func(), void _q_playlistDestroyed()) + Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoaded()) + Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoadFailed()) }; QT_END_NAMESPACE diff --git a/src/multimedia/playback/qmediaplaylist.cpp b/src/multimedia/playback/qmediaplaylist.cpp index 860fca2cc..8c2cd3112 100644 --- a/src/multimedia/playback/qmediaplaylist.cpp +++ b/src/multimedia/playback/qmediaplaylist.cpp @@ -42,8 +42,8 @@ #include "qmediaplaylist.h" #include "qmediaplaylist_p.h" #include "qmediaplaylistprovider_p.h" -#include "qlocalmediaplaylistprovider_p.h" #include "qmediaplaylistioplugin_p.h" +#include "qmedianetworkplaylistprovider_p.h" #include "qmediaservice.h" #include "qmediaplaylistcontrol_p.h" #include "qmediaplayercontrol.h" @@ -129,7 +129,7 @@ QMediaPlaylist::QMediaPlaylist(QObject *parent) Q_D(QMediaPlaylist); d->q_ptr = this; - d->localPlaylistControl = new QLocalMediaPlaylistControl(this); + d->networkPlaylistControl = new QMediaNetworkPlaylistControl(this); setMediaObject(0); } @@ -178,7 +178,7 @@ bool QMediaPlaylist::setMediaObject(QMediaObject *mediaObject) newControl = qobject_cast(service->requestControl(QMediaPlaylistControl_iid)); if (!newControl) - newControl = d->localPlaylistControl; + newControl = d->networkPlaylistControl; if (d->control != newControl) { int oldSize = 0; diff --git a/src/multimedia/playback/qmediaplaylist_p.h b/src/multimedia/playback/qmediaplaylist_p.h index 7d718da2a..9a1555184 100644 --- a/src/multimedia/playback/qmediaplaylist_p.h +++ b/src/multimedia/playback/qmediaplaylist_p.h @@ -57,7 +57,7 @@ #include "qmediaplaylistcontrol_p.h" #include "qmediaplayer.h" #include "qmediaplayercontrol.h" -#include "qlocalmediaplaylistprovider_p.h" +#include "qmedianetworkplaylistprovider_p.h" #include "qmediaobject_p.h" #include @@ -86,7 +86,7 @@ public: QMediaPlaylistPrivate() :mediaObject(0), control(0), - localPlaylistControl(0), + networkPlaylistControl(0), error(QMediaPlaylist::NoError) { } @@ -105,7 +105,7 @@ public: { Q_Q(QMediaPlaylist); mediaObject = 0; - if (control != localPlaylistControl) + if (control != networkPlaylistControl) control = 0; q->setMediaObject(0); } @@ -115,7 +115,7 @@ public: QMediaPlaylistControl *control; QMediaPlaylistProvider *playlist() const { return control->playlistProvider(); } - QMediaPlaylistControl *localPlaylistControl; + QMediaPlaylistControl *networkPlaylistControl; bool readItems(QMediaPlaylistReader *reader); bool writeItems(QMediaPlaylistWriter *writer); @@ -127,14 +127,14 @@ public: }; -class QLocalMediaPlaylistControl : public QMediaPlaylistControl +class QMediaNetworkPlaylistControl : public QMediaPlaylistControl { Q_OBJECT public: - QLocalMediaPlaylistControl(QObject *parent) + QMediaNetworkPlaylistControl(QObject *parent) :QMediaPlaylistControl(parent) { - QMediaPlaylistProvider *playlist = new QLocalMediaPlaylistProvider(this); + QMediaPlaylistProvider *playlist = new QMediaNetworkPlaylistProvider(this); m_navigator = new QMediaPlaylistNavigator(playlist,this); m_navigator->setPlaybackMode(QMediaPlaylist::Sequential); @@ -143,7 +143,7 @@ public: connect(m_navigator, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)), SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode))); } - virtual ~QLocalMediaPlaylistControl() {}; + virtual ~QMediaNetworkPlaylistControl() {}; QMediaPlaylistProvider* playlistProvider() const { return m_navigator->playlist(); } bool setPlaylistProvider(QMediaPlaylistProvider *mediaPlaylist) -- cgit v1.2.3