From 341b86c63fbe9e9f284e2d6547cb639f487a2ec4 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 17 Jul 2014 18:41:44 +0200 Subject: 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 --- .../src/mediaplayer/qandroidmediaplayercontrol.cpp | 4 +- .../src/mediaplayer/qandroidmediaplayercontrol.h | 1 + .../src/mediaplayer/qandroidmediaservice.cpp | 4 +- .../mediaplayer/qandroidmetadatareadercontrol.cpp | 10 +- .../mediaplayer/qandroidmetadatareadercontrol.h | 4 +- .../wrappers/jni/androidmediametadataretriever.cpp | 133 +++++++++++++++------ .../wrappers/jni/androidmediametadataretriever.h | 4 +- 7 files changed, 113 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index 6817d65b0..90efcc503 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -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(); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index 5744c11b8..1f61809cd 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -80,6 +80,7 @@ public: Q_SIGNALS: void metaDataUpdated(); + void actualMediaLocationChanged(const QString &url); public Q_SLOTS: void setPosition(qint64 position) Q_DECL_OVERRIDE; diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 175958676..c6a7d3c39 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -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())); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index 82bd74997..7f68bc13c 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -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); diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h index 67b92f1eb..a8f1d92f2 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h @@ -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; diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp index 7dfc6a6e3..83f12cb8c 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp @@ -43,9 +43,24 @@ #include #include +#include +#include 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("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 fd = fileInputStream.callObjectMethod("getFD", + "()Ljava/io/FileDescriptor;"); + if (exceptionCheckAndClear(env)) { + fileInputStream.callMethod("close"); + 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(); - } else { m_metadataRetriever.callMethod("setDataSource", - "(Landroid/content/Context;Landroid/net/Uri;)V", - QtAndroidPrivate::activity(), - uri.object()); - if (env->ExceptionCheck()) - env->ExceptionClear(); - else - loaded = true; - } + "(Ljava/io/FileDescriptor;)V", + fd.object()); + + bool ok = !exceptionCheckAndClear(env); + + fileInputStream.callMethod("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("close"); + exceptionCheckAndClear(env); + return false; + } - return loaded; -} + m_metadataRetriever.callMethod("setDataSource", + "(Ljava/io/FileDescriptor;JJ)V", + fd.object(), + assetFd.callMethod("getStartOffset"), + assetFd.callMethod("getLength")); -bool AndroidMediaMetadataRetriever::setDataSource(const QString &path) -{ - if (!m_metadataRetriever.isValid()) - return false; + bool ok = !exceptionCheckAndClear(env); - QJNIEnvironmentPrivate env; + assetFd.callMethod("close"); + exceptionCheckAndClear(env); - bool loaded = false; + if (!ok) + return false; + } else if (QtAndroidPrivate::androidSdkVersion() >= 14) { + // On API levels >= 14, only setDataSource(String, Map) accepts remote media + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate hash("java/util/HashMap"); - m_metadataRetriever.callMethod("setDataSource", - "(Ljava/lang/String;)V", - QJNIObjectPrivate::fromString(path).object()); - if (env->ExceptionCheck()) - env->ExceptionClear(); - else - loaded = true; + m_metadataRetriever.callMethod("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("setDataSource", + "(Landroid/content/Context;Landroid/net/Uri;)V", + QtAndroidPrivate::activity(), + uri.object()); + if (exceptionCheckAndClear(env)) + return false; + } - return loaded; + return true; } QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h index f18cec11d..1a4a876ee 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h @@ -43,7 +43,6 @@ #define ANDROIDMEDIAMETADATARETRIEVER_H #include -#include 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; -- cgit v1.2.3