From 63c53d21bd05465739635ac259441b17f20a37a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Thu, 19 Nov 2015 17:58:10 +0100 Subject: Android: Make the meta-data reader async Reading the meta-data can be slow and cause the UI to become unresponsive. This is especially noticeable when the media source is loaded from a remote location, or over a slow network. To improve this situation the media-reader is moved off the main thread and we wait until the media is loaded before starting the meta-data extraction. Task-number: QTBUG-46491 Change-Id: I0b9cf2ae6b8e08596a2f0b8fa0042d74604c46f9 Reviewed-by: Yoann Lopes --- .../src/mediaplayer/qandroidmediaplayercontrol.cpp | 1 + .../mediaplayer/qandroidmetadatareadercontrol.cpp | 211 ++++++++++++--------- .../mediaplayer/qandroidmetadatareadercontrol.h | 7 +- 3 files changed, 128 insertions(+), 91 deletions(-) (limited to 'src/plugins/android/src/mediaplayer') diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index 90beeabe7..a6258a74d 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -563,6 +563,7 @@ void QAndroidMediaPlayerControl::onStateChanged(qint32 state) } else { onBufferingChanged(100); } + Q_EMIT metaDataUpdated(); setAudioAvailable(true); flushPendingStates(); break; diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index d09a7734f..fbe6a0513 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -63,147 +65,180 @@ static const char* qt_ID3GenreNames[] = "Euro-House", "Dance Hall" }; +typedef QVector AndroidMetaDataReaders; +Q_GLOBAL_STATIC(AndroidMetaDataReaders, g_metaDataReaders) +Q_GLOBAL_STATIC(QMutex, g_metaDataReadersMtx) + QAndroidMetaDataReaderControl::QAndroidMetaDataReaderControl(QObject *parent) : QMetaDataReaderControl(parent) , m_available(false) - , m_retriever(new AndroidMediaMetadataRetriever) { } QAndroidMetaDataReaderControl::~QAndroidMetaDataReaderControl() { - if (m_retriever) { - m_retriever->release(); - delete m_retriever; - } + QMutexLocker l(g_metaDataReadersMtx); + const int idx = g_metaDataReaders->indexOf(this); + if (idx != -1) + g_metaDataReaders->remove(idx); } bool QAndroidMetaDataReaderControl::isMetaDataAvailable() const { - return m_available; + const QMutexLocker l(&m_mtx); + return m_available && !m_metadata.isEmpty(); } QVariant QAndroidMetaDataReaderControl::metaData(const QString &key) const { + const QMutexLocker l(&m_mtx); return m_metadata.value(key); } QStringList QAndroidMetaDataReaderControl::availableMetaData() const { + const QMutexLocker l(&m_mtx); return m_metadata.keys(); } void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media) { - if (!m_retriever) - return; - + const QMutexLocker l(&m_mtx); + m_metadata.clear(); m_mediaContent = media; - updateData(); } void QAndroidMetaDataReaderControl::onUpdateMetaData() { - if (!m_retriever || m_mediaContent.isNull()) + { + const QMutexLocker l(g_metaDataReadersMtx); + if (!g_metaDataReaders->contains(this)) + g_metaDataReaders->append(this); + } + + const QMutexLocker ml(&m_mtx); + if (m_mediaContent.isNull()) return; - updateData(); + const QUrl &url = m_mediaContent.canonicalUrl(); + QtConcurrent::run(&extractMetadata, this, url); } -void QAndroidMetaDataReaderControl::updateData() +void QAndroidMetaDataReaderControl::updateData(const QVariantMap &metadata, const QUrl &url) { - m_metadata.clear(); + const QMutexLocker l(&m_mtx); - 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); + if (m_mediaContent.canonicalUrl() != url) + return; - bool isVideo = !m_retriever->extractMetadata(AndroidMediaMetadataRetriever::HasVideo).isNull() - || mimeType.startsWith(QStringLiteral("video")); + const bool oldAvailable = m_available; + m_metadata = metadata; + m_available = !m_metadata.isEmpty(); - QString string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Album); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::AlbumTitle, string); + if (m_available != oldAvailable) + Q_EMIT metaDataAvailableChanged(m_available); - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::AlbumArtist); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::AlbumArtist, string); + Q_EMIT metaDataChanged(); +} - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Artist); - if (!string.isNull()) { - m_metadata.insert(isVideo ? QMediaMetaData::LeadPerformer - : QMediaMetaData::ContributingArtist, - string.split('/', QString::SkipEmptyParts)); - } +void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderControl *caller, + const QUrl &url) +{ + QVariantMap metadata; - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Author); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts)); + if (!url.isEmpty()) { + AndroidMediaMetadataRetriever retriever; + if (!retriever.setDataSource(url)) + return; - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Bitrate); - if (!string.isNull()) { - m_metadata.insert(isVideo ? QMediaMetaData::VideoBitRate - : QMediaMetaData::AudioBitRate, - string.toInt()); - } + QString mimeType = retriever.extractMetadata(AndroidMediaMetadataRetriever::MimeType); + if (!mimeType.isNull()) + metadata.insert(QMediaMetaData::MediaType, mimeType); - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::CDTrackNumber); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::TrackNumber, string.toInt()); - - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Composer); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Composer, string.split('/', QString::SkipEmptyParts)); - - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Date); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date()); - - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Duration); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Duration, string.toLongLong()); - - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Genre); - if (!string.isNull()) { - // The genre can be returned as an ID3v2 id, get the name for it in that case - if (string.startsWith('(') && string.endsWith(')')) { - bool ok = false; - int genreId = string.midRef(1, string.length() - 2).toInt(&ok); - if (ok && genreId >= 0 && genreId <= 125) - string = QLatin1String(qt_ID3GenreNames[genreId]); - } - m_metadata.insert(QMediaMetaData::Genre, string); - } + bool isVideo = !retriever.extractMetadata(AndroidMediaMetadataRetriever::HasVideo).isNull() + || mimeType.startsWith(QStringLiteral("video")); + + QString string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Album); + if (!string.isNull()) + metadata.insert(QMediaMetaData::AlbumTitle, string); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::AlbumArtist); + if (!string.isNull()) + metadata.insert(QMediaMetaData::AlbumArtist, string); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Artist); + if (!string.isNull()) { + metadata.insert(isVideo ? QMediaMetaData::LeadPerformer + : QMediaMetaData::ContributingArtist, + string.split('/', QString::SkipEmptyParts)); + } + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts)); - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Title); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Title, string); + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate); + if (!string.isNull()) { + metadata.insert(isVideo ? QMediaMetaData::VideoBitRate + : QMediaMetaData::AudioBitRate, + string.toInt()); + } - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::VideoHeight); - if (!string.isNull()) { - int height = string.toInt(); - int width = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::VideoWidth).toInt(); - m_metadata.insert(QMediaMetaData::Resolution, QSize(width, height)); + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::CDTrackNumber); + if (!string.isNull()) + metadata.insert(QMediaMetaData::TrackNumber, string.toInt()); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Composer); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Composer, string.split('/', QString::SkipEmptyParts)); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Date); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date()); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Duration); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Duration, string.toLongLong()); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Genre); + if (!string.isNull()) { + // The genre can be returned as an ID3v2 id, get the name for it in that case + if (string.startsWith('(') && string.endsWith(')')) { + bool ok = false; + const int genreId = string.midRef(1, string.length() - 2).toInt(&ok); + if (ok && genreId >= 0 && genreId <= 125) + string = QLatin1String(qt_ID3GenreNames[genreId]); } + metadata.insert(QMediaMetaData::Genre, string); + } - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Writer); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Writer, string.split('/', QString::SkipEmptyParts)); + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Title); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Title, string); - string = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Year); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Year, string.toInt()); + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoHeight); + if (!string.isNull()) { + const int height = string.toInt(); + const int width = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoWidth).toInt(); + metadata.insert(QMediaMetaData::Resolution, QSize(width, height)); } + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Writer); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Writer, string.split('/', QString::SkipEmptyParts)); + + string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Year); + if (!string.isNull()) + metadata.insert(QMediaMetaData::Year, string.toInt()); + + retriever.release(); } - bool oldAvailable = m_available; - m_available = !m_metadata.isEmpty(); - if (m_available != oldAvailable) - Q_EMIT metaDataAvailableChanged(m_available); + const QMutexLocker lock(g_metaDataReadersMtx); + if (!g_metaDataReaders->contains(caller)) + return; - Q_EMIT metaDataChanged(); + caller->updateData(metadata, url); } QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h index 14fb01ea0..e2e668d5c 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h @@ -36,6 +36,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -58,13 +59,13 @@ public Q_SLOTS: void onUpdateMetaData(); private: - void updateData(); + void updateData(const QVariantMap &metadata, const QUrl &url); + static void extractMetadata(QAndroidMetaDataReaderControl *caller, const QUrl &url); + mutable QMutex m_mtx; QMediaContent m_mediaContent; bool m_available; QVariantMap m_metadata; - - AndroidMediaMetadataRetriever *m_retriever; }; QT_END_NAMESPACE -- cgit v1.2.3