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
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@ private:
|
||||
void pushState();
|
||||
void popAndNotifyState();
|
||||
|
||||
bool m_ownStream;
|
||||
QGstreamerPlayerSession *m_session;
|
||||
QMediaPlayer::State m_userRequestedState;
|
||||
QMediaPlayer::State m_currentState;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,6 @@ private:
|
||||
bool m_inputAttached;
|
||||
int m_stopEventsToIgnore;
|
||||
int m_bufferLevel;
|
||||
QString m_tempMediaFileName;
|
||||
QTimer m_loadingTimer;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user