summaryrefslogtreecommitdiffstats
path: root/src/multimedia/playback/qmediaplayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/playback/qmediaplayer.cpp')
-rw-r--r--src/multimedia/playback/qmediaplayer.cpp157
1 files changed, 125 insertions, 32 deletions
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);
}
}