QMediaPlayer: handle resource files in a cross-platform way.
It was the backend's responsibility to handle resource files in an appropriate way. In practice, it was either not handled at all, or implemented in an almost identical manner in every backend that does handle it. This is now dealt with in QMediaPlayer, always passing to the backend something it will be able to play. If the backend has the StreamPlayback capability, we pass a QFile from which it streams the data. If it doesn't, we copy the resource to a temporary file and pass its path to the backend. Task-number: QTBUG-36175 Task-number: QTBUG-42263 Task-number: QTBUG-43839 Change-Id: I57b355c72692d02661baeaf74e66581ca0a0bd1d Reviewed-by: Andrew Knight <qt@panimo.net> Reviewed-by: Peng Wu <peng.wu@intopalo.com> Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
committed by
Friedemann Kleint
parent
4d17db19f8
commit
63cff37741
@@ -315,6 +315,11 @@ QMediaPlayerControl::QMediaPlayerControl(QObject *parent):
|
|||||||
Setting the media to a null QMediaContent will cause the control to discard all
|
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
|
information relating to the current media source and to cease all I/O operations related
|
||||||
to that media.
|
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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
#include <QtCore/qtimer.h>
|
#include <QtCore/qtimer.h>
|
||||||
#include <QtCore/qdebug.h>
|
#include <QtCore/qdebug.h>
|
||||||
#include <QtCore/qpointer.h>
|
#include <QtCore/qpointer.h>
|
||||||
|
#include <QtCore/qfileinfo.h>
|
||||||
|
#include <QtCore/qtemporaryfile.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -103,22 +105,30 @@ public:
|
|||||||
: provider(0)
|
: provider(0)
|
||||||
, control(0)
|
, control(0)
|
||||||
, state(QMediaPlayer::StoppedState)
|
, state(QMediaPlayer::StoppedState)
|
||||||
|
, status(QMediaPlayer::UnknownMediaStatus)
|
||||||
, error(QMediaPlayer::NoError)
|
, error(QMediaPlayer::NoError)
|
||||||
|
, ignoreNextStatusChange(-1)
|
||||||
, playlist(0)
|
, playlist(0)
|
||||||
, networkAccessControl(0)
|
, networkAccessControl(0)
|
||||||
|
, hasStreamPlaybackFeature(false)
|
||||||
, nestedPlaylists(0)
|
, nestedPlaylists(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
QMediaServiceProvider *provider;
|
QMediaServiceProvider *provider;
|
||||||
QMediaPlayerControl* control;
|
QMediaPlayerControl* control;
|
||||||
QMediaPlayer::State state;
|
QMediaPlayer::State state;
|
||||||
|
QMediaPlayer::MediaStatus status;
|
||||||
QMediaPlayer::Error error;
|
QMediaPlayer::Error error;
|
||||||
QString errorString;
|
QString errorString;
|
||||||
|
int ignoreNextStatusChange;
|
||||||
|
|
||||||
QPointer<QObject> videoOutput;
|
QPointer<QObject> videoOutput;
|
||||||
QMediaPlaylist *playlist;
|
QMediaPlaylist *playlist;
|
||||||
QMediaNetworkAccessControl *networkAccessControl;
|
QMediaNetworkAccessControl *networkAccessControl;
|
||||||
QVideoSurfaceOutput surfaceOutput;
|
QVideoSurfaceOutput surfaceOutput;
|
||||||
|
bool hasStreamPlaybackFeature;
|
||||||
|
QMediaContent qrcMedia;
|
||||||
|
QScopedPointer<QFile> qrcFile;
|
||||||
|
|
||||||
QMediaContent rootMedia;
|
QMediaContent rootMedia;
|
||||||
QMediaContent pendingPlaylist;
|
QMediaContent pendingPlaylist;
|
||||||
@@ -126,6 +136,8 @@ public:
|
|||||||
bool isInChain(QUrl url);
|
bool isInChain(QUrl url);
|
||||||
int nestedPlaylists;
|
int nestedPlaylists;
|
||||||
|
|
||||||
|
void setMedia(const QMediaContent &media, QIODevice *stream = 0);
|
||||||
|
|
||||||
void setPlaylist(QMediaPlaylist *playlist);
|
void setPlaylist(QMediaPlaylist *playlist);
|
||||||
void setPlaylistMedia();
|
void setPlaylistMedia();
|
||||||
void loadPlaylist();
|
void loadPlaylist();
|
||||||
@@ -137,6 +149,7 @@ public:
|
|||||||
void _q_error(int error, const QString &errorString);
|
void _q_error(int error, const QString &errorString);
|
||||||
void _q_updateMedia(const QMediaContent&);
|
void _q_updateMedia(const QMediaContent&);
|
||||||
void _q_playlistDestroyed();
|
void _q_playlistDestroyed();
|
||||||
|
void _q_handleMediaChanged(const QMediaContent&);
|
||||||
void _q_handlePlaylistLoaded();
|
void _q_handlePlaylistLoaded();
|
||||||
void _q_handlePlaylistLoadFailed();
|
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);
|
Q_Q(QMediaPlayer);
|
||||||
|
|
||||||
switch (status) {
|
if (int(s) == ignoreNextStatusChange) {
|
||||||
case QMediaPlayer::StalledMedia:
|
ignoreNextStatusChange = -1;
|
||||||
case QMediaPlayer::BufferingMedia:
|
return;
|
||||||
q->addPropertyWatch("bufferStatus");
|
|
||||||
emit q->mediaStatusChanged(status);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
q->removePropertyWatch("bufferStatus");
|
|
||||||
emit q->mediaStatusChanged(status);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
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;
|
const QMediaPlayer::State currentState = state;
|
||||||
|
|
||||||
control->setMedia(media, 0);
|
setMedia(media, 0);
|
||||||
|
|
||||||
if (!media.isNull()) {
|
if (!media.isNull()) {
|
||||||
switch (currentState) {
|
switch (currentState) {
|
||||||
@@ -297,11 +318,76 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media)
|
|||||||
void QMediaPlayerPrivate::_q_playlistDestroyed()
|
void QMediaPlayerPrivate::_q_playlistDestroyed()
|
||||||
{
|
{
|
||||||
playlist = 0;
|
playlist = 0;
|
||||||
|
setMedia(QMediaContent(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QMediaPlayerPrivate::setMedia(const QMediaContent &media, QIODevice *stream)
|
||||||
|
{
|
||||||
|
Q_Q(QMediaPlayer);
|
||||||
|
|
||||||
if (!control)
|
if (!control)
|
||||||
return;
|
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)
|
void QMediaPlayerPrivate::setPlaylist(QMediaPlaylist *pls)
|
||||||
@@ -333,7 +419,7 @@ void QMediaPlayerPrivate::setPlaylistMedia()
|
|||||||
playlist->next();
|
playlist->next();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (control != 0) {
|
} else {
|
||||||
// If we've just switched to a new playlist,
|
// If we've just switched to a new playlist,
|
||||||
// then last emitted currentMediaChanged was a playlist.
|
// then last emitted currentMediaChanged was a playlist.
|
||||||
// Make sure we emit currentMediaChanged if new playlist has
|
// Make sure we emit currentMediaChanged if new playlist has
|
||||||
@@ -344,14 +430,14 @@ void QMediaPlayerPrivate::setPlaylistMedia()
|
|||||||
// test.wav -- processed by backend,
|
// test.wav -- processed by backend,
|
||||||
// media is not changed,
|
// media is not changed,
|
||||||
// frontend needs to emit currentMediaChanged
|
// frontend needs to emit currentMediaChanged
|
||||||
bool isSameMedia = (control->media() == playlist->currentMedia());
|
bool isSameMedia = (q->currentMedia() == playlist->currentMedia());
|
||||||
control->setMedia(playlist->currentMedia(), 0);
|
setMedia(playlist->currentMedia(), 0);
|
||||||
if (isSameMedia) {
|
if (isSameMedia) {
|
||||||
emit q->currentMediaChanged(control->media());
|
emit q->currentMediaChanged(q->currentMedia());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
q->setMedia(QMediaContent(), 0);
|
setMedia(QMediaContent(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,7 +527,7 @@ void QMediaPlayerPrivate::_q_handlePlaylistLoadFailed()
|
|||||||
if (playlist)
|
if (playlist)
|
||||||
playlist->next();
|
playlist->next();
|
||||||
else
|
else
|
||||||
control->setMedia(QMediaContent(), 0);
|
setMedia(QMediaContent(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QMediaService *playerService(QMediaPlayer::Flags flags)
|
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->control = qobject_cast<QMediaPlayerControl*>(d->service->requestControl(QMediaPlayerControl_iid));
|
||||||
d->networkAccessControl = qobject_cast<QMediaNetworkAccessControl*>(d->service->requestControl(QMediaNetworkAccessControl_iid));
|
d->networkAccessControl = qobject_cast<QMediaNetworkAccessControl*>(d->service->requestControl(QMediaNetworkAccessControl_iid));
|
||||||
if (d->control != 0) {
|
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(stateChanged(QMediaPlayer::State)), SLOT(_q_stateChanged(QMediaPlayer::State)));
|
||||||
connect(d->control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
|
connect(d->control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
|
||||||
SLOT(_q_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(playbackRateChanged(qreal)), SIGNAL(playbackRateChanged(qreal)));
|
||||||
connect(d->control, SIGNAL(bufferStatusChanged(int)), SIGNAL(bufferStatusChanged(int)));
|
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");
|
addPropertyWatch("position");
|
||||||
|
|
||||||
if (d->control->mediaStatus() == StalledMedia || d->control->mediaStatus() == BufferingMedia)
|
if (d->status == StalledMedia || d->status == BufferingMedia)
|
||||||
addPropertyWatch("bufferStatus");
|
addPropertyWatch("bufferStatus");
|
||||||
|
|
||||||
|
d->hasStreamPlaybackFeature = d->provider->supportedFeatures(d->service).testFlag(QMediaServiceProviderHint::StreamPlayback);
|
||||||
}
|
}
|
||||||
if (d->networkAccessControl != 0) {
|
if (d->networkAccessControl != 0) {
|
||||||
connect(d->networkAccessControl, SIGNAL(configurationChanged(QNetworkConfiguration)),
|
connect(d->networkAccessControl, SIGNAL(configurationChanged(QNetworkConfiguration)),
|
||||||
@@ -549,7 +640,9 @@ const QIODevice *QMediaPlayer::mediaStream() const
|
|||||||
{
|
{
|
||||||
Q_D(const QMediaPlayer);
|
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 d->control->mediaStream();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -566,7 +659,12 @@ QMediaContent QMediaPlayer::currentMedia() const
|
|||||||
{
|
{
|
||||||
Q_D(const QMediaPlayer);
|
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 d->control->media();
|
||||||
|
|
||||||
return QMediaContent();
|
return QMediaContent();
|
||||||
@@ -600,12 +698,7 @@ QMediaPlayer::State QMediaPlayer::state() const
|
|||||||
|
|
||||||
QMediaPlayer::MediaStatus QMediaPlayer::mediaStatus() const
|
QMediaPlayer::MediaStatus QMediaPlayer::mediaStatus() const
|
||||||
{
|
{
|
||||||
Q_D(const QMediaPlayer);
|
return d_func()->status;
|
||||||
|
|
||||||
if (d->control != 0)
|
|
||||||
return d->control->mediaStatus();
|
|
||||||
|
|
||||||
return QMediaPlayer::UnknownMediaStatus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 QMediaPlayer::duration() const
|
qint64 QMediaPlayer::duration() const
|
||||||
@@ -877,8 +970,8 @@ void QMediaPlayer::setMedia(const QMediaContent &media, QIODevice *stream)
|
|||||||
// reset playlist to the 1st item
|
// reset playlist to the 1st item
|
||||||
media.playlist()->setCurrentIndex(0);
|
media.playlist()->setCurrentIndex(0);
|
||||||
d->setPlaylist(media.playlist());
|
d->setPlaylist(media.playlist());
|
||||||
} else if (d->control != 0) {
|
} else {
|
||||||
d->control->setMedia(media, stream);
|
d->setMedia(media, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ private:
|
|||||||
Q_PRIVATE_SLOT(d_func(), void _q_error(int, const QString &))
|
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_updateMedia(const QMediaContent&))
|
||||||
Q_PRIVATE_SLOT(d_func(), void _q_playlistDestroyed())
|
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_handlePlaylistLoaded())
|
||||||
Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoadFailed())
|
Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoadFailed())
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -299,7 +299,14 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, loader,
|
|||||||
|
|
||||||
class QPluginServiceProvider : public QMediaServiceProvider
|
class QPluginServiceProvider : public QMediaServiceProvider
|
||||||
{
|
{
|
||||||
QMap<QMediaService*, QMediaServiceProviderPlugin*> pluginMap;
|
struct MediaServiceData {
|
||||||
|
QByteArray type;
|
||||||
|
QMediaServiceProviderPlugin *plugin;
|
||||||
|
|
||||||
|
MediaServiceData() : plugin(0) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
QMap<const QMediaService*, MediaServiceData> mediaServiceData;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint)
|
QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint)
|
||||||
@@ -416,8 +423,12 @@ public:
|
|||||||
|
|
||||||
if (plugin != 0) {
|
if (plugin != 0) {
|
||||||
QMediaService *service = plugin->create(key);
|
QMediaService *service = plugin->create(key);
|
||||||
if (service != 0)
|
if (service != 0) {
|
||||||
pluginMap.insert(service, plugin);
|
MediaServiceData d;
|
||||||
|
d.type = type;
|
||||||
|
d.plugin = plugin;
|
||||||
|
mediaServiceData.insert(service, d);
|
||||||
|
}
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
@@ -430,13 +441,30 @@ public:
|
|||||||
void releaseService(QMediaService *service)
|
void releaseService(QMediaService *service)
|
||||||
{
|
{
|
||||||
if (service != 0) {
|
if (service != 0) {
|
||||||
QMediaServiceProviderPlugin *plugin = pluginMap.take(service);
|
MediaServiceData d = mediaServiceData.take(service);
|
||||||
|
|
||||||
if (plugin != 0)
|
if (d.plugin != 0)
|
||||||
plugin->release(service);
|
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 (iface)
|
||||||
|
return iface->supportedFeatures(d.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QMediaServiceProviderHint::Features();
|
||||||
|
}
|
||||||
|
|
||||||
QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType,
|
QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType,
|
||||||
const QString &mimeType,
|
const QString &mimeType,
|
||||||
const QStringList& codecs,
|
const QStringList& codecs,
|
||||||
@@ -660,6 +688,18 @@ Q_GLOBAL_STATIC(QPluginServiceProvider, pluginProvider);
|
|||||||
Releases a media \a service requested with requestService().
|
Releases a media \a service requested with requestService().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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
|
\fn QMultimedia::SupportEstimate QMediaServiceProvider::hasSupport(const QByteArray &serviceType, const QString &mimeType, const QStringList& codecs, int flags) const
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public:
|
|||||||
virtual QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint = QMediaServiceProviderHint()) = 0;
|
virtual QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint = QMediaServiceProviderHint()) = 0;
|
||||||
virtual void releaseService(QMediaService *service) = 0;
|
virtual void releaseService(QMediaService *service) = 0;
|
||||||
|
|
||||||
|
virtual QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const;
|
||||||
|
|
||||||
virtual QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType,
|
virtual QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType,
|
||||||
const QString &mimeType,
|
const QString &mimeType,
|
||||||
const QStringList& codecs,
|
const QStringList& codecs,
|
||||||
|
|||||||
@@ -318,8 +318,6 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
|
|||||||
if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
|
if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
|
||||||
mMediaPlayer->release();
|
mMediaPlayer->release();
|
||||||
|
|
||||||
QString mediaPath;
|
|
||||||
|
|
||||||
if (mediaContent.isNull()) {
|
if (mediaContent.isNull()) {
|
||||||
setMediaStatus(QMediaPlayer::NoMedia);
|
setMediaStatus(QMediaPlayer::NoMedia);
|
||||||
} else {
|
} else {
|
||||||
@@ -330,29 +328,17 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
|
|||||||
return;
|
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)
|
if (mVideoSize.isValid() && mVideoOutput)
|
||||||
mVideoOutput->setVideoSize(mVideoSize);
|
mVideoOutput->setVideoSize(mVideoSize);
|
||||||
|
|
||||||
if ((mMediaPlayer->display() == 0) && mVideoOutput)
|
if ((mMediaPlayer->display() == 0) && mVideoOutput)
|
||||||
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
|
mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
|
||||||
mMediaPlayer->setDataSource(mediaPath);
|
mMediaPlayer->setDataSource(mediaContent.canonicalUrl().toString(QUrl::FullyEncoded));
|
||||||
mMediaPlayer->prepareAsync();
|
mMediaPlayer->prepareAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mReloadingMedia) {
|
if (!mReloadingMedia)
|
||||||
Q_EMIT mediaChanged(mMediaContent);
|
Q_EMIT mediaChanged(mMediaContent);
|
||||||
Q_EMIT actualMediaLocationChanged(mediaPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetBufferingProgress();
|
resetBufferingProgress();
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
#include <qglobal.h>
|
#include <qglobal.h>
|
||||||
#include <QMediaPlayerControl>
|
#include <QMediaPlayerControl>
|
||||||
#include <qsize.h>
|
#include <qsize.h>
|
||||||
#include <QtCore/QTemporaryFile>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@@ -72,7 +71,6 @@ public:
|
|||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void metaDataUpdated();
|
void metaDataUpdated();
|
||||||
void actualMediaLocationChanged(const QString &url);
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void setPosition(qint64 position) Q_DECL_OVERRIDE;
|
void setPosition(qint64 position) Q_DECL_OVERRIDE;
|
||||||
@@ -112,7 +110,6 @@ private:
|
|||||||
int mPendingVolume;
|
int mPendingVolume;
|
||||||
int mPendingMute;
|
int mPendingMute;
|
||||||
bool mReloadingMedia;
|
bool mReloadingMedia;
|
||||||
QScopedPointer<QTemporaryFile> mTempFile;
|
|
||||||
int mActiveStateChangeNotifiers;
|
int mActiveStateChangeNotifiers;
|
||||||
|
|
||||||
void setState(QMediaPlayer::State state);
|
void setState(QMediaPlayer::State state);
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent)
|
|||||||
{
|
{
|
||||||
mMediaControl = new QAndroidMediaPlayerControl;
|
mMediaControl = new QAndroidMediaPlayerControl;
|
||||||
mMetadataControl = new QAndroidMetaDataReaderControl;
|
mMetadataControl = new QAndroidMetaDataReaderControl;
|
||||||
connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)),
|
connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)),
|
||||||
mMetadataControl, SLOT(onMediaChanged(QString)));
|
mMetadataControl, SLOT(onMediaChanged(QMediaContent)));
|
||||||
connect(mMediaControl, SIGNAL(metaDataUpdated()),
|
connect(mMediaControl, SIGNAL(metaDataUpdated()),
|
||||||
mMetadataControl, SLOT(onUpdateMetaData()));
|
mMetadataControl, SLOT(onUpdateMetaData()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,18 +93,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const
|
|||||||
return m_metadata.keys();
|
return m_metadata.keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url)
|
void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
|
||||||
{
|
{
|
||||||
if (!m_retriever)
|
if (!m_retriever)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_mediaLocation = url;
|
m_mediaContent = media;
|
||||||
updateData();
|
updateData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidMetaDataReaderControl::onUpdateMetaData()
|
void QAndroidMetaDataReaderControl::onUpdateMetaData()
|
||||||
{
|
{
|
||||||
if (!m_retriever || m_mediaLocation.isEmpty())
|
if (!m_retriever || m_mediaContent.isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
updateData();
|
updateData();
|
||||||
@@ -114,8 +114,8 @@ void QAndroidMetaDataReaderControl::updateData()
|
|||||||
{
|
{
|
||||||
m_metadata.clear();
|
m_metadata.clear();
|
||||||
|
|
||||||
if (!m_mediaLocation.isEmpty()) {
|
if (!m_mediaContent.isNull()) {
|
||||||
if (m_retriever->setDataSource(m_mediaLocation)) {
|
if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) {
|
||||||
QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType);
|
QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType);
|
||||||
if (!mimeType.isNull())
|
if (!mimeType.isNull())
|
||||||
m_metadata.insert(QMediaMetaData::MediaType, mimeType);
|
m_metadata.insert(QMediaMetaData::MediaType, mimeType);
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ public:
|
|||||||
QStringList availableMetaData() const Q_DECL_OVERRIDE;
|
QStringList availableMetaData() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void onMediaChanged(const QString &url);
|
void onMediaChanged(const QMediaContent &media);
|
||||||
void onUpdateMetaData();
|
void onUpdateMetaData();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateData();
|
void updateData();
|
||||||
|
|
||||||
QString m_mediaLocation;
|
QMediaContent m_mediaContent;
|
||||||
bool m_available;
|
bool m_available;
|
||||||
QVariantMap m_metadata;
|
QVariantMap m_metadata;
|
||||||
|
|
||||||
|
|||||||
@@ -83,15 +83,14 @@ void AndroidMediaMetadataRetriever::release()
|
|||||||
m_metadataRetriever.callMethod<void>("release");
|
m_metadataRetriever.callMethod<void>("release");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString)
|
bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
|
||||||
{
|
{
|
||||||
if (!m_metadataRetriever.isValid())
|
if (!m_metadataRetriever.isValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QJNIEnvironmentPrivate env;
|
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 string = QJNIObjectPrivate::fromString(url.path());
|
||||||
QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
|
QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
|
||||||
"(Ljava/lang/String;)V",
|
"(Ljava/lang/String;)V",
|
||||||
@@ -153,7 +152,7 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString)
|
|||||||
return false;
|
return false;
|
||||||
} else if (QtAndroidPrivate::androidSdkVersion() >= 14) {
|
} else if (QtAndroidPrivate::androidSdkVersion() >= 14) {
|
||||||
// On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media
|
// 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");
|
QJNIObjectPrivate hash("java/util/HashMap");
|
||||||
|
|
||||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||||
@@ -165,7 +164,7 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString)
|
|||||||
} else {
|
} else {
|
||||||
// While on API levels < 14, only setDataSource(Context, Uri) is available and works for
|
// While on API levels < 14, only setDataSource(Context, Uri) is available and works for
|
||||||
// remote media...
|
// remote media...
|
||||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString);
|
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded));
|
||||||
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
|
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
|
||||||
"parse",
|
"parse",
|
||||||
"(Ljava/lang/String;)Landroid/net/Uri;",
|
"(Ljava/lang/String;)Landroid/net/Uri;",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
|
|
||||||
QString extractMetadata(MetadataKey key);
|
QString extractMetadata(MetadataKey key);
|
||||||
void release();
|
void release();
|
||||||
bool setDataSource(const QString &url);
|
bool setDataSource(const QUrl &url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QJNIObjectPrivate m_metadataRetriever;
|
QJNIObjectPrivate m_metadataRetriever;
|
||||||
|
|||||||
@@ -605,21 +605,3 @@ HRESULT DirectShowIOSource::QueryDirection(PIN_DIRECTION *pPinDir)
|
|||||||
return S_OK;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -127,15 +127,4 @@ private:
|
|||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirectShowRcSource : public DirectShowIOSource
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DirectShowRcSource(DirectShowEventLoop *loop);
|
|
||||||
|
|
||||||
bool open(const QUrl &url);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QFile m_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -289,15 +289,6 @@ void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker)
|
|||||||
fileSource->Release();
|
fileSource->Release();
|
||||||
locker->relock();
|
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)) {
|
if (!SUCCEEDED(hr)) {
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent)
|
QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent)
|
||||||
: QMediaPlayerControl(parent)
|
: QMediaPlayerControl(parent)
|
||||||
, m_ownStream(false)
|
|
||||||
, m_session(session)
|
, m_session(session)
|
||||||
, m_userRequestedState(QMediaPlayer::StoppedState)
|
, m_userRequestedState(QMediaPlayer::StoppedState)
|
||||||
, m_currentState(QMediaPlayer::StoppedState)
|
, m_currentState(QMediaPlayer::StoppedState)
|
||||||
@@ -370,31 +369,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *
|
|||||||
emit bufferStatusChanged(0);
|
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_currentResource = content;
|
||||||
m_stream = stream;
|
m_stream = stream;
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ private:
|
|||||||
void pushState();
|
void pushState();
|
||||||
void popAndNotifyState();
|
void popAndNotifyState();
|
||||||
|
|
||||||
bool m_ownStream;
|
|
||||||
QGstreamerPlayerSession *m_session;
|
QGstreamerPlayerSession *m_session;
|
||||||
QMediaPlayer::State m_userRequestedState;
|
QMediaPlayer::State m_userRequestedState;
|
||||||
QMediaPlayer::State m_currentState;
|
QMediaPlayer::State m_currentState;
|
||||||
|
|||||||
@@ -162,22 +162,6 @@ QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url)
|
|||||||
const QFileInfo fileInfo(relativeFilePath);
|
const QFileInfo fileInfo(relativeFilePath);
|
||||||
return QFile::encodeName(QStringLiteral("file://") + fileInfo.absoluteFilePath());
|
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
|
// HTTP or similar URL
|
||||||
} else {
|
} else {
|
||||||
return url.toEncoded();
|
return url.toEncoded();
|
||||||
@@ -187,7 +171,7 @@ QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url)
|
|||||||
void MmRendererMediaPlayerControl::attach()
|
void MmRendererMediaPlayerControl::attach()
|
||||||
{
|
{
|
||||||
// Should only be called in detached state
|
// 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) {
|
if (m_media.isNull() || !m_context) {
|
||||||
setMediaStatus(QMediaPlayer::NoMedia);
|
setMediaStatus(QMediaPlayer::NoMedia);
|
||||||
@@ -251,10 +235,6 @@ void MmRendererMediaPlayerControl::detach()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_tempMediaFileName.isEmpty()) {
|
|
||||||
QFile::remove(m_tempMediaFileName);
|
|
||||||
m_tempMediaFileName.clear();
|
|
||||||
}
|
|
||||||
m_loadingTimer.stop();
|
m_loadingTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,6 @@ private:
|
|||||||
bool m_inputAttached;
|
bool m_inputAttached;
|
||||||
int m_stopEventsToIgnore;
|
int m_stopEventsToIgnore;
|
||||||
int m_bufferLevel;
|
int m_bufferLevel;
|
||||||
QString m_tempMediaFileName;
|
|
||||||
QTimer m_loadingTimer;
|
QTimer m_loadingTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ CONFIG += testcase no_private_qt_headers_warning
|
|||||||
TARGET = tst_qmediaplayer
|
TARGET = tst_qmediaplayer
|
||||||
QT += network multimedia-private testlib
|
QT += network multimedia-private testlib
|
||||||
SOURCES += tst_qmediaplayer.cpp
|
SOURCES += tst_qmediaplayer.cpp
|
||||||
|
RESOURCES += testdata.qrc
|
||||||
|
|
||||||
include (../qmultimedia_common/mock.pri)
|
include (../qmultimedia_common/mock.pri)
|
||||||
include (../qmultimedia_common/mockplayer.pri)
|
include (../qmultimedia_common/mockplayer.pri)
|
||||||
|
|||||||
5
tests/auto/unit/qmediaplayer/testdata.qrc
Normal file
5
tests/auto/unit/qmediaplayer/testdata.qrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>testdata/nokia-tune.mp3</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
BIN
tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3
vendored
Normal file
BIN
tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3
vendored
Normal file
Binary file not shown.
@@ -133,6 +133,8 @@ private slots:
|
|||||||
void testPlayerFlags();
|
void testPlayerFlags();
|
||||||
void testDestructor();
|
void testDestructor();
|
||||||
void testSupportedMimeTypes();
|
void testSupportedMimeTypes();
|
||||||
|
void testQrc_data();
|
||||||
|
void testQrc();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupCommonTestData();
|
void setupCommonTestData();
|
||||||
@@ -976,12 +978,17 @@ void tst_QMediaPlayer::testPlaylist()
|
|||||||
player->setPlaylist(playlist);
|
player->setPlaylist(playlist);
|
||||||
player->play();
|
player->play();
|
||||||
QCOMPARE(ss.count(), 1);
|
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);
|
mockService->setState(QMediaPlayer::StoppedState, QMediaPlayer::InvalidMedia);
|
||||||
QCOMPARE(player->state(), QMediaPlayer::PlayingState);
|
QCOMPARE(player->state(), QMediaPlayer::PlayingState);
|
||||||
QCOMPARE(player->mediaStatus(), QMediaPlayer::InvalidMedia);
|
QCOMPARE(player->mediaStatus(), QMediaPlayer::LoadingMedia);
|
||||||
QCOMPARE(ss.count(), 1);
|
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.
|
// NOTE: status should begin transitioning through to BufferedMedia.
|
||||||
QCOMPARE(player->currentMedia(), content1);
|
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.
|
// 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)
|
QTEST_GUILESS_MAIN(tst_QMediaPlayer)
|
||||||
#include "tst_qmediaplayer.moc"
|
#include "tst_qmediaplayer.moc"
|
||||||
|
|||||||
@@ -91,11 +91,10 @@ public:
|
|||||||
{
|
{
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
_media = content;
|
_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 stateChanged(_state = QMediaPlayer::StoppedState);
|
||||||
emit mediaStatusChanged(_mediaStatus);
|
emit mediaStatusChanged(_mediaStatus);
|
||||||
}
|
|
||||||
emit mediaChanged(_media = content);
|
emit mediaChanged(_media = content);
|
||||||
}
|
}
|
||||||
QIODevice *mediaStream() const { return _stream; }
|
QIODevice *mediaStream() const { return _stream; }
|
||||||
|
|||||||
@@ -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
|
QByteArray defaultDevice(const QByteArray &serviceType) const
|
||||||
{
|
{
|
||||||
if (serviceType == Q_MEDIASERVICE_CAMERA)
|
if (serviceType == Q_MEDIASERVICE_CAMERA)
|
||||||
@@ -97,6 +107,7 @@ public:
|
|||||||
|
|
||||||
QMediaService *service;
|
QMediaService *service;
|
||||||
bool deleteServiceOnRelease;
|
bool deleteServiceOnRelease;
|
||||||
|
QMediaServiceProviderHint::Features features;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MOCKMEDIASERVICEPROVIDER_H
|
#endif // MOCKMEDIASERVICEPROVIDER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user