Android: fix retrieving metadata from assets, qrc and remote files.
We need the same logic as for the media player: local files and assets must be loaded with a FileDescriptor. Because of a bug in Android API level >= 14, remote files have to be loaded in different ways depending on the version. Task-number: QTBUG-40274 Change-Id: I6411b959064d22219cf981a4dc8f4f26cf16f65f Reviewed-by: Christian Stromme <christian.stromme@digia.com>
This commit is contained in:
@@ -325,8 +325,10 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
|
||||
mMediaPlayer->setDataSource(mediaPath);
|
||||
mMediaPlayer->prepareAsync();
|
||||
|
||||
if (!reloading)
|
||||
if (!reloading) {
|
||||
Q_EMIT mediaChanged(mMediaContent);
|
||||
Q_EMIT actualMediaLocationChanged(mediaPath);
|
||||
}
|
||||
|
||||
resetBufferingProgress();
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void metaDataUpdated();
|
||||
void actualMediaLocationChanged(const QString &url);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setPosition(qint64 position) Q_DECL_OVERRIDE;
|
||||
|
||||
@@ -53,8 +53,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent)
|
||||
{
|
||||
mMediaControl = new QAndroidMediaPlayerControl;
|
||||
mMetadataControl = new QAndroidMetaDataReaderControl;
|
||||
connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)),
|
||||
mMetadataControl, SLOT(onMediaChanged(QMediaContent)));
|
||||
connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)),
|
||||
mMetadataControl, SLOT(onMediaChanged(QString)));
|
||||
connect(mMediaControl, SIGNAL(metaDataUpdated()),
|
||||
mMetadataControl, SLOT(onUpdateMetaData()));
|
||||
}
|
||||
|
||||
@@ -101,18 +101,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const
|
||||
return m_metadata.keys();
|
||||
}
|
||||
|
||||
void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
|
||||
void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url)
|
||||
{
|
||||
if (!m_retriever)
|
||||
return;
|
||||
|
||||
m_mediaContent = media;
|
||||
m_mediaLocation = url;
|
||||
updateData();
|
||||
}
|
||||
|
||||
void QAndroidMetaDataReaderControl::onUpdateMetaData()
|
||||
{
|
||||
if (!m_retriever || m_mediaContent.isNull())
|
||||
if (!m_retriever || m_mediaLocation.isEmpty())
|
||||
return;
|
||||
|
||||
updateData();
|
||||
@@ -122,8 +122,8 @@ void QAndroidMetaDataReaderControl::updateData()
|
||||
{
|
||||
m_metadata.clear();
|
||||
|
||||
if (!m_mediaContent.isNull()) {
|
||||
if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) {
|
||||
if (!m_mediaLocation.isEmpty()) {
|
||||
if (m_retriever->setDataSource(m_mediaLocation)) {
|
||||
QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType);
|
||||
if (!mimeType.isNull())
|
||||
m_metadata.insert(QMediaMetaData::MediaType, mimeType);
|
||||
|
||||
@@ -62,13 +62,13 @@ public:
|
||||
QStringList availableMetaData() const Q_DECL_OVERRIDE;
|
||||
|
||||
public Q_SLOTS:
|
||||
void onMediaChanged(const QMediaContent &media);
|
||||
void onMediaChanged(const QString &url);
|
||||
void onUpdateMetaData();
|
||||
|
||||
private:
|
||||
void updateData();
|
||||
|
||||
QMediaContent m_mediaContent;
|
||||
QString m_mediaLocation;
|
||||
bool m_available;
|
||||
QVariantMap m_metadata;
|
||||
|
||||
|
||||
@@ -43,9 +43,24 @@
|
||||
|
||||
#include <QtCore/private/qjnihelpers_p.h>
|
||||
#include <QtCore/private/qjni_p.h>
|
||||
#include <QtCore/QUrl>
|
||||
#include <qdebug.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static bool exceptionCheckAndClear(JNIEnv *env)
|
||||
{
|
||||
if (Q_UNLIKELY(env->ExceptionCheck())) {
|
||||
#ifdef QT_DEBUG
|
||||
env->ExceptionDescribe();
|
||||
#endif // QT_DEBUG
|
||||
env->ExceptionClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever()
|
||||
{
|
||||
m_metadataRetriever = QJNIObjectPrivate("android/media/MediaMetadataRetriever");
|
||||
@@ -76,55 +91,105 @@ void AndroidMediaMetadataRetriever::release()
|
||||
m_metadataRetriever.callMethod<void>("release");
|
||||
}
|
||||
|
||||
bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
|
||||
bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString)
|
||||
{
|
||||
if (!m_metadataRetriever.isValid())
|
||||
return false;
|
||||
|
||||
QJNIEnvironmentPrivate env;
|
||||
QUrl url(urlString);
|
||||
|
||||
bool loaded = false;
|
||||
if (url.isLocalFile()) { // also includes qrc files (copied to a temp file)
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path());
|
||||
QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
|
||||
"(Ljava/lang/String;)V",
|
||||
string.object());
|
||||
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
|
||||
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
|
||||
"parse",
|
||||
"(Ljava/lang/String;)Landroid/net/Uri;",
|
||||
string.object());
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionClear();
|
||||
QJNIObjectPrivate fd = fileInputStream.callObjectMethod("getFD",
|
||||
"()Ljava/io/FileDescriptor;");
|
||||
if (exceptionCheckAndClear(env)) {
|
||||
fileInputStream.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/io/FileDescriptor;)V",
|
||||
fd.object());
|
||||
|
||||
bool ok = !exceptionCheckAndClear(env);
|
||||
|
||||
fileInputStream.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
} else if (url.scheme() == QLatin1String("assets")) {
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path().mid(1)); // remove first '/'
|
||||
QJNIObjectPrivate activity(QtAndroidPrivate::activity());
|
||||
QJNIObjectPrivate assetManager = activity.callObjectMethod("getAssets",
|
||||
"()Landroid/content/res/AssetManager;");
|
||||
QJNIObjectPrivate assetFd = assetManager.callObjectMethod("openFd",
|
||||
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;",
|
||||
string.object());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
|
||||
QJNIObjectPrivate fd = assetFd.callObjectMethod("getFileDescriptor",
|
||||
"()Ljava/io/FileDescriptor;");
|
||||
if (exceptionCheckAndClear(env)) {
|
||||
assetFd.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/io/FileDescriptor;JJ)V",
|
||||
fd.object(),
|
||||
assetFd.callMethod<jlong>("getStartOffset"),
|
||||
assetFd.callMethod<jlong>("getLength"));
|
||||
|
||||
bool ok = !exceptionCheckAndClear(env);
|
||||
|
||||
assetFd.callMethod<void>("close");
|
||||
exceptionCheckAndClear(env);
|
||||
|
||||
if (!ok)
|
||||
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 hash("java/util/HashMap");
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/lang/String;Ljava/util/Map;)V",
|
||||
string.object(),
|
||||
hash.object());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
} else {
|
||||
// While on API levels < 14, only setDataSource(Context, Uri) is available and works for
|
||||
// remote media...
|
||||
QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString);
|
||||
QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri",
|
||||
"parse",
|
||||
"(Ljava/lang/String;)Landroid/net/Uri;",
|
||||
string.object());
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Landroid/content/Context;Landroid/net/Uri;)V",
|
||||
QtAndroidPrivate::activity(),
|
||||
uri.object());
|
||||
if (env->ExceptionCheck())
|
||||
env->ExceptionClear();
|
||||
else
|
||||
loaded = true;
|
||||
if (exceptionCheckAndClear(env))
|
||||
return false;
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool AndroidMediaMetadataRetriever::setDataSource(const QString &path)
|
||||
{
|
||||
if (!m_metadataRetriever.isValid())
|
||||
return false;
|
||||
|
||||
QJNIEnvironmentPrivate env;
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
m_metadataRetriever.callMethod<void>("setDataSource",
|
||||
"(Ljava/lang/String;)V",
|
||||
QJNIObjectPrivate::fromString(path).object());
|
||||
if (env->ExceptionCheck())
|
||||
env->ExceptionClear();
|
||||
else
|
||||
loaded = true;
|
||||
|
||||
return loaded;
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
#define ANDROIDMEDIAMETADATARETRIEVER_H
|
||||
|
||||
#include <QtCore/private/qjni_p.h>
|
||||
#include <qurl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -81,8 +80,7 @@ public:
|
||||
|
||||
QString extractMetadata(MetadataKey key);
|
||||
void release();
|
||||
bool setDataSource(const QUrl &url);
|
||||
bool setDataSource(const QString &path);
|
||||
bool setDataSource(const QString &url);
|
||||
|
||||
private:
|
||||
QJNIObjectPrivate m_metadataRetriever;
|
||||
|
||||
Reference in New Issue
Block a user