summaryrefslogtreecommitdiffstats
path: root/src/plugins/android/src/mediaplayer
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@digia.com>2013-03-25 18:11:27 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-12 12:56:57 +0200
commit0a7882f6b308296ff9d44d891d3bdfed91679ce2 (patch)
tree8a1e1575158d8213773fcf4e692a160bf4b301cd /src/plugins/android/src/mediaplayer
parent99fff6941ba6b1929c1bd8d46dc8776a6cbf11c6 (diff)
Android: added camera support.
This patch includes all camera features: viewport, settings, image capture, and video recording. It also adds support for QAudioRecorder. Change-Id: Ib962177cc8de4bac03f42a2bc0f534e03464bbfc Reviewed-by: Christian Stromme <christian.stromme@digia.com>
Diffstat (limited to 'src/plugins/android/src/mediaplayer')
-rw-r--r--src/plugins/android/src/mediaplayer/mediaplayer.pri11
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp535
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h131
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp97
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaservice.h71
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp222
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h80
7 files changed, 1147 insertions, 0 deletions
diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri
new file mode 100644
index 000000000..c386d996b
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/mediaplayer.pri
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qandroidmediaplayercontrol.h \
+ $$PWD/qandroidmediaservice.h \
+ $$PWD/qandroidmetadatareadercontrol.h
+
+SOURCES += \
+ $$PWD/qandroidmediaplayercontrol.cpp \
+ $$PWD/qandroidmediaservice.cpp \
+ $$PWD/qandroidmetadatareadercontrol.cpp
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
new file mode 100644
index 000000000..753c60662
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaplayercontrol.h"
+#include "jmediaplayer.h"
+#include "qandroidvideooutput.h"
+
+QT_BEGIN_NAMESPACE
+
+static void textureReadyCallback(void *context)
+{
+ if (context)
+ reinterpret_cast<QAndroidMediaPlayerControl *>(context)->onSurfaceTextureReady();
+}
+
+QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
+ : QMediaPlayerControl(parent),
+ mMediaPlayer(new JMediaPlayer),
+ mCurrentState(QMediaPlayer::StoppedState),
+ mCurrentMediaStatus(QMediaPlayer::NoMedia),
+ mMediaStream(0),
+ mVideoOutput(0),
+ mSeekable(true),
+ mBufferPercent(-1),
+ mAudioAvailable(false),
+ mVideoAvailable(false),
+ mBuffering(false),
+ mMediaPlayerReady(false),
+ mPendingPosition(-1),
+ mPendingSetMedia(false)
+{
+ connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)),
+ this, SLOT(onBufferChanged(qint32)));
+ connect(mMediaPlayer, SIGNAL(info(qint32, qint32)),
+ this, SLOT(onInfo(qint32, qint32)));
+ connect(mMediaPlayer, SIGNAL(error(qint32, qint32)),
+ this, SLOT(onError(qint32, qint32)));
+ connect(mMediaPlayer, SIGNAL(mediaPlayerInfo(qint32, qint32)),
+ this, SLOT(onMediaPlayerInfo(qint32, qint32)));
+ connect(mMediaPlayer, SIGNAL(videoSizeChanged(qint32,qint32)),
+ this, SLOT(onVideoSizeChanged(qint32,qint32)));
+}
+
+QAndroidMediaPlayerControl::~QAndroidMediaPlayerControl()
+{
+ mMediaPlayer->stop();
+ mMediaPlayer->release();
+ delete mMediaPlayer;
+}
+
+QMediaPlayer::State QAndroidMediaPlayerControl::state() const
+{
+ return mCurrentState;
+}
+
+QMediaPlayer::MediaStatus QAndroidMediaPlayerControl::mediaStatus() const
+{
+ return mCurrentMediaStatus;
+}
+
+qint64 QAndroidMediaPlayerControl::duration() const
+{
+ return (mCurrentMediaStatus == QMediaPlayer::InvalidMedia
+ || mCurrentMediaStatus == QMediaPlayer::NoMedia
+ || !mMediaPlayerReady) ? 0
+ : mMediaPlayer->getDuration();
+}
+
+qint64 QAndroidMediaPlayerControl::position() const
+{
+ if (!mMediaPlayerReady)
+ return mPendingPosition < 0 ? 0 : mPendingPosition;
+
+ return mMediaPlayer->getCurrentPosition();
+}
+
+void QAndroidMediaPlayerControl::setPosition(qint64 position)
+{
+ if (!mSeekable)
+ return;
+
+ const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
+
+ if (!mMediaPlayerReady) {
+ mPendingPosition = seekPosition;
+ Q_EMIT positionChanged(seekPosition);
+ return;
+ }
+
+ mMediaPlayer->seekTo(seekPosition);
+ mPendingPosition = -1;
+}
+
+int QAndroidMediaPlayerControl::volume() const
+{
+ return mMediaPlayer->volume();
+}
+
+void QAndroidMediaPlayerControl::setVolume(int volume)
+{
+ mMediaPlayer->setVolume(volume);
+ Q_EMIT volumeChanged(volume);
+}
+
+bool QAndroidMediaPlayerControl::isMuted() const
+{
+ return mMediaPlayer->isMuted();
+}
+
+void QAndroidMediaPlayerControl::setMuted(bool muted)
+{
+ mMediaPlayer->setMuted(muted);
+ Q_EMIT mutedChanged(muted);
+}
+
+int QAndroidMediaPlayerControl::bufferStatus() const
+{
+ return mBufferPercent;
+}
+
+bool QAndroidMediaPlayerControl::isAudioAvailable() const
+{
+ return mAudioAvailable;
+}
+
+bool QAndroidMediaPlayerControl::isVideoAvailable() const
+{
+ return mVideoAvailable;
+}
+
+bool QAndroidMediaPlayerControl::isSeekable() const
+{
+ return mSeekable;
+}
+
+QMediaTimeRange QAndroidMediaPlayerControl::availablePlaybackRanges() const
+{
+ return mAvailablePlaybackRange;
+}
+
+void QAndroidMediaPlayerControl::updateAvailablePlaybackRanges()
+{
+ if (mBuffering) {
+ const qint64 pos = position();
+ const qint64 end = (duration() / 100) * mBufferPercent;
+ mAvailablePlaybackRange.addInterval(pos, end);
+ } else if (mSeekable) {
+ mAvailablePlaybackRange = QMediaTimeRange(0, duration());
+ } else {
+ mAvailablePlaybackRange = QMediaTimeRange();
+ }
+
+ Q_EMIT availablePlaybackRangesChanged(mAvailablePlaybackRange);
+}
+
+qreal QAndroidMediaPlayerControl::playbackRate() const
+{
+ return 1.0f;
+}
+
+void QAndroidMediaPlayerControl::setPlaybackRate(qreal rate)
+{
+ Q_UNUSED(rate);
+}
+
+QMediaContent QAndroidMediaPlayerControl::media() const
+{
+ return mMediaContent;
+}
+
+const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
+{
+ return mMediaStream;
+}
+
+void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent,
+ QIODevice *stream)
+{
+ mMediaContent = mediaContent;
+ mMediaStream = stream;
+
+ if (mVideoOutput && !mMediaPlayer->display()) {
+ // if a video output is set but the video texture is not ready, delay loading the media
+ // since it can cause problems on some hardware
+ mPendingSetMedia = true;
+ return;
+ }
+
+ const QString uri = mediaContent.canonicalUrl().toString();
+
+ if (!uri.isEmpty())
+ mMediaPlayer->setDataSource(uri);
+ else
+ setMediaStatus(QMediaPlayer::NoMedia);
+
+ Q_EMIT mediaChanged(mMediaContent);
+
+ resetBufferingProgress();
+
+ // reset some properties
+ setAudioAvailable(false);
+ setVideoAvailable(false);
+ setSeekable(true);
+}
+
+void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput)
+{
+ if (mVideoOutput)
+ mVideoOutput->stop();
+
+ mVideoOutput = videoOutput;
+
+ if (mVideoOutput && !mMediaPlayer->display()) {
+ if (mVideoOutput->isTextureReady())
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+ else
+ mVideoOutput->setTextureReadyCallback(textureReadyCallback, this);
+ }
+}
+
+void QAndroidMediaPlayerControl::play()
+{
+ if (!mMediaPlayerReady) {
+ mPendingState = QMediaPlayer::PlayingState;
+ if (mCurrentState == QMediaPlayer::StoppedState
+ && !mMediaContent.isNull()
+ && mCurrentMediaStatus != QMediaPlayer::LoadingMedia
+ && !mPendingSetMedia) {
+ setMedia(mMediaContent, 0);
+ }
+ return;
+ }
+
+ mMediaPlayer->play();
+ setState(QMediaPlayer::PlayingState);
+}
+
+void QAndroidMediaPlayerControl::pause()
+{
+ if (!mMediaPlayerReady) {
+ mPendingState = QMediaPlayer::PausedState;
+ return;
+ }
+
+ mMediaPlayer->pause();
+ setState(QMediaPlayer::PausedState);
+}
+
+void QAndroidMediaPlayerControl::stop()
+{
+ mMediaPlayer->stop();
+ setState(QMediaPlayer::StoppedState);
+}
+
+void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
+{
+ Q_UNUSED(extra);
+ switch (what) {
+ case JMediaPlayer::MEDIA_INFO_UNKNOWN:
+ break;
+ case JMediaPlayer::MEDIA_INFO_VIDEO_TRACK_LAGGING:
+ // IGNORE
+ break;
+ case JMediaPlayer::MEDIA_INFO_VIDEO_RENDERING_START:
+ break;
+ case JMediaPlayer::MEDIA_INFO_BUFFERING_START:
+ mPendingState = mCurrentState;
+ setState(QMediaPlayer::PausedState);
+ setMediaStatus(QMediaPlayer::StalledMedia);
+ break;
+ case JMediaPlayer::MEDIA_INFO_BUFFERING_END:
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia);
+ flushPendingStates();
+ break;
+ case JMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING:
+ break;
+ case JMediaPlayer::MEDIA_INFO_NOT_SEEKABLE:
+ setSeekable(false);
+ break;
+ case JMediaPlayer::MEDIA_INFO_METADATA_UPDATE:
+ Q_EMIT metaDataUpdated();
+ break;
+ }
+}
+
+void QAndroidMediaPlayerControl::onMediaPlayerInfo(qint32 what, qint32 extra)
+{
+ switch (what) {
+ case JMediaPlayer::MEDIA_PLAYER_INVALID_STATE:
+ setError(what, QStringLiteral("Error: Invalid state"));
+ break;
+ case JMediaPlayer::MEDIA_PLAYER_PREPARING:
+ setMediaStatus(QMediaPlayer::LoadingMedia);
+ setState(QMediaPlayer::StoppedState);
+ break;
+ case JMediaPlayer::MEDIA_PLAYER_READY:
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ onBufferChanged(100);
+ }
+ setAudioAvailable(true);
+ mMediaPlayerReady = true;
+ flushPendingStates();
+ break;
+ case JMediaPlayer::MEDIA_PLAYER_DURATION:
+ Q_EMIT durationChanged(extra);
+ break;
+ case JMediaPlayer::MEDIA_PLAYER_PROGRESS:
+ Q_EMIT positionChanged(extra);
+ break;
+ case JMediaPlayer::MEDIA_PLAYER_FINISHED:
+ setState(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::EndOfMedia);
+ break;
+ }
+}
+
+void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
+{
+ QString errorString;
+ QMediaPlayer::Error error = QMediaPlayer::ResourceError;
+
+ switch (what) {
+ case JMediaPlayer::MEDIA_ERROR_UNKNOWN:
+ errorString = QLatin1String("Error:");
+ break;
+ case JMediaPlayer::MEDIA_ERROR_SERVER_DIED:
+ errorString = QLatin1String("Error: Server died");
+ error = QMediaPlayer::ServiceMissingError;
+ break;
+ }
+
+ switch (extra) {
+ case JMediaPlayer::MEDIA_ERROR_IO: // Network OR file error
+ errorString += QLatin1String(" (I/O operation failed)");
+ error = QMediaPlayer::NetworkError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case JMediaPlayer::MEDIA_ERROR_MALFORMED:
+ errorString += QLatin1String(" (Malformed bitstream)");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case JMediaPlayer::MEDIA_ERROR_UNSUPPORTED:
+ errorString += QLatin1String(" (Unsupported media)");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case JMediaPlayer::MEDIA_ERROR_TIMED_OUT:
+ errorString += QLatin1String(" (Timed out)");
+ break;
+ case JMediaPlayer::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
+ errorString += QLatin1String(" (Unable to start progressive playback')");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ }
+
+ setError(error, errorString);
+}
+
+void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent)
+{
+ mBuffering = percent != 100;
+ mBufferPercent = percent;
+ Q_EMIT bufferStatusChanged(mBufferPercent);
+
+ updateAvailablePlaybackRanges();
+
+ if (mBufferPercent == 100)
+ setMediaStatus(QMediaPlayer::BufferedMedia);
+}
+
+void QAndroidMediaPlayerControl::onVideoSizeChanged(qint32 width, qint32 height)
+{
+ QSize newSize(width, height);
+
+ if (width == 0 || height == 0 || newSize == mVideoSize)
+ return;
+
+ setVideoAvailable(true);
+ mVideoSize = newSize;
+
+ if (mVideoOutput)
+ mVideoOutput->setVideoSize(mVideoSize);
+}
+
+void QAndroidMediaPlayerControl::onSurfaceTextureReady()
+{
+ if (!mMediaPlayer->display() && mVideoOutput) {
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceHolder());
+ flushPendingStates();
+ }
+}
+
+void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
+{
+ if (mCurrentState == state)
+ return;
+
+ if (state == QMediaPlayer::StoppedState) {
+ if (mVideoOutput)
+ mVideoOutput->stop();
+ resetBufferingProgress();
+ mMediaPlayerReady = false;
+ mPendingPosition = -1;
+ Q_EMIT positionChanged(0);
+ }
+
+ mCurrentState = state;
+ Q_EMIT stateChanged(mCurrentState);
+}
+
+void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
+{
+ if (mCurrentMediaStatus == status)
+ return;
+
+ if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
+ Q_EMIT durationChanged(0);
+
+ mCurrentMediaStatus = status;
+ Q_EMIT mediaStatusChanged(mCurrentMediaStatus);
+}
+
+void QAndroidMediaPlayerControl::setError(int error,
+ const QString &errorString)
+{
+ setState(QMediaPlayer::StoppedState);
+ Q_EMIT QMediaPlayerControl::error(error, errorString);
+}
+
+void QAndroidMediaPlayerControl::setSeekable(bool seekable)
+{
+ if (mSeekable == seekable)
+ return;
+
+ mSeekable = seekable;
+ Q_EMIT seekableChanged(mSeekable);
+}
+
+void QAndroidMediaPlayerControl::setAudioAvailable(bool available)
+{
+ if (mAudioAvailable == available)
+ return;
+
+ mAudioAvailable = available;
+ Q_EMIT audioAvailableChanged(mAudioAvailable);
+}
+
+void QAndroidMediaPlayerControl::setVideoAvailable(bool available)
+{
+ if (mVideoAvailable == available)
+ return;
+
+ if (!available)
+ mVideoSize = QSize();
+
+ mVideoAvailable = available;
+ Q_EMIT videoAvailableChanged(mVideoAvailable);
+}
+
+void QAndroidMediaPlayerControl::resetBufferingProgress()
+{
+ mBuffering = false;
+ mBufferPercent = 0;
+ mAvailablePlaybackRange = QMediaTimeRange();
+ Q_EMIT bufferStatusChanged(mBufferPercent);
+}
+
+void QAndroidMediaPlayerControl::flushPendingStates()
+{
+ if (mPendingSetMedia) {
+ setMedia(mMediaContent, 0);
+ mPendingSetMedia = false;
+ return;
+ }
+
+ switch (mPendingState) {
+ case QMediaPlayer::PlayingState:
+ if (mPendingPosition > -1)
+ setPosition(mPendingPosition);
+ play();
+ break;
+ case QMediaPlayer::PausedState:
+ pause();
+ break;
+ case QMediaPlayer::StoppedState:
+ stop();
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
new file mode 100644
index 000000000..93eced853
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIAPLAYERCONTROL_H
+#define QANDROIDMEDIAPLAYERCONTROL_H
+
+#include <qglobal.h>
+#include <QMediaPlayerControl>
+#include <qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class JMediaPlayer;
+class QAndroidVideoOutput;
+
+class QAndroidMediaPlayerControl : public QMediaPlayerControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMediaPlayerControl(QObject *parent = 0);
+ ~QAndroidMediaPlayerControl() Q_DECL_OVERRIDE;
+
+ QMediaPlayer::State state() const Q_DECL_OVERRIDE;
+ QMediaPlayer::MediaStatus mediaStatus() const Q_DECL_OVERRIDE;
+ qint64 duration() const Q_DECL_OVERRIDE;
+ qint64 position() const Q_DECL_OVERRIDE;
+ int volume() const Q_DECL_OVERRIDE;
+ bool isMuted() const Q_DECL_OVERRIDE;
+ int bufferStatus() const Q_DECL_OVERRIDE;
+ bool isAudioAvailable() const Q_DECL_OVERRIDE;
+ bool isVideoAvailable() const Q_DECL_OVERRIDE;
+ bool isSeekable() const Q_DECL_OVERRIDE;
+ QMediaTimeRange availablePlaybackRanges() const Q_DECL_OVERRIDE;
+ qreal playbackRate() const Q_DECL_OVERRIDE;
+ void setPlaybackRate(qreal rate) Q_DECL_OVERRIDE;
+ QMediaContent media() const Q_DECL_OVERRIDE;
+ const QIODevice *mediaStream() const Q_DECL_OVERRIDE;
+ void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE;
+
+ void setVideoOutput(QAndroidVideoOutput *videoOutput);
+ void onSurfaceTextureReady();
+
+Q_SIGNALS:
+ void metaDataUpdated();
+
+public Q_SLOTS:
+ void setPosition(qint64 position) Q_DECL_OVERRIDE;
+ void play() Q_DECL_OVERRIDE;
+ void pause() Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ void setVolume(int volume) Q_DECL_OVERRIDE;
+ void setMuted(bool muted) Q_DECL_OVERRIDE;
+
+private Q_SLOTS:
+ void onError(qint32 what, qint32 extra);
+ void onInfo(qint32 what, qint32 extra);
+ void onMediaPlayerInfo(qint32 what, qint32 extra);
+ void onBufferChanged(qint32 percent);
+ void onVideoSizeChanged(qint32 width, qint32 height);
+
+private:
+ JMediaPlayer *mMediaPlayer;
+ QMediaPlayer::State mCurrentState;
+ QMediaPlayer::MediaStatus mCurrentMediaStatus;
+ QMediaContent mMediaContent;
+ QIODevice *mMediaStream;
+ QAndroidVideoOutput *mVideoOutput;
+ bool mSeekable;
+ int mBufferPercent;
+ bool mAudioAvailable;
+ bool mVideoAvailable;
+ QSize mVideoSize;
+ bool mBuffering;
+ QMediaTimeRange mAvailablePlaybackRange;
+ bool mMediaPlayerReady;
+ QMediaPlayer::State mPendingState;
+ qint64 mPendingPosition;
+ bool mPendingSetMedia;
+
+ void setState(QMediaPlayer::State state);
+ void setMediaStatus(QMediaPlayer::MediaStatus status);
+ void setError(int error, const QString &errorString);
+ void setSeekable(bool seekable);
+ void setAudioAvailable(bool available);
+ void setVideoAvailable(bool available);
+ void updateAvailablePlaybackRanges();
+ void resetBufferingProgress();
+ void flushPendingStates();
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAPLAYERCONTROL_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
new file mode 100644
index 000000000..175958676
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaservice.h"
+
+#include "qandroidmediaplayercontrol.h"
+#include "qandroidmetadatareadercontrol.h"
+#include "qandroidvideorendercontrol.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaService::QAndroidMediaService(QObject *parent)
+ : QMediaService(parent)
+ , mVideoRendererControl(0)
+{
+ mMediaControl = new QAndroidMediaPlayerControl;
+ mMetadataControl = new QAndroidMetaDataReaderControl;
+ connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)),
+ mMetadataControl, SLOT(onMediaChanged(QMediaContent)));
+ connect(mMediaControl, SIGNAL(metaDataUpdated()),
+ mMetadataControl, SLOT(onUpdateMetaData()));
+}
+
+QAndroidMediaService::~QAndroidMediaService()
+{
+ delete mMediaControl;
+ delete mMetadataControl;
+ delete mVideoRendererControl;
+}
+
+QMediaControl *QAndroidMediaService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QMediaPlayerControl_iid) == 0)
+ return mMediaControl;
+
+ if (qstrcmp(name, QMetaDataReaderControl_iid) == 0)
+ return mMetadataControl;
+
+ if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
+ if (!mVideoRendererControl) {
+ mVideoRendererControl = new QAndroidVideoRendererControl;
+ mMediaControl->setVideoOutput(mVideoRendererControl);
+ return mVideoRendererControl;
+ }
+ }
+
+ return 0;
+}
+
+void QAndroidMediaService::releaseControl(QMediaControl *control)
+{
+ if (control == mVideoRendererControl) {
+ mMediaControl->setVideoOutput(0);
+ delete mVideoRendererControl;
+ mVideoRendererControl = 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
new file mode 100644
index 000000000..4d310e8e0
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIASERVICE_H
+#define QANDROIDMEDIASERVICE_H
+
+#include <QMediaService>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaPlayerControl;
+class QAndroidMetaDataReaderControl;
+class QAndroidVideoRendererControl;
+
+class QAndroidMediaService : public QMediaService
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMediaService(QObject *parent = 0);
+ ~QAndroidMediaService() Q_DECL_OVERRIDE;
+
+ QMediaControl* requestControl(const char *name) Q_DECL_OVERRIDE;
+ void releaseControl(QMediaControl *control) Q_DECL_OVERRIDE;
+
+private:
+ QAndroidMediaPlayerControl *mMediaControl;
+ QAndroidMetaDataReaderControl *mMetadataControl;
+ QAndroidVideoRendererControl *mVideoRendererControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIASERVICE_H
diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
new file mode 100644
index 000000000..e52c46387
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmetadatareadercontrol.h"
+
+#include "jmediametadataretriever.h"
+#include <QtMultimedia/qmediametadata.h>
+#include <qsize.h>
+#include <QDate>
+
+QT_BEGIN_NAMESPACE
+
+// Genre name ordered by ID
+// see: http://id3.org/id3v2.3.0#Appendix_A_-_Genre_List_from_ID3v1
+static const char* qt_ID3GenreNames[] =
+{
+ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz",
+ "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno",
+ "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno",
+ "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental",
+ "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk",
+ "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave",
+ "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
+ "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American",
+ "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
+ "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk",
+ "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic",
+ "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
+ "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour",
+ "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
+ "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad",
+ "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella",
+ "Euro-House", "Dance Hall"
+};
+
+QAndroidMetaDataReaderControl::QAndroidMetaDataReaderControl(QObject *parent)
+ : QMetaDataReaderControl(parent)
+ , m_available(false)
+ , m_retriever(0)
+{
+ m_retriever = new JMediaMetadataRetriever;
+ if (!m_retriever->isValid()) {
+ delete m_retriever;
+ m_retriever = 0;
+ }
+}
+
+QAndroidMetaDataReaderControl::~QAndroidMetaDataReaderControl()
+{
+ if (m_retriever) {
+ m_retriever->release();
+ delete m_retriever;
+ }
+}
+
+bool QAndroidMetaDataReaderControl::isMetaDataAvailable() const
+{
+ return m_available;
+}
+
+QVariant QAndroidMetaDataReaderControl::metaData(const QString &key) const
+{
+ return m_metadata.value(key);
+}
+
+QStringList QAndroidMetaDataReaderControl::availableMetaData() const
+{
+ return m_metadata.keys();
+}
+
+void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media)
+{
+ if (!m_retriever)
+ return;
+
+ m_mediaContent = media;
+ updateData();
+}
+
+void QAndroidMetaDataReaderControl::onUpdateMetaData()
+{
+ if (!m_retriever || m_mediaContent.isNull())
+ return;
+
+ updateData();
+}
+
+void QAndroidMetaDataReaderControl::updateData()
+{
+ m_metadata.clear();
+
+ if (!m_mediaContent.isNull()) {
+ if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) {
+ QString mimeType = m_retriever->extractMetadata(JMediaMetadataRetriever::MimeType);
+ if (!mimeType.isNull())
+ m_metadata.insert(QMediaMetaData::MediaType, mimeType);
+
+ bool isVideo = !m_retriever->extractMetadata(JMediaMetadataRetriever::HasVideo).isNull()
+ || mimeType.startsWith(QStringLiteral("video"));
+
+ QString string = m_retriever->extractMetadata(JMediaMetadataRetriever::Album);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::AlbumTitle, string);
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::AlbumArtist);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::AlbumArtist, string);
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Artist);
+ if (!string.isNull()) {
+ m_metadata.insert(isVideo ? QMediaMetaData::LeadPerformer
+ : QMediaMetaData::ContributingArtist,
+ string.split('/', QString::SkipEmptyParts));
+ }
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Author);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts));
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Bitrate);
+ if (!string.isNull()) {
+ m_metadata.insert(isVideo ? QMediaMetaData::VideoBitRate
+ : QMediaMetaData::AudioBitRate,
+ string.toInt());
+ }
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::CDTrackNumber);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::TrackNumber, string.toInt());
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Composer);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Composer, string.split('/', QString::SkipEmptyParts));
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Date);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date());
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Duration);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Duration, string.toLongLong());
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::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.mid(1, string.length() - 2).toInt(&ok);
+ if (ok && genreId >= 0 && genreId <= 125)
+ string = QLatin1String(qt_ID3GenreNames[genreId]);
+ }
+ m_metadata.insert(QMediaMetaData::Genre, string);
+ }
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Title);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Title, string);
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::VideoHeight);
+ if (!string.isNull()) {
+ int height = string.toInt();
+ int width = m_retriever->extractMetadata(JMediaMetadataRetriever::VideoWidth).toInt();
+ m_metadata.insert(QMediaMetaData::Resolution, QSize(width, height));
+ }
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Writer);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Writer, string.split('/', QString::SkipEmptyParts));
+
+ string = m_retriever->extractMetadata(JMediaMetadataRetriever::Year);
+ if (!string.isNull())
+ m_metadata.insert(QMediaMetaData::Year, string.toInt());
+ }
+ }
+
+ bool oldAvailable = m_available;
+ m_available = !m_metadata.isEmpty();
+ if (m_available != oldAvailable)
+ Q_EMIT metaDataAvailableChanged(m_available);
+
+ Q_EMIT metaDataChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
new file mode 100644
index 000000000..7ea736ffd
--- /dev/null
+++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMETADATAREADERCONTROL_H
+#define QANDROIDMETADATAREADERCONTROL_H
+
+#include <QMetaDataReaderControl>
+#include <qmediacontent.h>
+
+QT_BEGIN_NAMESPACE
+
+class JMediaMetadataRetriever;
+
+class QAndroidMetaDataReaderControl : public QMetaDataReaderControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMetaDataReaderControl(QObject *parent = 0);
+ ~QAndroidMetaDataReaderControl() Q_DECL_OVERRIDE;
+
+ bool isMetaDataAvailable() const Q_DECL_OVERRIDE;
+
+ QVariant metaData(const QString &key) const Q_DECL_OVERRIDE;
+ QStringList availableMetaData() const Q_DECL_OVERRIDE;
+
+public Q_SLOTS:
+ void onMediaChanged(const QMediaContent &media);
+ void onUpdateMetaData();
+
+private:
+ void updateData();
+
+ QMediaContent m_mediaContent;
+ bool m_available;
+ QVariantMap m_metadata;
+
+ JMediaMetadataRetriever *m_retriever;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMETADATAREADERCONTROL_H