diff options
25 files changed, 304 insertions, 165 deletions
diff --git a/src/multimedia/controls/qmediaplayercontrol.cpp b/src/multimedia/controls/qmediaplayercontrol.cpp index 1eccb7627..9ea6fde82 100644 --- a/src/multimedia/controls/qmediaplayercontrol.cpp +++ b/src/multimedia/controls/qmediaplayercontrol.cpp @@ -315,6 +315,11 @@ QMediaPlayerControl::QMediaPlayerControl(QObject *parent): Setting the media to a null QMediaContent will cause the control to discard all information relating to the current media source and to cease all I/O operations related to that media. + + Qt resource files are never passed as is. If the service supports + QMediaServiceProviderHint::StreamPlayback, a \a stream is supplied, pointing to an opened + QFile. Otherwise, the resource is copied into a temporary file and \a media contains the + url to that file. */ /*! diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index aae4c7efa..b43faa2b9 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -48,6 +48,8 @@ #include <QtCore/qtimer.h> #include <QtCore/qdebug.h> #include <QtCore/qpointer.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qtemporaryfile.h> QT_BEGIN_NAMESPACE @@ -103,22 +105,30 @@ public: : provider(0) , control(0) , state(QMediaPlayer::StoppedState) + , status(QMediaPlayer::UnknownMediaStatus) , error(QMediaPlayer::NoError) + , ignoreNextStatusChange(-1) , playlist(0) , networkAccessControl(0) + , hasStreamPlaybackFeature(false) , nestedPlaylists(0) {} QMediaServiceProvider *provider; QMediaPlayerControl* control; QMediaPlayer::State state; + QMediaPlayer::MediaStatus status; QMediaPlayer::Error error; QString errorString; + int ignoreNextStatusChange; QPointer<QObject> videoOutput; QMediaPlaylist *playlist; QMediaNetworkAccessControl *networkAccessControl; QVideoSurfaceOutput surfaceOutput; + bool hasStreamPlaybackFeature; + QMediaContent qrcMedia; + QScopedPointer<QFile> qrcFile; QMediaContent rootMedia; QMediaContent pendingPlaylist; @@ -126,6 +136,8 @@ public: bool isInChain(QUrl url); int nestedPlaylists; + void setMedia(const QMediaContent &media, QIODevice *stream = 0); + void setPlaylist(QMediaPlaylist *playlist); void setPlaylistMedia(); void loadPlaylist(); @@ -137,6 +149,7 @@ public: void _q_error(int error, const QString &errorString); void _q_updateMedia(const QMediaContent&); void _q_playlistDestroyed(); + void _q_handleMediaChanged(const QMediaContent&); void _q_handlePlaylistLoaded(); void _q_handlePlaylistLoadFailed(); }; @@ -196,22 +209,30 @@ void QMediaPlayerPrivate::_q_stateChanged(QMediaPlayer::State ps) } } -void QMediaPlayerPrivate::_q_mediaStatusChanged(QMediaPlayer::MediaStatus status) +void QMediaPlayerPrivate::_q_mediaStatusChanged(QMediaPlayer::MediaStatus s) { Q_Q(QMediaPlayer); - switch (status) { - case QMediaPlayer::StalledMedia: - case QMediaPlayer::BufferingMedia: - q->addPropertyWatch("bufferStatus"); - emit q->mediaStatusChanged(status); - break; - default: - q->removePropertyWatch("bufferStatus"); - emit q->mediaStatusChanged(status); - break; + if (int(s) == ignoreNextStatusChange) { + ignoreNextStatusChange = -1; + return; } + if (s != status) { + status = s; + + switch (s) { + case QMediaPlayer::StalledMedia: + case QMediaPlayer::BufferingMedia: + q->addPropertyWatch("bufferStatus"); + break; + default: + q->removePropertyWatch("bufferStatus"); + break; + } + + emit q->mediaStatusChanged(s); + } } void QMediaPlayerPrivate::_q_error(int error, const QString &errorString) @@ -276,7 +297,7 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) const QMediaPlayer::State currentState = state; - control->setMedia(media, 0); + setMedia(media, 0); if (!media.isNull()) { switch (currentState) { @@ -297,11 +318,76 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) void QMediaPlayerPrivate::_q_playlistDestroyed() { playlist = 0; + setMedia(QMediaContent(), 0); +} + +void QMediaPlayerPrivate::setMedia(const QMediaContent &media, QIODevice *stream) +{ + Q_Q(QMediaPlayer); if (!control) return; - control->setMedia(QMediaContent(), 0); + QScopedPointer<QFile> file; + + // Backends can't play qrc files directly. + // If the backend supports StreamPlayback, we pass a QFile for that resource. + // If it doesn't, we copy the data to a temporary file and pass its path. + if (!media.isNull() && !stream && media.canonicalUrl().scheme() == QLatin1String("qrc")) { + qrcMedia = media; + + file.reset(new QFile(QLatin1Char(':') + media.canonicalUrl().path())); + if (!file->open(QFile::ReadOnly)) { + QMetaObject::invokeMethod(q, "_q_error", Qt::QueuedConnection, + Q_ARG(int, QMediaPlayer::ResourceError), + Q_ARG(QString, QObject::tr("Attempting to play invalid Qt resource"))); + QMetaObject::invokeMethod(q, "_q_mediaStatusChanged", Qt::QueuedConnection, + Q_ARG(QMediaPlayer::MediaStatus, QMediaPlayer::InvalidMedia)); + file.reset(); + // Ignore the next NoMedia status change, we just want to clear the current media + // on the backend side since we can't load the new one and we want to be in the + // InvalidMedia status. + ignoreNextStatusChange = QMediaPlayer::NoMedia; + control->setMedia(QMediaContent(), 0); + + } else if (hasStreamPlaybackFeature) { + control->setMedia(media, file.data()); + } else { + QTemporaryFile *tempFile = new QTemporaryFile; + + // Preserve original file extension, some backends might not load the file if it doesn't + // have an extension. + const QString suffix = QFileInfo(*file).suffix(); + if (!suffix.isEmpty()) + tempFile->setFileTemplate(tempFile->fileTemplate() + QLatin1Char('.') + suffix); + + // Copy the qrc data into the temporary file + tempFile->open(); + char buffer[4096]; + while (true) { + qint64 len = file->read(buffer, sizeof(buffer)); + if (len < 1) + break; + tempFile->write(buffer, len); + } + tempFile->close(); + + file.reset(tempFile); + control->setMedia(QMediaContent(QUrl::fromLocalFile(file->fileName())), 0); + } + } else { + qrcMedia = QMediaContent(); + control->setMedia(media, stream); + } + + qrcFile.swap(file); // Cleans up any previous file +} + +void QMediaPlayerPrivate::_q_handleMediaChanged(const QMediaContent &media) +{ + Q_Q(QMediaPlayer); + + emit q->currentMediaChanged(qrcMedia.isNull() ? media : qrcMedia); } void QMediaPlayerPrivate::setPlaylist(QMediaPlaylist *pls) @@ -333,7 +419,7 @@ void QMediaPlayerPrivate::setPlaylistMedia() playlist->next(); } return; - } else if (control != 0) { + } else { // If we've just switched to a new playlist, // then last emitted currentMediaChanged was a playlist. // Make sure we emit currentMediaChanged if new playlist has @@ -344,14 +430,14 @@ void QMediaPlayerPrivate::setPlaylistMedia() // 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); + bool isSameMedia = (q->currentMedia() == playlist->currentMedia()); + setMedia(playlist->currentMedia(), 0); if (isSameMedia) { - emit q->currentMediaChanged(control->media()); + emit q->currentMediaChanged(q->currentMedia()); } } } else { - q->setMedia(QMediaContent(), 0); + setMedia(QMediaContent(), 0); } } @@ -441,7 +527,7 @@ void QMediaPlayerPrivate::_q_handlePlaylistLoadFailed() if (playlist) playlist->next(); else - control->setMedia(QMediaContent(), 0); + setMedia(QMediaContent(), 0); } static QMediaService *playerService(QMediaPlayer::Flags flags) @@ -484,7 +570,7 @@ QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags): d->control = qobject_cast<QMediaPlayerControl*>(d->service->requestControl(QMediaPlayerControl_iid)); d->networkAccessControl = qobject_cast<QMediaNetworkAccessControl*>(d->service->requestControl(QMediaNetworkAccessControl_iid)); if (d->control != 0) { - connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SIGNAL(currentMediaChanged(QMediaContent))); + connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SLOT(_q_handleMediaChanged(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))); @@ -500,11 +586,16 @@ QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags): connect(d->control, SIGNAL(playbackRateChanged(qreal)), SIGNAL(playbackRateChanged(qreal))); connect(d->control, SIGNAL(bufferStatusChanged(int)), SIGNAL(bufferStatusChanged(int))); - if (d->control->state() == PlayingState) + d->state = d->control->state(); + d->status = d->control->mediaStatus(); + + if (d->state == PlayingState) addPropertyWatch("position"); - if (d->control->mediaStatus() == StalledMedia || d->control->mediaStatus() == BufferingMedia) + if (d->status == StalledMedia || d->status == BufferingMedia) addPropertyWatch("bufferStatus"); + + d->hasStreamPlaybackFeature = d->provider->supportedFeatures(d->service).testFlag(QMediaServiceProviderHint::StreamPlayback); } if (d->networkAccessControl != 0) { connect(d->networkAccessControl, SIGNAL(configurationChanged(QNetworkConfiguration)), @@ -549,7 +640,9 @@ const QIODevice *QMediaPlayer::mediaStream() const { Q_D(const QMediaPlayer); - if (d->control != 0) + // When playing a resource file, we might have passed a QFile to the backend. Hide it from + // the user. + if (d->control && d->qrcMedia.isNull()) return d->control->mediaStream(); return 0; @@ -566,7 +659,12 @@ QMediaContent QMediaPlayer::currentMedia() const { Q_D(const QMediaPlayer); - if (d->control != 0) + // When playing a resource file, don't return the backend's current media, which + // can be a temporary file. + if (!d->qrcMedia.isNull()) + return d->qrcMedia; + + if (d->control) return d->control->media(); return QMediaContent(); @@ -600,12 +698,7 @@ QMediaPlayer::State QMediaPlayer::state() const QMediaPlayer::MediaStatus QMediaPlayer::mediaStatus() const { - Q_D(const QMediaPlayer); - - if (d->control != 0) - return d->control->mediaStatus(); - - return QMediaPlayer::UnknownMediaStatus; + return d_func()->status; } qint64 QMediaPlayer::duration() const @@ -877,8 +970,8 @@ void QMediaPlayer::setMedia(const QMediaContent &media, QIODevice *stream) // reset playlist to the 1st item media.playlist()->setCurrentIndex(0); d->setPlaylist(media.playlist()); - } else if (d->control != 0) { - d->control->setMedia(media, stream); + } else { + d->setMedia(media, stream); } } diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index b1215eee1..735f11130 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -202,6 +202,7 @@ 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_handleMediaChanged(const QMediaContent&)) Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoaded()) Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoadFailed()) }; diff --git a/src/multimedia/qmediaserviceprovider.cpp b/src/multimedia/qmediaserviceprovider.cpp index 563af846a..658679c56 100644 --- a/src/multimedia/qmediaserviceprovider.cpp +++ b/src/multimedia/qmediaserviceprovider.cpp @@ -299,7 +299,14 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, loader, class QPluginServiceProvider : public QMediaServiceProvider { - QMap<QMediaService*, QMediaServiceProviderPlugin*> pluginMap; + struct MediaServiceData { + QByteArray type; + QMediaServiceProviderPlugin *plugin; + + MediaServiceData() : plugin(0) { } + }; + + QMap<const QMediaService*, MediaServiceData> mediaServiceData; public: QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint) @@ -416,8 +423,12 @@ public: if (plugin != 0) { QMediaService *service = plugin->create(key); - if (service != 0) - pluginMap.insert(service, plugin); + if (service != 0) { + MediaServiceData d; + d.type = type; + d.plugin = plugin; + mediaServiceData.insert(service, d); + } return service; } @@ -430,11 +441,28 @@ public: void releaseService(QMediaService *service) { if (service != 0) { - QMediaServiceProviderPlugin *plugin = pluginMap.take(service); + MediaServiceData d = mediaServiceData.take(service); + + if (d.plugin != 0) + d.plugin->release(service); + } + } + + QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const + { + if (service) { + MediaServiceData d = mediaServiceData.value(service); + + if (d.plugin) { + QMediaServiceFeaturesInterface *iface = + qobject_cast<QMediaServiceFeaturesInterface*>(d.plugin); - if (plugin != 0) - plugin->release(service); + if (iface) + return iface->supportedFeatures(d.type); + } } + + return QMediaServiceProviderHint::Features(); } QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType, @@ -661,6 +689,18 @@ Q_GLOBAL_STATIC(QPluginServiceProvider, pluginProvider); */ /*! + \fn QMediaServiceProvider::supportedFeatures(const QMediaService *service) const + + Returns the features supported by a given \a service. +*/ +QMediaServiceProviderHint::Features QMediaServiceProvider::supportedFeatures(const QMediaService *service) const +{ + Q_UNUSED(service); + + return QMediaServiceProviderHint::Features(0); +} + +/*! \fn QMultimedia::SupportEstimate QMediaServiceProvider::hasSupport(const QByteArray &serviceType, const QString &mimeType, const QStringList& codecs, int flags) const Returns how confident a media service provider is that is can provide a \a diff --git a/src/multimedia/qmediaserviceprovider_p.h b/src/multimedia/qmediaserviceprovider_p.h index 62ee510c2..4230c427d 100644 --- a/src/multimedia/qmediaserviceprovider_p.h +++ b/src/multimedia/qmediaserviceprovider_p.h @@ -53,6 +53,8 @@ public: virtual QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint = QMediaServiceProviderHint()) = 0; virtual void releaseService(QMediaService *service) = 0; + virtual QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const; + virtual QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType, const QString &mimeType, const QStringList& codecs, diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index c65dec44e..9a050e7ad 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -318,8 +318,6 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0) mMediaPlayer->release(); - QString mediaPath; - if (mediaContent.isNull()) { setMediaStatus(QMediaPlayer::NoMedia); } else { @@ -330,29 +328,17 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, return; } - const QUrl url = mediaContent.canonicalUrl(); - if (url.scheme() == QLatin1String("qrc")) { - const QString path = url.toString().mid(3); - mTempFile.reset(QTemporaryFile::createNativeFile(path)); - if (!mTempFile.isNull()) - mediaPath = QStringLiteral("file://") + mTempFile->fileName(); - } else { - mediaPath = url.toString(QUrl::FullyEncoded); - } - if (mVideoSize.isValid() && mVideoOutput) mVideoOutput->setVideoSize(mVideoSize); if ((mMediaPlayer->display() == 0) && mVideoOutput) mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); - mMediaPlayer->setDataSource(mediaPath); + mMediaPlayer->setDataSource(mediaContent.canonicalUrl().toString(QUrl::FullyEncoded)); mMediaPlayer->prepareAsync(); } - if (!mReloadingMedia) { + if (!mReloadingMedia) Q_EMIT mediaChanged(mMediaContent); - Q_EMIT actualMediaLocationChanged(mediaPath); - } resetBufferingProgress(); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index dfc3853a2..3f92d809c 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -37,7 +37,6 @@ #include <qglobal.h> #include <QMediaPlayerControl> #include <qsize.h> -#include <QtCore/QTemporaryFile> QT_BEGIN_NAMESPACE @@ -72,7 +71,6 @@ public: Q_SIGNALS: void metaDataUpdated(); - void actualMediaLocationChanged(const QString &url); public Q_SLOTS: void setPosition(qint64 position) Q_DECL_OVERRIDE; @@ -112,7 +110,6 @@ private: int mPendingVolume; int mPendingMute; bool mReloadingMedia; - QScopedPointer<QTemporaryFile> mTempFile; int mActiveStateChangeNotifiers; void setState(QMediaPlayer::State state); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 94df8d3c5..74943ca64 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -45,8 +45,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent) { mMediaControl = new QAndroidMediaPlayerControl; mMetadataControl = new QAndroidMetaDataReaderControl; - connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)), - mMetadataControl, SLOT(onMediaChanged(QString))); + connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)), + mMetadataControl, SLOT(onMediaChanged(QMediaContent))); connect(mMediaControl, SIGNAL(metaDataUpdated()), mMetadataControl, SLOT(onUpdateMetaData())); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index 81d7cf1a7..d09a7734f 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -93,18 +93,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const return m_metadata.keys(); } -void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url) +void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media) { if (!m_retriever) return; - m_mediaLocation = url; + m_mediaContent = media; updateData(); } void QAndroidMetaDataReaderControl::onUpdateMetaData() { - if (!m_retriever || m_mediaLocation.isEmpty()) + if (!m_retriever || m_mediaContent.isNull()) return; updateData(); @@ -114,8 +114,8 @@ void QAndroidMetaDataReaderControl::updateData() { m_metadata.clear(); - if (!m_mediaLocation.isEmpty()) { - if (m_retriever->setDataSource(m_mediaLocation)) { + if (!m_mediaContent.isNull()) { + if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) { QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType); if (!mimeType.isNull()) m_metadata.insert(QMediaMetaData::MediaType, mimeType); diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h index 268477301..14fb01ea0 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h @@ -54,13 +54,13 @@ public: QStringList availableMetaData() const Q_DECL_OVERRIDE; public Q_SLOTS: - void onMediaChanged(const QString &url); + void onMediaChanged(const QMediaContent &media); void onUpdateMetaData(); private: void updateData(); - QString m_mediaLocation; + QMediaContent m_mediaContent; bool m_available; QVariantMap m_metadata; diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp index 9714654a2..56ac0e0ac 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp @@ -83,15 +83,14 @@ void AndroidMediaMetadataRetriever::release() m_metadataRetriever.callMethod<void>("release"); } -bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) +bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url) { if (!m_metadataRetriever.isValid()) return false; QJNIEnvironmentPrivate env; - QUrl url(urlString); - if (url.isLocalFile()) { // also includes qrc files (copied to a temp file) + if (url.isLocalFile()) { // also includes qrc files (copied to a temp file by QMediaPlayer) QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path()); QJNIObjectPrivate fileInputStream("java/io/FileInputStream", "(Ljava/lang/String;)V", @@ -153,7 +152,7 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) return false; } else if (QtAndroidPrivate::androidSdkVersion() >= 14) { // On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media - QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded)); QJNIObjectPrivate hash("java/util/HashMap"); m_metadataRetriever.callMethod<void>("setDataSource", @@ -165,7 +164,7 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) } else { // While on API levels < 14, only setDataSource(Context, Uri) is available and works for // remote media... - QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded)); QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h index 13cab7741..01a98490b 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h @@ -72,7 +72,7 @@ public: QString extractMetadata(MetadataKey key); void release(); - bool setDataSource(const QString &url); + bool setDataSource(const QUrl &url); private: QJNIObjectPrivate m_metadataRetriever; diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp index 03b53c44a..3a4e1075c 100644 --- a/src/plugins/directshow/player/directshowiosource.cpp +++ b/src/plugins/directshow/player/directshowiosource.cpp @@ -605,21 +605,3 @@ HRESULT DirectShowIOSource::QueryDirection(PIN_DIRECTION *pPinDir) return S_OK; } } - -DirectShowRcSource::DirectShowRcSource(DirectShowEventLoop *loop) - : DirectShowIOSource(loop) -{ -} - -bool DirectShowRcSource::open(const QUrl &url) -{ - m_file.moveToThread(QCoreApplication::instance()->thread()); - m_file.setFileName(QLatin1Char(':') + url.path()); - - if (m_file.open(QIODevice::ReadOnly)) { - setDevice(&m_file); - return true; - } else { - return false; - } -} diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h index 241c86fba..fb3774af6 100644 --- a/src/plugins/directshow/player/directshowiosource.h +++ b/src/plugins/directshow/player/directshowiosource.h @@ -127,15 +127,4 @@ private: QMutex m_mutex; }; -class DirectShowRcSource : public DirectShowIOSource -{ -public: - DirectShowRcSource(DirectShowEventLoop *loop); - - bool open(const QUrl &url); - -private: - QFile m_file; -}; - #endif diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 809839c6f..67aea6e9a 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -289,15 +289,6 @@ void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker) fileSource->Release(); locker->relock(); } - } else if (m_url.scheme() == QLatin1String("qrc")) { - DirectShowRcSource *rcSource = new DirectShowRcSource(m_loop); - - locker->unlock(); - if (rcSource->open(m_url) && SUCCEEDED(hr = m_graph->AddFilter(rcSource, L"Source"))) - source = rcSource; - else - rcSource->Release(); - locker->relock(); } if (!SUCCEEDED(hr)) { diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp index 8fc301a38..4846353a9 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) : QMediaPlayerControl(parent) - , m_ownStream(false) , m_session(session) , m_userRequestedState(QMediaPlayer::StoppedState) , m_currentState(QMediaPlayer::StoppedState) @@ -370,31 +369,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * emit bufferStatusChanged(0); } - if (m_stream && m_stream != stream) { - if (m_ownStream) - delete m_stream; - m_stream = 0; - m_ownStream = false; - } - - // If the canonical URL refers to a Qt resource, open with QFile and use - // the stream playback capability to play. - if (stream == 0 && content.canonicalUrl().scheme() == QLatin1String("qrc")) { - stream = new QFile(QLatin1Char(':') + content.canonicalUrl().path(), this); - if (!stream->open(QIODevice::ReadOnly)) { - delete stream; - m_mediaStatus = QMediaPlayer::InvalidMedia; - m_currentResource = content; - emit mediaChanged(m_currentResource); - emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid Qt resource")); - if (m_currentState != QMediaPlayer::PlayingState) - m_resources->release(); - popAndNotifyState(); - return; - } - m_ownStream = true; - } - m_currentResource = content; m_stream = stream; diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h index 216885064..c9621b798 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h @@ -116,7 +116,6 @@ private: void pushState(); void popAndNotifyState(); - bool m_ownStream; QGstreamerPlayerSession *m_session; QMediaPlayer::State m_userRequestedState; QMediaPlayer::State m_currentState; diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp index 30524106e..3ba640cd6 100644 --- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp +++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp @@ -162,22 +162,6 @@ QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url) const QFileInfo fileInfo(relativeFilePath); return QFile::encodeName(QStringLiteral("file://") + fileInfo.absoluteFilePath()); - // QRC, copy to temporary file, as mmrenderer does not support resource files - } else if (url.scheme() == QStringLiteral("qrc")) { - const QString qrcPath = ':' + url.path(); - const QFileInfo resourceFileInfo(qrcPath); - m_tempMediaFileName = QDir::tempPath() + QStringLiteral("/qtmedia_") + - QUuid::createUuid().toString() + QStringLiteral(".") + - resourceFileInfo.suffix(); - if (!QFile::copy(qrcPath, m_tempMediaFileName)) { - const QString errorMsg = QString("Failed to copy resource file to temporary file " - "%1 for playback").arg(m_tempMediaFileName); - qDebug() << errorMsg; - emit error(0, errorMsg); - return QByteArray(); - } - return QFile::encodeName(m_tempMediaFileName); - // HTTP or similar URL } else { return url.toEncoded(); @@ -187,7 +171,7 @@ QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url) void MmRendererMediaPlayerControl::attach() { // Should only be called in detached state - Q_ASSERT(m_audioId == -1 && !m_inputAttached && m_tempMediaFileName.isEmpty()); + Q_ASSERT(m_audioId == -1 && !m_inputAttached); if (m_media.isNull() || !m_context) { setMediaStatus(QMediaPlayer::NoMedia); @@ -251,10 +235,6 @@ void MmRendererMediaPlayerControl::detach() } } - if (!m_tempMediaFileName.isEmpty()) { - QFile::remove(m_tempMediaFileName); - m_tempMediaFileName.clear(); - } m_loadingTimer.stop(); } diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h index d4ddf3633..79fc9be0b 100644 --- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h +++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h @@ -156,7 +156,6 @@ private: bool m_inputAttached; int m_stopEventsToIgnore; int m_bufferLevel; - QString m_tempMediaFileName; QTimer m_loadingTimer; }; diff --git a/tests/auto/unit/qmediaplayer/qmediaplayer.pro b/tests/auto/unit/qmediaplayer/qmediaplayer.pro index 52568c071..cbdbf71f4 100644 --- a/tests/auto/unit/qmediaplayer/qmediaplayer.pro +++ b/tests/auto/unit/qmediaplayer/qmediaplayer.pro @@ -2,6 +2,7 @@ CONFIG += testcase no_private_qt_headers_warning TARGET = tst_qmediaplayer QT += network multimedia-private testlib SOURCES += tst_qmediaplayer.cpp +RESOURCES += testdata.qrc include (../qmultimedia_common/mock.pri) include (../qmultimedia_common/mockplayer.pri) diff --git a/tests/auto/unit/qmediaplayer/testdata.qrc b/tests/auto/unit/qmediaplayer/testdata.qrc new file mode 100644 index 000000000..1afc630dd --- /dev/null +++ b/tests/auto/unit/qmediaplayer/testdata.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> + <file>testdata/nokia-tune.mp3</file> +</qresource> +</RCC> diff --git a/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3 b/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3 Binary files differnew file mode 100644 index 000000000..2435f65b8 --- /dev/null +++ b/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3 diff --git a/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp b/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp index 2082fe14a..0271f1a8f 100644 --- a/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp +++ b/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp @@ -133,6 +133,8 @@ private slots: void testPlayerFlags(); void testDestructor(); void testSupportedMimeTypes(); + void testQrc_data(); + void testQrc(); private: void setupCommonTestData(); @@ -976,12 +978,17 @@ void tst_QMediaPlayer::testPlaylist() player->setPlaylist(playlist); player->play(); QCOMPARE(ss.count(), 1); + QCOMPARE(ms.count(), 1); + QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(ms.last().value(0)), QMediaPlayer::LoadingMedia); + ms.clear(); mockService->setState(QMediaPlayer::StoppedState, QMediaPlayer::InvalidMedia); QCOMPARE(player->state(), QMediaPlayer::PlayingState); - QCOMPARE(player->mediaStatus(), QMediaPlayer::InvalidMedia); + QCOMPARE(player->mediaStatus(), QMediaPlayer::LoadingMedia); QCOMPARE(ss.count(), 1); - QCOMPARE(ms.count(), 1); + QCOMPARE(ms.count(), 2); + QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(ms.at(0).value(0)), QMediaPlayer::InvalidMedia); + QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(ms.at(1).value(0)), QMediaPlayer::LoadingMedia); // NOTE: status should begin transitioning through to BufferedMedia. QCOMPARE(player->currentMedia(), content1); @@ -1210,5 +1217,84 @@ void tst_QMediaPlayer::testSupportedMimeTypes() // This is empty on some platforms, and not on others, so can't test something here at the moment. } +void tst_QMediaPlayer::testQrc_data() +{ + QTest::addColumn<QMediaContent>("mediaContent"); + QTest::addColumn<QMediaPlayer::MediaStatus>("status"); + QTest::addColumn<QMediaPlayer::Error>("error"); + QTest::addColumn<int>("errorCount"); + QTest::addColumn<bool>("hasStreamFeature"); + QTest::addColumn<QString>("backendMediaContentScheme"); + QTest::addColumn<bool>("backendHasStream"); + + QTest::newRow("invalid") << QMediaContent(QUrl(QLatin1String("qrc:/invalid.mp3"))) + << QMediaPlayer::InvalidMedia + << QMediaPlayer::ResourceError + << 1 // error count + << false // No StreamPlayback support + << QString() // backend should not have got any media (empty URL scheme) + << false; // backend should not have got any stream + + QTest::newRow("valid+nostream") << QMediaContent(QUrl(QLatin1String("qrc:/testdata/nokia-tune.mp3"))) + << QMediaPlayer::LoadingMedia + << QMediaPlayer::NoError + << 0 // error count + << false // No StreamPlayback support + << QStringLiteral("file") // backend should have a got a temporary file + << false; // backend should not have got any stream + + QTest::newRow("valid+stream") << QMediaContent(QUrl(QLatin1String("qrc:/testdata/nokia-tune.mp3"))) + << QMediaPlayer::LoadingMedia + << QMediaPlayer::NoError + << 0 // error count + << true // StreamPlayback support + << QStringLiteral("qrc") + << true; // backend should have got a stream (QFile opened from the resource) +} + +void tst_QMediaPlayer::testQrc() +{ + QFETCH(QMediaContent, mediaContent); + QFETCH(QMediaPlayer::MediaStatus, status); + QFETCH(QMediaPlayer::Error, error); + QFETCH(int, errorCount); + QFETCH(bool, hasStreamFeature); + QFETCH(QString, backendMediaContentScheme); + QFETCH(bool, backendHasStream); + + if (hasStreamFeature) + mockProvider->setSupportedFeatures(QMediaServiceProviderHint::StreamPlayback); + + QMediaPlayer player; + + mockService->setState(QMediaPlayer::PlayingState, QMediaPlayer::NoMedia); + + QSignalSpy mediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); + QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); + + player.setMedia(mediaContent); + + QTRY_COMPARE(player.mediaStatus(), status); + QCOMPARE(statusSpy.count(), 1); + QCOMPARE(qvariant_cast<QMediaPlayer::MediaStatus>(statusSpy.last().value(0)), status); + + QCOMPARE(player.media(), mediaContent); + QCOMPARE(player.currentMedia(), mediaContent); + QCOMPARE(mediaSpy.count(), 1); + QCOMPARE(qvariant_cast<QMediaContent>(mediaSpy.last().value(0)), mediaContent); + + QCOMPARE(player.error(), error); + QCOMPARE(errorSpy.count(), errorCount); + if (errorCount > 0) { + QCOMPARE(qvariant_cast<QMediaPlayer::Error>(errorSpy.last().value(0)), error); + QVERIFY(!player.errorString().isEmpty()); + } + + // Check the media actually passed to the backend + QCOMPARE(mockService->mockControl->media().canonicalUrl().scheme(), backendMediaContentScheme); + QCOMPARE(bool(mockService->mockControl->mediaStream()), backendHasStream); +} + QTEST_GUILESS_MAIN(tst_QMediaPlayer) #include "tst_qmediaplayer.moc" diff --git a/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h b/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h index 5b313fb95..5127498bc 100644 --- a/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h +++ b/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h @@ -91,11 +91,10 @@ public: { _stream = stream; _media = content; - if (_state != QMediaPlayer::StoppedState) { - _mediaStatus = _media.isNull() ? QMediaPlayer::NoMedia : QMediaPlayer::LoadingMedia; + _mediaStatus = _media.isNull() ? QMediaPlayer::NoMedia : QMediaPlayer::LoadingMedia; + if (_state != QMediaPlayer::StoppedState) emit stateChanged(_state = QMediaPlayer::StoppedState); - emit mediaStatusChanged(_mediaStatus); - } + emit mediaStatusChanged(_mediaStatus); emit mediaChanged(_media = content); } QIODevice *mediaStream() const { return _stream; } diff --git a/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h b/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h index 60ea672c9..89820c557 100644 --- a/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h +++ b/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h @@ -61,6 +61,16 @@ public: } } + QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *) const + { + return features; + } + + void setSupportedFeatures(QMediaServiceProviderHint::Features f) + { + features = f; + } + QByteArray defaultDevice(const QByteArray &serviceType) const { if (serviceType == Q_MEDIASERVICE_CAMERA) @@ -97,6 +107,7 @@ public: QMediaService *service; bool deleteServiceOnRelease; + QMediaServiceProviderHint::Features features; }; #endif // MOCKMEDIASERVICEPROVIDER_H |