summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/multimedia/controls/qmediaplayercontrol.cpp5
-rw-r--r--src/multimedia/playback/qmediaplayer.cpp157
-rw-r--r--src/multimedia/playback/qmediaplayer.h1
-rw-r--r--src/multimedia/qmediaserviceprovider.cpp52
-rw-r--r--src/multimedia/qmediaserviceprovider_p.h2
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp18
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h3
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp4
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp10
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h4
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp9
-rw-r--r--src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h2
-rw-r--r--src/plugins/directshow/player/directshowiosource.cpp18
-rw-r--r--src/plugins/directshow/player/directshowiosource.h11
-rw-r--r--src/plugins/directshow/player/directshowplayerservice.cpp9
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp26
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h1
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp22
-rw-r--r--src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h1
-rw-r--r--tests/auto/unit/qmediaplayer/qmediaplayer.pro1
-rw-r--r--tests/auto/unit/qmediaplayer/testdata.qrc5
-rw-r--r--tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3bin0 -> 62715 bytes
-rw-r--r--tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp90
-rw-r--r--tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h7
-rw-r--r--tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h11
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
new file mode 100644
index 000000000..2435f65b8
--- /dev/null
+++ b/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3
Binary files differ
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