diff options
Diffstat (limited to 'src/plugins/android/src/mediaplayer')
9 files changed, 271 insertions, 103 deletions
diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri index c386d996b..9f758a993 100644 --- a/src/plugins/android/src/mediaplayer/mediaplayer.pri +++ b/src/plugins/android/src/mediaplayer/mediaplayer.pri @@ -3,9 +3,11 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qandroidmediaplayercontrol.h \ $$PWD/qandroidmediaservice.h \ - $$PWD/qandroidmetadatareadercontrol.h + $$PWD/qandroidmetadatareadercontrol.h \ + $$PWD/qandroidmediaplayervideorenderercontrol.h SOURCES += \ $$PWD/qandroidmediaplayercontrol.cpp \ $$PWD/qandroidmediaservice.cpp \ - $$PWD/qandroidmetadatareadercontrol.cpp + $$PWD/qandroidmetadatareadercontrol.cpp \ + $$PWD/qandroidmediaplayervideorenderercontrol.cpp diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index 9a050e7ad..a6258a74d 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -345,7 +345,7 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, mReloadingMedia = false; } -void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) +void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput) { if (mVideoOutput) { mMediaPlayer->setDisplay(0); @@ -353,7 +353,7 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) mVideoOutput->reset(); } - mVideoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput); + mVideoOutput = videoOutput; if (!mVideoOutput) return; @@ -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/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index 3f92d809c..a015a6809 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -67,7 +67,7 @@ public: const QIODevice *mediaStream() const Q_DECL_OVERRIDE; void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE; - void setVideoOutput(QObject *videoOutput); + void setVideoOutput(QAndroidVideoOutput *videoOutput); Q_SIGNALS: void metaDataUpdated(); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp new file mode 100644 index 000000000..5dd51c395 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidmediaplayervideorenderercontrol.h" + +#include "qandroidmediaplayercontrol.h" +#include "qandroidvideooutput.h" +#include <qabstractvideosurface.h> + +QT_BEGIN_NAMESPACE + +QAndroidMediaPlayerVideoRendererControl::QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent) + : QVideoRendererControl(parent) + , m_mediaPlayerControl(mediaPlayer) + , m_surface(0) + , m_textureOutput(new QAndroidTextureVideoOutput(this)) +{ + m_mediaPlayerControl->setVideoOutput(m_textureOutput); +} + +QAndroidMediaPlayerVideoRendererControl::~QAndroidMediaPlayerVideoRendererControl() +{ + m_mediaPlayerControl->setVideoOutput(0); +} + +QAbstractVideoSurface *QAndroidMediaPlayerVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidMediaPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface == surface) + return; + + m_surface = surface; + m_textureOutput->setSurface(m_surface); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h new file mode 100644 index 000000000..cfa41980d --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H +#define QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H + +#include <qvideorenderercontrol.h> + +QT_BEGIN_NAMESPACE + +class QAndroidMediaPlayerControl; +class QAndroidTextureVideoOutput; + +class QAndroidMediaPlayerVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent = 0); + ~QAndroidMediaPlayerVideoRendererControl() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; + void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; + +private: + QAndroidMediaPlayerControl *m_mediaPlayerControl; + QAbstractVideoSurface *m_surface; + QAndroidTextureVideoOutput *m_textureOutput; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 74943ca64..992bcead2 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -35,7 +35,7 @@ #include "qandroidmediaplayercontrol.h" #include "qandroidmetadatareadercontrol.h" -#include "qandroidvideorendercontrol.h" +#include "qandroidmediaplayervideorenderercontrol.h" QT_BEGIN_NAMESPACE @@ -53,9 +53,9 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent) QAndroidMediaService::~QAndroidMediaService() { - delete mMediaControl; - delete mMetadataControl; delete mVideoRendererControl; + delete mMetadataControl; + delete mMediaControl; } QMediaControl *QAndroidMediaService::requestControl(const char *name) @@ -68,8 +68,7 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name) if (qstrcmp(name, QVideoRendererControl_iid) == 0) { if (!mVideoRendererControl) { - mVideoRendererControl = new QAndroidVideoRendererControl; - mMediaControl->setVideoOutput(mVideoRendererControl); + mVideoRendererControl = new QAndroidMediaPlayerVideoRendererControl(mMediaControl); return mVideoRendererControl; } } @@ -80,7 +79,6 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name) void QAndroidMediaService::releaseControl(QMediaControl *control) { if (control == mVideoRendererControl) { - mMediaControl->setVideoOutput(0); delete mVideoRendererControl; mVideoRendererControl = 0; } diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h index 6babbb15f..798d6ef39 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h @@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE class QAndroidMediaPlayerControl; class QAndroidMetaDataReaderControl; +class QAndroidMediaPlayerVideoRendererControl; class QAndroidMediaService : public QMediaService { @@ -54,7 +55,7 @@ public: private: QAndroidMediaPlayerControl *mMediaControl; QAndroidMetaDataReaderControl *mMetadataControl; - QMediaControl *mVideoRendererControl; + QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index d09a7734f..b0f027ac3 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -37,6 +37,8 @@ #include <QtMultimedia/qmediametadata.h> #include <qsize.h> #include <QDate> +#include <QtConcurrent/qtconcurrentrun.h> +#include <QtCore/qvector.h> QT_BEGIN_NAMESPACE @@ -63,147 +65,178 @@ static const char* qt_ID3GenreNames[] = "Euro-House", "Dance Hall" }; +typedef QVector<QAndroidMetaDataReaderControl *> 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 = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::Title); - if (!string.isNull()) - m_metadata.insert(QMediaMetaData::Title, 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 = 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()); } - 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 <QMetaDataReaderControl> #include <qmediacontent.h> +#include <QMutex> 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 |