summaryrefslogtreecommitdiffstats
path: root/src/multimedia/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/platform/android')
-rw-r--r--src/multimedia/platform/android/android.pri12
-rw-r--r--src/multimedia/platform/android/common/common.pri10
-rw-r--r--src/multimedia/platform/android/common/qandroidglobal_p.h64
-rw-r--r--src/multimedia/platform/android/common/qandroidmultimediautils.cpp152
-rw-r--r--src/multimedia/platform/android/common/qandroidmultimediautils_p.h74
-rw-r--r--src/multimedia/platform/android/common/qandroidvideooutput.cpp502
-rw-r--r--src/multimedia/platform/android/common/qandroidvideooutput_p.h156
-rw-r--r--src/multimedia/platform/android/mediacapture/mediacapture.pri39
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp96
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h78
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp111
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h82
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp343
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h112
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp355
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h102
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp383
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h133
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp94
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h83
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp140
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h86
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp939
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h216
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp281
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h83
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp200
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h108
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp594
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h193
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp93
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h83
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp84
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h77
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp118
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h86
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp61
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h72
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp115
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h87
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp97
-rw-r--r--src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h79
-rw-r--r--src/multimedia/platform/android/mediaplayer/mediaplayer.pri13
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp835
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h154
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp76
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h79
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp95
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h84
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp248
-rw-r--r--src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h90
-rw-r--r--src/multimedia/platform/android/qandroidmediaserviceplugin.cpp159
-rw-r--r--src/multimedia/platform/android/qandroidmediaserviceplugin_p.h81
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidcamera.cpp1712
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidcamera_p.h248
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp193
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h100
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp435
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h152
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp405
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h187
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp78
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h77
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp211
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h95
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp195
-rw-r--r--src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h113
-rw-r--r--src/multimedia/platform/android/wrappers/jni/jni.pri21
68 files changed, 13009 insertions, 0 deletions
diff --git a/src/multimedia/platform/android/android.pri b/src/multimedia/platform/android/android.pri
new file mode 100644
index 000000000..ce9fa093e
--- /dev/null
+++ b/src/multimedia/platform/android/android.pri
@@ -0,0 +1,12 @@
+QT += opengl core-private network
+
+HEADERS += \
+ $$PWD/qandroidmediaserviceplugin_p.h
+
+SOURCES += \
+ $$PWD/qandroidmediaserviceplugin.cpp
+
+include(wrappers/jni/jni.pri)
+include(common/common.pri)
+include(mediaplayer/mediaplayer.pri)
+include(mediacapture/mediacapture.pri)
diff --git a/src/multimedia/platform/android/common/common.pri b/src/multimedia/platform/android/common/common.pri
new file mode 100644
index 000000000..67a5116d2
--- /dev/null
+++ b/src/multimedia/platform/android/common/common.pri
@@ -0,0 +1,10 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qandroidglobal_p.h \
+ $$PWD/qandroidvideooutput_p.h \
+ $$PWD/qandroidmultimediautils_p.h
+
+SOURCES += \
+ $$PWD/qandroidvideooutput.cpp \
+ $$PWD/qandroidmultimediautils.cpp
diff --git a/src/multimedia/platform/android/common/qandroidglobal_p.h b/src/multimedia/platform/android/common/qandroidglobal_p.h
new file mode 100644
index 000000000..45bd22ffb
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidglobal_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDGLOBAL_H
+#define QANDROIDGLOBAL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtmultimediaglobal_p.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qtAndroidMediaPlugin)
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDGLOBAL_H
diff --git a/src/multimedia/platform/android/common/qandroidmultimediautils.cpp b/src/multimedia/platform/android/common/qandroidmultimediautils.cpp
new file mode 100644
index 000000000..850b3d7ea
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidmultimediautils.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmultimediautils_p.h"
+#include "qandroidglobal_p.h"
+
+#include <qlist.h>
+#include <QtCore/private/qjni_p.h>
+#include <QtCore/private/qjnihelpers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+int qt_findClosestValue(const QList<int> &list, int value)
+{
+ if (list.size() < 2)
+ return 0;
+
+ int begin = 0;
+ int end = list.size() - 1;
+ int pivot = begin + (end - begin) / 2;
+ int v = list.at(pivot);
+
+ while (end - begin > 1) {
+ if (value == v)
+ return pivot;
+
+ if (value > v)
+ begin = pivot;
+ else
+ end = pivot;
+
+ pivot = begin + (end - begin) / 2;
+ v = list.at(pivot);
+ }
+
+ return value - v >= list.at(pivot + 1) - value ? pivot + 1 : pivot;
+}
+
+bool qt_sizeLessThan(const QSize &s1, const QSize &s2)
+{
+ return s1.width() * s1.height() < s2.width() * s2.height();
+}
+
+QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f)
+{
+ switch (f) {
+ case AndroidCamera::NV21:
+ return QVideoFrame::Format_NV21;
+ case AndroidCamera::YV12:
+ return QVideoFrame::Format_YV12;
+ case AndroidCamera::RGB565:
+ return QVideoFrame::Format_RGB565;
+ case AndroidCamera::YUY2:
+ return QVideoFrame::Format_YUYV;
+ case AndroidCamera::JPEG:
+ return QVideoFrame::Format_Jpeg;
+ default:
+ return QVideoFrame::Format_Invalid;
+ }
+}
+
+AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f)
+{
+ switch (f) {
+ case QVideoFrame::Format_NV21:
+ return AndroidCamera::NV21;
+ case QVideoFrame::Format_YV12:
+ return AndroidCamera::YV12;
+ case QVideoFrame::Format_RGB565:
+ return AndroidCamera::RGB565;
+ case QVideoFrame::Format_YUYV:
+ return AndroidCamera::YUY2;
+ case QVideoFrame::Format_Jpeg:
+ return AndroidCamera::JPEG;
+ default:
+ return AndroidCamera::UnknownImageFormat;
+ }
+}
+
+static bool androidRequestPermission(const QString &key)
+{
+ using namespace QtAndroidPrivate;
+
+ if (androidSdkVersion() < 23)
+ return true;
+
+ PermissionsResult res = checkPermission(key);
+ if (res == PermissionsResult::Granted) // Permission already granted?
+ return true;
+
+ QJNIEnvironmentPrivate env;
+ const auto &results = requestPermissionsSync(env, QStringList() << key);
+ if (!results.contains(key)) {
+ qCWarning(qtAndroidMediaPlugin, "No permission found for key: %s", qPrintable(key));
+ return false;
+ }
+
+ if (results[key] == PermissionsResult::Denied) {
+ qCDebug(qtAndroidMediaPlugin, "%s - Permission denied by user!", qPrintable(key));
+ return false;
+ }
+
+ return true;
+}
+
+bool qt_androidRequestCameraPermission()
+{
+ return androidRequestPermission(QLatin1String("android.permission.CAMERA"));
+}
+
+bool qt_androidRequestRecordingPermission()
+{
+ return androidRequestPermission(QLatin1String("android.permission.RECORD_AUDIO"));
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/common/qandroidmultimediautils_p.h b/src/multimedia/platform/android/common/qandroidmultimediautils_p.h
new file mode 100644
index 000000000..205244eb5
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidmultimediautils_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMULTIMEDIAUTILS_H
+#define QANDROIDMULTIMEDIAUTILS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <qsize.h>
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// return the index of the closest value to <value> in <list>
+// (binary search)
+int qt_findClosestValue(const QList<int> &list, int value);
+
+bool qt_sizeLessThan(const QSize &s1, const QSize &s2);
+
+QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f);
+AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f);
+
+bool qt_androidRequestCameraPermission();
+bool qt_androidRequestRecordingPermission();
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMULTIMEDIAUTILS_H
diff --git a/src/multimedia/platform/android/common/qandroidvideooutput.cpp b/src/multimedia/platform/android/common/qandroidvideooutput.cpp
new file mode 100644
index 000000000..0fa6cb84a
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidvideooutput.cpp
@@ -0,0 +1,502 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidvideooutput_p.h"
+
+#include "androidsurfacetexture_p.h"
+#include <QAbstractVideoSurface>
+#include <QVideoSurfaceFormat>
+#include <qevent.h>
+#include <qcoreapplication.h>
+#include <qopenglcontext.h>
+#include <qopenglfunctions.h>
+#include <qopenglshaderprogram.h>
+#include <qopenglframebufferobject.h>
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtGui/QWindow>
+#include <QtGui/QOffscreenSurface>
+
+QT_BEGIN_NAMESPACE
+
+static const GLfloat g_vertex_data[] = {
+ -1.f, 1.f,
+ 1.f, 1.f,
+ 1.f, -1.f,
+ -1.f, -1.f
+};
+
+static const GLfloat g_texture_data[] = {
+ 0.f, 0.f,
+ 1.f, 0.f,
+ 1.f, 1.f,
+ 0.f, 1.f
+};
+
+void OpenGLResourcesDeleter::deleteTextureHelper(quint32 id)
+{
+ if (id != 0)
+ glDeleteTextures(1, &id);
+}
+
+void OpenGLResourcesDeleter::deleteFboHelper(void *fbo)
+{
+ delete reinterpret_cast<QOpenGLFramebufferObject *>(fbo);
+}
+
+void OpenGLResourcesDeleter::deleteShaderProgramHelper(void *prog)
+{
+ delete reinterpret_cast<QOpenGLShaderProgram *>(prog);
+}
+
+void OpenGLResourcesDeleter::deleteThisHelper()
+{
+ delete this;
+}
+
+class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
+{
+public:
+ AndroidTextureVideoBuffer(QAndroidTextureVideoOutput *output, const QSize &size)
+ : QAbstractVideoBuffer(GLTextureHandle)
+ , m_mapMode(NotMapped)
+ , m_output(output)
+ , m_size(size)
+ , m_textureUpdated(false)
+ {
+ }
+
+ virtual ~AndroidTextureVideoBuffer() {}
+
+ MapMode mapMode() const override { return m_mapMode; }
+
+ MapData map(MapMode mode) override
+ {
+ MapData mapData;
+ if (m_mapMode == NotMapped && mode == ReadOnly && updateFrame()) {
+ m_mapMode = mode;
+ m_image = m_output->m_fbo->toImage();
+
+ mapData.nBytes = static_cast<int>(m_image.sizeInBytes());
+ mapData.nPlanes = 1;
+ mapData.bytesPerLine[0] = m_image.bytesPerLine();
+ mapData.data[0] = m_image.bits();
+ }
+
+ return mapData;
+ }
+
+ void unmap() override
+ {
+ m_image = QImage();
+ m_mapMode = NotMapped;
+ }
+
+ QVariant handle() const override
+ {
+ AndroidTextureVideoBuffer *that = const_cast<AndroidTextureVideoBuffer*>(this);
+ if (!that->updateFrame())
+ return QVariant();
+
+ return m_output->m_fbo->texture();
+ }
+
+private:
+ bool updateFrame()
+ {
+ // Even though the texture was updated in a previous call, we need to re-check
+ // that this has not become a stale buffer, e.g., if the output size changed or
+ // has since became invalid.
+ if (!m_output->m_nativeSize.isValid())
+ return false;
+
+ // Size changed
+ if (m_output->m_nativeSize != m_size)
+ return false;
+
+ // In the unlikely event that we don't have a valid fbo, but have a valid size,
+ // force an update.
+ const bool forceUpdate = !m_output->m_fbo;
+
+ if (m_textureUpdated && !forceUpdate)
+ return true;
+
+ // update the video texture (called from the render thread)
+ return (m_textureUpdated = m_output->renderFrameToFbo());
+ }
+
+ MapMode m_mapMode;
+ QAndroidTextureVideoOutput *m_output;
+ QImage m_image;
+ QSize m_size;
+ bool m_textureUpdated;
+};
+
+QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
+ : QAndroidVideoOutput(parent)
+ , m_surface(0)
+ , m_surfaceTexture(0)
+ , m_externalTex(0)
+ , m_fbo(0)
+ , m_program(0)
+ , m_glDeleter(0)
+ , m_surfaceTextureCanAttachToContext(QtAndroidPrivate::androidSdkVersion() >= 16)
+{
+
+}
+
+QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
+{
+ delete m_offscreenSurface;
+ delete m_glContext;
+ clearSurfaceTexture();
+
+ if (m_glDeleter) { // Make sure all of these are deleted on the render thread.
+ m_glDeleter->deleteFbo(m_fbo);
+ m_glDeleter->deleteShaderProgram(m_program);
+ m_glDeleter->deleteTexture(m_externalTex);
+ m_glDeleter->deleteThis();
+ }
+}
+
+QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const
+{
+ return m_surface;
+}
+
+void QAndroidTextureVideoOutput::setSurface(QAbstractVideoSurface *surface)
+{
+ if (surface == m_surface)
+ return;
+
+ if (m_surface) {
+ if (m_surface->isActive())
+ m_surface->stop();
+
+ if (!m_surfaceTextureCanAttachToContext)
+ m_surface->setProperty("_q_GLThreadCallback", QVariant());
+ }
+
+ m_surface = surface;
+
+ if (m_surface && !m_surfaceTextureCanAttachToContext) {
+ m_surface->setProperty("_q_GLThreadCallback",
+ QVariant::fromValue<QObject*>(this));
+ }
+}
+
+bool QAndroidTextureVideoOutput::isReady()
+{
+ return m_surfaceTextureCanAttachToContext || QOpenGLContext::currentContext() || m_externalTex;
+}
+
+bool QAndroidTextureVideoOutput::initSurfaceTexture()
+{
+ if (m_surfaceTexture)
+ return true;
+
+ if (!m_surface)
+ return false;
+
+ if (!m_surfaceTextureCanAttachToContext) {
+ // if we have an OpenGL context in the current thread, create a texture. Otherwise, wait
+ // for the GL render thread to call us back to do it.
+ if (QOpenGLContext::currentContext()) {
+ glGenTextures(1, &m_externalTex);
+ if (!m_glDeleter)
+ m_glDeleter = new OpenGLResourcesDeleter;
+ } else if (!m_externalTex) {
+ return false;
+ }
+ }
+
+ QMutexLocker locker(&m_mutex);
+
+ m_surfaceTexture = new AndroidSurfaceTexture(m_externalTex);
+
+ if (m_surfaceTexture->surfaceTexture() != 0) {
+ connect(m_surfaceTexture, SIGNAL(frameAvailable()), this, SLOT(onFrameAvailable()));
+ } else {
+ delete m_surfaceTexture;
+ m_surfaceTexture = 0;
+ if (m_glDeleter)
+ m_glDeleter->deleteTexture(m_externalTex);
+ m_externalTex = 0;
+ }
+
+ return m_surfaceTexture != 0;
+}
+
+void QAndroidTextureVideoOutput::clearSurfaceTexture()
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_surfaceTexture) {
+ delete m_surfaceTexture;
+ m_surfaceTexture = 0;
+ }
+
+ // Also reset the attached OpenGL texture
+ // Note: The Android SurfaceTexture class does not release the texture on deletion,
+ // only if detachFromGLContext() called (API level >= 16), so we'll do it manually,
+ // on the render thread.
+ if (m_surfaceTextureCanAttachToContext) {
+ if (m_glDeleter)
+ m_glDeleter->deleteTexture(m_externalTex);
+ m_externalTex = 0;
+ }
+}
+
+AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
+{
+ if (!initSurfaceTexture())
+ return 0;
+
+ return m_surfaceTexture;
+}
+
+void QAndroidTextureVideoOutput::setVideoSize(const QSize &size)
+{
+ QMutexLocker locker(&m_mutex);
+ if (m_nativeSize == size)
+ return;
+
+ stop();
+
+ m_nativeSize = size;
+}
+
+void QAndroidTextureVideoOutput::stop()
+{
+ if (m_surface && m_surface->isActive())
+ m_surface->stop();
+ m_nativeSize = QSize();
+}
+
+void QAndroidTextureVideoOutput::reset()
+{
+ // flush pending frame
+ if (m_surface)
+ m_surface->present(QVideoFrame());
+
+ clearSurfaceTexture();
+}
+
+void QAndroidTextureVideoOutput::onFrameAvailable()
+{
+ if (!m_nativeSize.isValid() || !m_surface)
+ return;
+
+ QAbstractVideoBuffer *buffer = new AndroidTextureVideoBuffer(this, m_nativeSize);
+ QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_ABGR32);
+
+ if (m_surface->isActive() && (m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()
+ || m_surface->surfaceFormat().frameSize() != frame.size())) {
+ m_surface->stop();
+ }
+
+ if (!m_surface->isActive()) {
+ QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(),
+ QAbstractVideoBuffer::GLTextureHandle);
+
+ m_surface->start(format);
+ }
+
+ if (m_surface->isActive())
+ m_surface->present(frame);
+}
+
+bool QAndroidTextureVideoOutput::renderFrameToFbo()
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (!m_nativeSize.isValid() || !m_surfaceTexture)
+ return false;
+
+ QOpenGLContext *shareContext = !m_glContext && m_surface
+ ? qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>())
+ : nullptr;
+
+ // Make sure we have an OpenGL context to make current.
+ if (shareContext || (!QOpenGLContext::currentContext() && !m_glContext)) {
+ // Create Hidden QWindow surface to create context in this thread.
+ m_offscreenSurface = new QWindow();
+ m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface);
+ // Needs geometry to be a valid surface, but size is not important.
+ m_offscreenSurface->setGeometry(0, 0, 1, 1);
+ m_offscreenSurface->create();
+ m_offscreenSurface->moveToThread(m_surface->thread());
+
+ // Create OpenGL context and set share context from surface.
+ m_glContext = new QOpenGLContext();
+ m_glContext->setFormat(m_offscreenSurface->requestedFormat());
+
+ auto surface = qobject_cast<QAbstractVideoSurface *>(m_surface->property("videoSurface").value<QObject *>());
+ if (!surface)
+ surface = m_surface;
+ if (shareContext)
+ m_glContext->setShareContext(shareContext);
+
+ if (!m_glContext->create()) {
+ qWarning("Failed to create QOpenGLContext");
+ return false;
+ }
+ }
+
+ if (m_glContext)
+ m_glContext->makeCurrent(m_offscreenSurface);
+
+ createGLResources();
+
+ m_surfaceTexture->updateTexImage();
+
+ // save current render states
+ GLboolean stencilTestEnabled;
+ GLboolean depthTestEnabled;
+ GLboolean scissorTestEnabled;
+ GLboolean blendEnabled;
+ glGetBooleanv(GL_STENCIL_TEST, &stencilTestEnabled);
+ glGetBooleanv(GL_DEPTH_TEST, &depthTestEnabled);
+ glGetBooleanv(GL_SCISSOR_TEST, &scissorTestEnabled);
+ glGetBooleanv(GL_BLEND, &blendEnabled);
+
+ if (stencilTestEnabled)
+ glDisable(GL_STENCIL_TEST);
+ if (depthTestEnabled)
+ glDisable(GL_DEPTH_TEST);
+ if (scissorTestEnabled)
+ glDisable(GL_SCISSOR_TEST);
+ if (blendEnabled)
+ glDisable(GL_BLEND);
+
+ m_fbo->bind();
+
+ glViewport(0, 0, m_nativeSize.width(), m_nativeSize.height());
+
+ m_program->bind();
+ m_program->enableAttributeArray(0);
+ m_program->enableAttributeArray(1);
+ m_program->setUniformValue("frameTexture", GLuint(0));
+ m_program->setUniformValue("texMatrix", m_surfaceTexture->getTransformMatrix());
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, g_vertex_data);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, g_texture_data);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ m_program->disableAttributeArray(0);
+ m_program->disableAttributeArray(1);
+
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
+ m_fbo->release();
+
+ // restore render states
+ if (stencilTestEnabled)
+ glEnable(GL_STENCIL_TEST);
+ if (depthTestEnabled)
+ glEnable(GL_DEPTH_TEST);
+ if (scissorTestEnabled)
+ glEnable(GL_SCISSOR_TEST);
+ if (blendEnabled)
+ glEnable(GL_BLEND);
+
+ return true;
+}
+
+void QAndroidTextureVideoOutput::createGLResources()
+{
+ Q_ASSERT(QOpenGLContext::currentContext() != NULL);
+
+ if (!m_glDeleter)
+ m_glDeleter = new OpenGLResourcesDeleter;
+
+ if (m_surfaceTextureCanAttachToContext && !m_externalTex) {
+ m_surfaceTexture->detachFromGLContext();
+ glGenTextures(1, &m_externalTex);
+ m_surfaceTexture->attachToGLContext(m_externalTex);
+ }
+
+ if (!m_fbo || m_fbo->size() != m_nativeSize) {
+ delete m_fbo;
+ m_fbo = new QOpenGLFramebufferObject(m_nativeSize);
+ }
+
+ if (!m_program) {
+ m_program = new QOpenGLShaderProgram;
+
+ QOpenGLShader *vertexShader = new QOpenGLShader(QOpenGLShader::Vertex, m_program);
+ vertexShader->compileSourceCode("attribute highp vec4 vertexCoordsArray; \n" \
+ "attribute highp vec2 textureCoordArray; \n" \
+ "uniform highp mat4 texMatrix; \n" \
+ "varying highp vec2 textureCoords; \n" \
+ "void main(void) \n" \
+ "{ \n" \
+ " gl_Position = vertexCoordsArray; \n" \
+ " textureCoords = (texMatrix * vec4(textureCoordArray, 0.0, 1.0)).xy; \n" \
+ "}\n");
+ m_program->addShader(vertexShader);
+
+ QOpenGLShader *fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment, m_program);
+ fragmentShader->compileSourceCode("#extension GL_OES_EGL_image_external : require \n" \
+ "varying highp vec2 textureCoords; \n" \
+ "uniform samplerExternalOES frameTexture; \n" \
+ "void main() \n" \
+ "{ \n" \
+ " gl_FragColor = texture2D(frameTexture, textureCoords); \n" \
+ "}\n");
+ m_program->addShader(fragmentShader);
+
+ m_program->bindAttributeLocation("vertexCoordsArray", 0);
+ m_program->bindAttributeLocation("textureCoordArray", 1);
+ m_program->link();
+ }
+}
+
+void QAndroidTextureVideoOutput::customEvent(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ // This is running in the render thread (OpenGL enabled)
+ if (!m_surfaceTextureCanAttachToContext && !m_externalTex) {
+ glGenTextures(1, &m_externalTex);
+ if (!m_glDeleter) // We'll use this to cleanup GL resources in the correct thread
+ m_glDeleter = new OpenGLResourcesDeleter;
+ emit readyChanged(true);
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/common/qandroidvideooutput_p.h b/src/multimedia/platform/android/common/qandroidvideooutput_p.h
new file mode 100644
index 000000000..dbc53ca44
--- /dev/null
+++ b/src/multimedia/platform/android/common/qandroidvideooutput_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDVIDEOOUTPUT_H
+#define QANDROIDVIDEOOUTPUT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <qsize.h>
+#include <qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+class QOpenGLFramebufferObject;
+class QOpenGLShaderProgram;
+class QAbstractVideoSurface;
+class QWindow;
+class QOpenGLContext;
+
+class QAndroidVideoOutput : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~QAndroidVideoOutput() { }
+
+ virtual AndroidSurfaceTexture *surfaceTexture() { return 0; }
+ virtual AndroidSurfaceHolder *surfaceHolder() { return 0; }
+
+ virtual bool isReady() { return true; }
+
+ virtual void setVideoSize(const QSize &) { }
+ virtual void stop() { }
+ virtual void reset() { }
+
+Q_SIGNALS:
+ void readyChanged(bool);
+
+protected:
+ QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
+};
+
+class OpenGLResourcesDeleter : public QObject
+{
+ Q_OBJECT
+public:
+ void deleteTexture(quint32 id) { QMetaObject::invokeMethod(this, "deleteTextureHelper", Qt::AutoConnection, Q_ARG(quint32, id)); }
+ void deleteFbo(QOpenGLFramebufferObject *fbo) { QMetaObject::invokeMethod(this, "deleteFboHelper", Qt::AutoConnection, Q_ARG(void *, fbo)); }
+ void deleteShaderProgram(QOpenGLShaderProgram *prog) { QMetaObject::invokeMethod(this, "deleteShaderProgramHelper", Qt::AutoConnection, Q_ARG(void *, prog)); }
+ void deleteThis() { QMetaObject::invokeMethod(this, "deleteThisHelper"); }
+
+private:
+ Q_INVOKABLE void deleteTextureHelper(quint32 id);
+ Q_INVOKABLE void deleteFboHelper(void *fbo);
+ Q_INVOKABLE void deleteShaderProgramHelper(void *prog);
+ Q_INVOKABLE void deleteThisHelper();
+};
+
+class QAndroidTextureVideoOutput : public QAndroidVideoOutput
+{
+ Q_OBJECT
+public:
+ explicit QAndroidTextureVideoOutput(QObject *parent = 0);
+ ~QAndroidTextureVideoOutput() override;
+
+ QAbstractVideoSurface *surface() const;
+ void setSurface(QAbstractVideoSurface *surface);
+
+ AndroidSurfaceTexture *surfaceTexture() override;
+
+ bool isReady() override;
+ void setVideoSize(const QSize &) override;
+ void stop() override;
+ void reset() override;
+
+ void customEvent(QEvent *) override;
+
+private Q_SLOTS:
+ void onFrameAvailable();
+
+private:
+ bool initSurfaceTexture();
+ bool renderFrameToFbo();
+ void createGLResources();
+
+ QMutex m_mutex;
+ void clearSurfaceTexture();
+
+ QAbstractVideoSurface *m_surface;
+ QSize m_nativeSize;
+
+ AndroidSurfaceTexture *m_surfaceTexture;
+
+ quint32 m_externalTex;
+ QOpenGLFramebufferObject *m_fbo;
+ QOpenGLShaderProgram *m_program;
+ OpenGLResourcesDeleter *m_glDeleter;
+
+ bool m_surfaceTextureCanAttachToContext;
+
+ QWindow *m_offscreenSurface = nullptr;
+ QOpenGLContext *m_glContext = nullptr;
+
+ friend class AndroidTextureVideoBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDVIDEOOUTPUT_H
diff --git a/src/multimedia/platform/android/mediacapture/mediacapture.pri b/src/multimedia/platform/android/mediacapture/mediacapture.pri
new file mode 100644
index 000000000..a1f4b41a6
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/mediacapture.pri
@@ -0,0 +1,39 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/qandroidcaptureservice.cpp \
+ $$PWD/qandroidcameracontrol.cpp \
+ $$PWD/qandroidvideodeviceselectorcontrol.cpp \
+ $$PWD/qandroidcamerasession.cpp \
+ $$PWD/qandroidcameraexposurecontrol.cpp \
+ $$PWD/qandroidcameraimageprocessingcontrol.cpp \
+ $$PWD/qandroidimageencodercontrol.cpp \
+ $$PWD/qandroidcameraimagecapturecontrol.cpp \
+ $$PWD/qandroidcamerafocuscontrol.cpp \
+ $$PWD/qandroidcapturesession.cpp \
+ $$PWD/qandroidmediarecordercontrol.cpp \
+ $$PWD/qandroidaudioencodersettingscontrol.cpp \
+ $$PWD/qandroidmediacontainercontrol.cpp \
+ $$PWD/qandroidvideoencodersettingscontrol.cpp \
+ $$PWD/qandroidaudioinputselectorcontrol.cpp \
+ $$PWD/qandroidmediavideoprobecontrol.cpp \
+ $$PWD/qandroidcameravideorenderercontrol.cpp
+
+HEADERS += \
+ $$PWD/qandroidcaptureservice_p.h \
+ $$PWD/qandroidcameracontrol_p.h \
+ $$PWD/qandroidvideodeviceselectorcontrol_p.h \
+ $$PWD/qandroidcamerasession_p.h \
+ $$PWD/qandroidcameraexposurecontrol_p.h \
+ $$PWD/qandroidcameraimageprocessingcontrol_p.h \
+ $$PWD/qandroidimageencodercontrol_p.h \
+ $$PWD/qandroidcameraimagecapturecontrol_p.h \
+ $$PWD/qandroidcamerafocuscontrol_p.h \
+ $$PWD/qandroidcapturesession_p.h \
+ $$PWD/qandroidmediarecordercontrol_p.h \
+ $$PWD/qandroidaudioencodersettingscontrol_p.h \
+ $$PWD/qandroidmediacontainercontrol_p.h \
+ $$PWD/qandroidvideoencodersettingscontrol_p.h \
+ $$PWD/qandroidaudioinputselectorcontrol_p.h \
+ $$PWD/qandroidmediavideoprobecontrol_p.h \
+ $$PWD/qandroidcameravideorenderercontrol_p.h
diff --git a/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp
new file mode 100644
index 000000000..c32a93cee
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidaudioencodersettingscontrol.h"
+
+#include "qandroidcapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidAudioEncoderSettingsControl::QAndroidAudioEncoderSettingsControl(QAndroidCaptureSession *session)
+ : QAudioEncoderSettingsControl()
+ , m_session(session)
+{
+}
+
+QStringList QAndroidAudioEncoderSettingsControl::supportedAudioCodecs() const
+{
+ return QStringList() << QLatin1String("amr-nb") << QLatin1String("amr-wb") << QLatin1String("aac");
+}
+
+QString QAndroidAudioEncoderSettingsControl::codecDescription(const QString &codecName) const
+{
+ if (codecName == QLatin1String("amr-nb"))
+ return tr("Adaptive Multi-Rate Narrowband (AMR-NB) audio codec");
+ else if (codecName == QLatin1String("amr-wb"))
+ return tr("Adaptive Multi-Rate Wideband (AMR-WB) audio codec");
+ else if (codecName == QLatin1String("aac"))
+ return tr("AAC Low Complexity (AAC-LC) audio codec");
+
+ return QString();
+}
+
+QList<int> QAndroidAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const
+{
+ if (continuous)
+ *continuous = false;
+
+ if (settings.isNull() || settings.codec().isNull() || settings.codec() == QLatin1String("aac")) {
+ return QList<int>() << 8000 << 11025 << 12000 << 16000 << 22050
+ << 24000 << 32000 << 44100 << 48000 << 96000;
+ } else if (settings.codec() == QLatin1String("amr-nb")) {
+ return QList<int>() << 8000;
+ } else if (settings.codec() == QLatin1String("amr-wb")) {
+ return QList<int>() << 16000;
+ }
+
+ return QList<int>();
+}
+
+QAudioEncoderSettings QAndroidAudioEncoderSettingsControl::audioSettings() const
+{
+ return m_session->audioSettings();
+}
+
+void QAndroidAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings)
+{
+ m_session->setAudioSettings(settings);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h
new file mode 100644
index 000000000..7259ff685
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioencodersettingscontrol_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDAUDIOENCODERSETTINGSCONTROL_H
+#define QANDROIDAUDIOENCODERSETTINGSCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qaudioencodersettingscontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCaptureSession;
+
+class QAndroidAudioEncoderSettingsControl : public QAudioEncoderSettingsControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidAudioEncoderSettingsControl(QAndroidCaptureSession *session);
+
+ QStringList supportedAudioCodecs() const override;
+ QString codecDescription(const QString &codecName) const override;
+ QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const override;
+ QAudioEncoderSettings audioSettings() const override;
+ void setAudioSettings(const QAudioEncoderSettings &settings) override;
+
+private:
+ QAndroidCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDAUDIOENCODERSETTINGSCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp
new file mode 100644
index 000000000..9745d0725
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidaudioinputselectorcontrol.h"
+
+#include "qandroidcapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidAudioInputSelectorControl::QAndroidAudioInputSelectorControl(QAndroidCaptureSession *session)
+ : QAudioInputSelectorControl()
+ , m_session(session)
+{
+ connect(m_session, SIGNAL(audioInputChanged(QString)), this, SIGNAL(activeInputChanged(QString)));
+}
+
+QList<QString> QAndroidAudioInputSelectorControl::availableInputs() const
+{
+ return QList<QString>() << QLatin1String("default")
+ << QLatin1String("mic")
+ << QLatin1String("voice_uplink")
+ << QLatin1String("voice_downlink")
+ << QLatin1String("voice_call")
+ << QLatin1String("voice_recognition");
+}
+
+QString QAndroidAudioInputSelectorControl::inputDescription(const QString& name) const
+{
+ return availableDeviceDescription(name.toLatin1());
+}
+
+QString QAndroidAudioInputSelectorControl::defaultInput() const
+{
+ return QLatin1String("default");
+}
+
+QString QAndroidAudioInputSelectorControl::activeInput() const
+{
+ return m_session->audioInput();
+}
+
+void QAndroidAudioInputSelectorControl::setActiveInput(const QString& name)
+{
+ m_session->setAudioInput(name);
+}
+
+QList<QByteArray> QAndroidAudioInputSelectorControl::availableDevices()
+{
+ return QList<QByteArray>() << "default"
+ << "mic"
+ << "voice_uplink"
+ << "voice_downlink"
+ << "voice_call"
+ << "voice_recognition";
+}
+
+QString QAndroidAudioInputSelectorControl::availableDeviceDescription(const QByteArray &device)
+{
+ if (device == "default")
+ return QLatin1String("Default audio source");
+ else if (device == "mic")
+ return QLatin1String("Microphone audio source");
+ else if (device == "voice_uplink")
+ return QLatin1String("Voice call uplink (Tx) audio source");
+ else if (device == "voice_downlink")
+ return QLatin1String("Voice call downlink (Rx) audio source");
+ else if (device == "voice_call")
+ return QLatin1String("Voice call uplink + downlink audio source");
+ else if (device == "voice_recognition")
+ return QLatin1String("Microphone audio source tuned for voice recognition");
+ else
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h
new file mode 100644
index 000000000..ef53dbdae
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidaudioinputselectorcontrol_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDAUDIOINPUTSELECTORCONTROL_H
+#define QANDROIDAUDIOINPUTSELECTORCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qaudioinputselectorcontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCaptureSession;
+
+class QAndroidAudioInputSelectorControl : public QAudioInputSelectorControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidAudioInputSelectorControl(QAndroidCaptureSession *session);
+
+ QList<QString> availableInputs() const;
+ QString inputDescription(const QString& name) const;
+ QString defaultInput() const;
+
+ QString activeInput() const;
+ void setActiveInput(const QString& name);
+
+ static QList<QByteArray> availableDevices();
+ static QString availableDeviceDescription(const QByteArray &device);
+
+private:
+ QAndroidCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDAUDIOINPUTSELECTORCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp
new file mode 100644
index 000000000..18f6de7c1
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol.cpp
@@ -0,0 +1,343 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcameracontrol_p.h"
+#include "qandroidcamerasession_p.h"
+#include <qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCameraControl::QAndroidCameraControl(QAndroidCameraSession *session)
+ : QCameraControl(0)
+ , m_cameraSession(session)
+
+{
+ connect(m_cameraSession, SIGNAL(statusChanged(QCamera::Status)),
+ this, SIGNAL(statusChanged(QCamera::Status)));
+
+ connect(m_cameraSession, SIGNAL(stateChanged(QCamera::State)),
+ this, SIGNAL(stateChanged(QCamera::State)));
+
+ connect(m_cameraSession, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
+
+ connect(m_cameraSession, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
+ this, SIGNAL(captureModeChanged(QCamera::CaptureModes)));
+
+ connect(m_cameraSession, SIGNAL(opened()), this, SLOT(onCameraOpened()));
+
+ m_recalculateTimer = new QTimer(this);
+ m_recalculateTimer->setInterval(1000);
+ m_recalculateTimer->setSingleShot(true);
+ connect(m_recalculateTimer, SIGNAL(timeout()), this, SLOT(onRecalculateTimeOut()));
+}
+
+QAndroidCameraControl::~QAndroidCameraControl()
+{
+}
+
+QCamera::CaptureModes QAndroidCameraControl::captureMode() const
+{
+ return m_cameraSession->captureMode();
+}
+
+void QAndroidCameraControl::setCaptureMode(QCamera::CaptureModes mode)
+{
+ m_cameraSession->setCaptureMode(mode);
+}
+
+bool QAndroidCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ return m_cameraSession->isCaptureModeSupported(mode);
+}
+
+void QAndroidCameraControl::setState(QCamera::State state)
+{
+ m_cameraSession->setState(state);
+}
+
+QCamera::State QAndroidCameraControl::state() const
+{
+ return m_cameraSession->state();
+}
+
+QCamera::Status QAndroidCameraControl::status() const
+{
+ return m_cameraSession->status();
+}
+
+bool QAndroidCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
+{
+ Q_UNUSED(status);
+
+ switch (changeType) {
+ case QCameraControl::CaptureMode:
+ case QCameraControl::ImageEncodingSettings:
+ case QCameraControl::VideoEncodingSettings:
+ case QCameraControl::Viewfinder:
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+QCamera::LockTypes QAndroidCameraControl::supportedLocks() const
+{
+ return m_supportedLocks;
+}
+
+QCamera::LockStatus QAndroidCameraControl::lockStatus(QCamera::LockType lock) const
+{
+ if (!m_supportedLocks.testFlag(lock) || !m_cameraSession->camera())
+ return QCamera::Unlocked;
+
+ if (lock == QCamera::LockFocus)
+ return m_focusLockStatus;
+
+ if (lock == QCamera::LockExposure)
+ return m_exposureLockStatus;
+
+ if (lock == QCamera::LockWhiteBalance)
+ return m_whiteBalanceLockStatus;
+
+ return QCamera::Unlocked;
+}
+
+void QAndroidCameraControl::searchAndLock(QCamera::LockTypes locks)
+{
+ if (!m_cameraSession->camera())
+ return;
+
+ // filter out unsupported locks
+ locks &= m_supportedLocks;
+
+ if (locks.testFlag(QCamera::LockFocus)) {
+ QString focusMode = m_cameraSession->camera()->getFocusMode();
+ if (focusMode == QLatin1String("auto")
+ || focusMode == QLatin1String("macro")
+ || focusMode == QLatin1String("continuous-picture")
+ || focusMode == QLatin1String("continuous-video")) {
+
+ if (m_focusLockStatus == QCamera::Searching)
+ m_cameraSession->camera()->cancelAutoFocus();
+ else
+ setFocusLockStatus(QCamera::Searching, QCamera::UserRequest);
+
+ m_cameraSession->camera()->autoFocus();
+
+ } else {
+ setFocusLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+ }
+
+ if (locks.testFlag(QCamera::LockExposure) && m_exposureLockStatus != QCamera::Searching) {
+ if (m_cameraSession->camera()->getAutoExposureLock()) {
+ // if already locked, unlock and give some time to recalculate exposure
+ m_cameraSession->camera()->setAutoExposureLock(false);
+ setExposureLockStatus(QCamera::Searching, QCamera::UserRequest);
+ } else {
+ m_cameraSession->camera()->setAutoExposureLock(true);
+ setExposureLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+ }
+
+ if (locks.testFlag(QCamera::LockWhiteBalance) && m_whiteBalanceLockStatus != QCamera::Searching) {
+ if (m_cameraSession->camera()->getAutoWhiteBalanceLock()) {
+ // if already locked, unlock and give some time to recalculate white balance
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(false);
+ setWhiteBalanceLockStatus(QCamera::Searching, QCamera::UserRequest);
+ } else {
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(true);
+ setWhiteBalanceLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+ }
+
+ if (m_exposureLockStatus == QCamera::Searching || m_whiteBalanceLockStatus == QCamera::Searching)
+ m_recalculateTimer->start();
+}
+
+void QAndroidCameraControl::unlock(QCamera::LockTypes locks)
+{
+ if (!m_cameraSession->camera())
+ return;
+
+ if (m_recalculateTimer->isActive())
+ m_recalculateTimer->stop();
+
+ // filter out unsupported locks
+ locks &= m_supportedLocks;
+
+ if (locks.testFlag(QCamera::LockFocus)) {
+ m_cameraSession->camera()->cancelAutoFocus();
+ setFocusLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+
+ if (locks.testFlag(QCamera::LockExposure)) {
+ m_cameraSession->camera()->setAutoExposureLock(false);
+ setExposureLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+
+ if (locks.testFlag(QCamera::LockWhiteBalance)) {
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(false);
+ setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+}
+
+void QAndroidCameraControl::onCameraOpened()
+{
+ m_supportedLocks = QCamera::NoLock;
+ m_focusLockStatus = QCamera::Unlocked;
+ m_exposureLockStatus = QCamera::Unlocked;
+ m_whiteBalanceLockStatus = QCamera::Unlocked;
+
+ // check if focus lock is supported
+ QStringList focusModes = m_cameraSession->camera()->getSupportedFocusModes();
+ for (int i = 0; i < focusModes.size(); ++i) {
+ const QString &focusMode = focusModes.at(i);
+ if (focusMode == QLatin1String("auto")
+ || focusMode == QLatin1String("continuous-picture")
+ || focusMode == QLatin1String("continuous-video")
+ || focusMode == QLatin1String("macro")) {
+
+ m_supportedLocks |= QCamera::LockFocus;
+ setFocusLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+
+ connect(m_cameraSession->camera(), SIGNAL(autoFocusComplete(bool)),
+ this, SLOT(onCameraAutoFocusComplete(bool)));
+
+ break;
+ }
+ }
+
+ if (m_cameraSession->camera()->isAutoExposureLockSupported()) {
+ m_supportedLocks |= QCamera::LockExposure;
+ setExposureLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+ }
+
+ if (m_cameraSession->camera()->isAutoWhiteBalanceLockSupported()) {
+ m_supportedLocks |= QCamera::LockWhiteBalance;
+ setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::UserRequest);
+
+ connect(m_cameraSession->camera(), SIGNAL(whiteBalanceChanged()),
+ this, SLOT(onWhiteBalanceChanged()));
+ }
+}
+
+void QAndroidCameraControl::onCameraAutoFocusComplete(bool success)
+{
+ m_focusLockStatus = success ? QCamera::Locked : QCamera::Unlocked;
+ QCamera::LockChangeReason reason = success ? QCamera::LockAcquired : QCamera::LockFailed;
+ emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, reason);
+}
+
+void QAndroidCameraControl::onRecalculateTimeOut()
+{
+ if (m_exposureLockStatus == QCamera::Searching) {
+ m_cameraSession->camera()->setAutoExposureLock(true);
+ setExposureLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+
+ if (m_whiteBalanceLockStatus == QCamera::Searching) {
+ m_cameraSession->camera()->setAutoWhiteBalanceLock(true);
+ setWhiteBalanceLockStatus(QCamera::Locked, QCamera::LockAcquired);
+ }
+}
+
+void QAndroidCameraControl::onWhiteBalanceChanged()
+{
+ // changing the white balance mode releases the white balance lock
+ if (m_whiteBalanceLockStatus != QCamera::Unlocked)
+ setWhiteBalanceLockStatus(QCamera::Unlocked, QCamera::LockLost);
+}
+
+void QAndroidCameraControl::setFocusLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_focusLockStatus = status;
+ emit lockStatusChanged(QCamera::LockFocus, m_focusLockStatus, reason);
+}
+
+void QAndroidCameraControl::setWhiteBalanceLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_whiteBalanceLockStatus = status;
+ emit lockStatusChanged(QCamera::LockWhiteBalance, m_whiteBalanceLockStatus, reason);
+}
+
+void QAndroidCameraControl::setExposureLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason)
+{
+ m_exposureLockStatus = status;
+ emit lockStatusChanged(QCamera::LockExposure, m_exposureLockStatus, reason);
+}
+
+QList<QCameraViewfinderSettings> QAndroidCameraControl::supportedViewfinderSettings() const
+{
+ QList<QCameraViewfinderSettings> viewfinderSettings;
+
+ const QList<QSize> previewSizes = m_cameraSession->getSupportedPreviewSizes();
+ const QList<QVideoFrame::PixelFormat> pixelFormats = m_cameraSession->getSupportedPixelFormats();
+ const QList<AndroidCamera::FpsRange> fpsRanges = m_cameraSession->getSupportedPreviewFpsRange();
+
+ viewfinderSettings.reserve(previewSizes.size() * pixelFormats.size() * fpsRanges.size());
+
+ for (const QSize& size : previewSizes) {
+ for (QVideoFrame::PixelFormat pixelFormat : pixelFormats) {
+ for (const AndroidCamera::FpsRange& fpsRange : fpsRanges) {
+ QCameraViewfinderSettings s;
+ s.setResolution(size);
+ s.setPixelAspectRatio(QSize(1, 1));
+ s.setPixelFormat(pixelFormat);
+ s.setMinimumFrameRate(fpsRange.getMinReal());
+ s.setMaximumFrameRate(fpsRange.getMaxReal());
+ viewfinderSettings << s;
+ }
+ }
+ }
+ return viewfinderSettings;
+}
+
+QCameraViewfinderSettings QAndroidCameraControl::viewfinderSettings() const
+{
+ return m_cameraSession->viewfinderSettings();
+}
+
+void QAndroidCameraControl::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ m_cameraSession->setViewfinderSettings(settings);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h
new file mode 100644
index 000000000..23fbc2dc6
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameracontrol_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QANDROIDCAMERACONTROL_H
+#define QANDROIDCAMERACONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameracontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCameraControl : public QCameraControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraControl(QAndroidCameraSession *session);
+ virtual ~QAndroidCameraControl();
+
+ QCamera::State state() const override;
+ void setState(QCamera::State state) override;
+
+ QCamera::Status status() const override;
+
+ QCamera::CaptureModes captureMode() const override;
+ void setCaptureMode(QCamera::CaptureModes mode) override;
+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const override;
+
+ bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const override;
+
+ QCamera::LockTypes supportedLocks() const override;
+ QCamera::LockStatus lockStatus(QCamera::LockType lock) const override;
+ void searchAndLock(QCamera::LockTypes locks) override;
+ void unlock(QCamera::LockTypes locks) override;
+
+ QList<QCameraViewfinderSettings> supportedViewfinderSettings() const override;
+ QCameraViewfinderSettings viewfinderSettings() const override;
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings) override;
+
+private Q_SLOTS:
+ void onCameraOpened();
+ void onCameraAutoFocusComplete(bool success);
+ void onRecalculateTimeOut();
+ void onWhiteBalanceChanged();
+
+private:
+ void setFocusLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
+ void setWhiteBalanceLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
+ void setExposureLockStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
+
+ QAndroidCameraSession *m_cameraSession;
+
+ QTimer *m_recalculateTimer;
+
+ QCamera::LockTypes m_supportedLocks;
+
+ QCamera::LockStatus m_focusLockStatus;
+ QCamera::LockStatus m_exposureLockStatus;
+ QCamera::LockStatus m_whiteBalanceLockStatus;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERACONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp
new file mode 100644
index 000000000..e2c1d09c1
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol.cpp
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcameraexposurecontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCameraExposureControl::QAndroidCameraExposureControl(QAndroidCameraSession *session)
+ : QCameraExposureControl()
+ , m_session(session)
+ , m_minExposureCompensationIndex(0)
+ , m_maxExposureCompensationIndex(0)
+ , m_exposureCompensationStep(0.0)
+ , m_requestedExposureCompensation(0.0)
+ , m_actualExposureCompensation(0.0)
+ , m_requestedExposureMode(QCameraExposure::ExposureAuto)
+ , m_actualExposureMode(QCameraExposure::ExposureAuto)
+{
+ connect(m_session, SIGNAL(opened()),
+ this, SLOT(onCameraOpened()));
+}
+
+bool QAndroidCameraExposureControl::isParameterSupported(ExposureParameter parameter) const
+{
+ if (!m_session->camera())
+ return false;
+
+ switch (parameter) {
+ case QCameraExposureControl::ISO:
+ return false;
+ case QCameraExposureControl::Aperture:
+ return false;
+ case QCameraExposureControl::ShutterSpeed:
+ return false;
+ case QCameraExposureControl::ExposureCompensation:
+ return !m_supportedExposureCompensations.isEmpty();
+ case QCameraExposureControl::TorchPower:
+ return false;
+ case QCameraExposureControl::ExposureMode:
+ return !m_supportedExposureModes.isEmpty();
+ default:
+ return false;
+ }
+}
+
+QVariantList QAndroidCameraExposureControl::supportedParameterRange(ExposureParameter parameter, bool *continuous) const
+{
+ if (!m_session->camera())
+ return QVariantList();
+
+ if (continuous)
+ *continuous = false;
+
+ if (parameter == QCameraExposureControl::ExposureCompensation)
+ return m_supportedExposureCompensations;
+ else if (parameter == QCameraExposureControl::ExposureMode)
+ return m_supportedExposureModes;
+
+ return QVariantList();
+}
+
+QVariant QAndroidCameraExposureControl::requestedValue(ExposureParameter parameter) const
+{
+ if (parameter == QCameraExposureControl::ExposureCompensation)
+ return QVariant::fromValue(m_requestedExposureCompensation);
+ else if (parameter == QCameraExposureControl::ExposureMode)
+ return QVariant::fromValue(m_requestedExposureMode);
+
+ return QVariant();
+}
+
+QVariant QAndroidCameraExposureControl::actualValue(ExposureParameter parameter) const
+{
+ if (parameter == QCameraExposureControl::ExposureCompensation)
+ return QVariant::fromValue(m_actualExposureCompensation);
+ else if (parameter == QCameraExposureControl::ExposureMode)
+ return QVariant::fromValue(m_actualExposureMode);
+
+ return QVariant();
+}
+
+bool QAndroidCameraExposureControl::setValue(ExposureParameter parameter, const QVariant& value)
+{
+ if (!value.isValid())
+ return false;
+
+ if (parameter == QCameraExposureControl::ExposureCompensation) {
+ qreal expComp = value.toReal();
+ if (!qFuzzyCompare(m_requestedExposureCompensation, expComp)) {
+ m_requestedExposureCompensation = expComp;
+ emit requestedValueChanged(QCameraExposureControl::ExposureCompensation);
+ }
+
+ if (!m_session->camera())
+ return true;
+
+ int expCompIndex = qRound(m_requestedExposureCompensation / m_exposureCompensationStep);
+ if (expCompIndex >= m_minExposureCompensationIndex
+ && expCompIndex <= m_maxExposureCompensationIndex) {
+ qreal comp = expCompIndex * m_exposureCompensationStep;
+ m_session->camera()->setExposureCompensation(expCompIndex);
+ if (!qFuzzyCompare(m_actualExposureCompensation, comp)) {
+ m_actualExposureCompensation = expCompIndex * m_exposureCompensationStep;
+ emit actualValueChanged(QCameraExposureControl::ExposureCompensation);
+ }
+
+ return true;
+ }
+
+ } else if (parameter == QCameraExposureControl::ExposureMode) {
+ QCameraExposure::ExposureMode expMode = value.value<QCameraExposure::ExposureMode>();
+ if (m_requestedExposureMode != expMode) {
+ m_requestedExposureMode = expMode;
+ emit requestedValueChanged(QCameraExposureControl::ExposureMode);
+ }
+
+ if (!m_session->camera())
+ return true;
+
+ if (!m_supportedExposureModes.isEmpty()) {
+ m_actualExposureMode = m_requestedExposureMode;
+
+ QString sceneMode;
+ switch (m_requestedExposureMode) {
+ case QCameraExposure::ExposureAuto:
+ sceneMode = QLatin1String("auto");
+ break;
+ case QCameraExposure::ExposureSports:
+ sceneMode = QLatin1String("sports");
+ break;
+ case QCameraExposure::ExposurePortrait:
+ sceneMode = QLatin1String("portrait");
+ break;
+ case QCameraExposure::ExposureBeach:
+ sceneMode = QLatin1String("beach");
+ break;
+ case QCameraExposure::ExposureSnow:
+ sceneMode = QLatin1String("snow");
+ break;
+ case QCameraExposure::ExposureNight:
+ sceneMode = QLatin1String("night");
+ break;
+ case QCameraExposure::ExposureAction:
+ sceneMode = QLatin1String("action");
+ break;
+ case QCameraExposure::ExposureLandscape:
+ sceneMode = QLatin1String("landscape");
+ break;
+ case QCameraExposure::ExposureNightPortrait:
+ sceneMode = QLatin1String("night-portrait");
+ break;
+ case QCameraExposure::ExposureTheatre:
+ sceneMode = QLatin1String("theatre");
+ break;
+ case QCameraExposure::ExposureSunset:
+ sceneMode = QLatin1String("sunset");
+ break;
+ case QCameraExposure::ExposureSteadyPhoto:
+ sceneMode = QLatin1String("steadyphoto");
+ break;
+ case QCameraExposure::ExposureFireworks:
+ sceneMode = QLatin1String("fireworks");
+ break;
+ case QCameraExposure::ExposureParty:
+ sceneMode = QLatin1String("party");
+ break;
+ case QCameraExposure::ExposureCandlelight:
+ sceneMode = QLatin1String("candlelight");
+ break;
+ case QCameraExposure::ExposureBarcode:
+ sceneMode = QLatin1String("barcode");
+ break;
+ default:
+ sceneMode = QLatin1String("auto");
+ m_actualExposureMode = QCameraExposure::ExposureAuto;
+ break;
+ }
+
+ m_session->camera()->setSceneMode(sceneMode);
+ emit actualValueChanged(QCameraExposureControl::ExposureMode);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QAndroidCameraExposureControl::onCameraOpened()
+{
+ m_supportedExposureCompensations.clear();
+ m_minExposureCompensationIndex = m_session->camera()->getMinExposureCompensation();
+ m_maxExposureCompensationIndex = m_session->camera()->getMaxExposureCompensation();
+ m_exposureCompensationStep = m_session->camera()->getExposureCompensationStep();
+ if (m_minExposureCompensationIndex != 0 || m_maxExposureCompensationIndex != 0) {
+ for (int i = m_minExposureCompensationIndex; i <= m_maxExposureCompensationIndex; ++i)
+ m_supportedExposureCompensations.append(i * m_exposureCompensationStep);
+ emit parameterRangeChanged(QCameraExposureControl::ExposureCompensation);
+ }
+
+ m_supportedExposureModes.clear();
+ QStringList sceneModes = m_session->camera()->getSupportedSceneModes();
+ if (!sceneModes.isEmpty()) {
+ for (int i = 0; i < sceneModes.size(); ++i) {
+ const QString &sceneMode = sceneModes.at(i);
+ if (sceneMode == QLatin1String("auto"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureAuto);
+ else if (sceneMode == QLatin1String("beach"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureBeach);
+ else if (sceneMode == QLatin1String("night"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureNight);
+ else if (sceneMode == QLatin1String("portrait"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposurePortrait);
+ else if (sceneMode == QLatin1String("snow"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSnow);
+ else if (sceneMode == QLatin1String("sports"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSports);
+ else if (sceneMode == QLatin1String("action"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureAction);
+ else if (sceneMode == QLatin1String("landscape"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureLandscape);
+ else if (sceneMode == QLatin1String("night-portrait"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureNightPortrait);
+ else if (sceneMode == QLatin1String("theatre"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureTheatre);
+ else if (sceneMode == QLatin1String("sunset"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSunset);
+ else if (sceneMode == QLatin1String("steadyphoto"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureSteadyPhoto);
+ else if (sceneMode == QLatin1String("fireworks"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureFireworks);
+ else if (sceneMode == QLatin1String("party"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureParty);
+ else if (sceneMode == QLatin1String("candlelight"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureCandlelight);
+ else if (sceneMode == QLatin1String("barcode"))
+ m_supportedExposureModes << QVariant::fromValue(QCameraExposure::ExposureBarcode);
+ }
+ emit parameterRangeChanged(QCameraExposureControl::ExposureMode);
+ }
+
+ setValue(QCameraExposureControl::ExposureCompensation, QVariant::fromValue(m_requestedExposureCompensation));
+ setValue(QCameraExposureControl::ExposureMode, QVariant::fromValue(m_requestedExposureMode));
+
+ m_supportedFlashModes.clear();
+
+ QStringList flashModes = m_session->camera()->getSupportedFlashModes();
+ for (int i = 0; i < flashModes.size(); ++i) {
+ const QString &flashMode = flashModes.at(i);
+ if (flashMode == QLatin1String("off"))
+ m_supportedFlashModes << QCameraExposure::FlashOff;
+ else if (flashMode == QLatin1String("auto"))
+ m_supportedFlashModes << QCameraExposure::FlashAuto;
+ else if (flashMode == QLatin1String("on"))
+ m_supportedFlashModes << QCameraExposure::FlashOn;
+ else if (flashMode == QLatin1String("red-eye"))
+ m_supportedFlashModes << QCameraExposure::FlashRedEyeReduction;
+ else if (flashMode == QLatin1String("torch"))
+ m_supportedFlashModes << QCameraExposure::FlashVideoLight;
+ }
+
+ if (!m_supportedFlashModes.contains(m_flashMode))
+ m_flashMode = QCameraExposure::FlashOff;
+
+ setFlashMode(m_flashMode);
+}
+
+
+QCameraExposure::FlashModes QAndroidCameraExposureControl::flashMode() const
+{
+ return m_flashMode;
+}
+
+void QAndroidCameraExposureControl::setFlashMode(QCameraExposure::FlashModes mode)
+{
+ if (!m_session->camera()) {
+ m_flashMode = mode;
+ return;
+ }
+
+ if (!isFlashModeSupported(mode))
+ return;
+
+ // if torch was enabled, it first needs to be turned off before setting another mode
+ if (m_flashMode == QCameraExposure::FlashVideoLight)
+ m_session->camera()->setFlashMode(QLatin1String("off"));
+
+ m_flashMode = mode;
+
+ QString flashMode;
+ if (mode.testFlag(QCameraExposure::FlashAuto))
+ flashMode = QLatin1String("auto");
+ else if (mode.testFlag(QCameraExposure::FlashOn))
+ flashMode = QLatin1String("on");
+ else if (mode.testFlag(QCameraExposure::FlashRedEyeReduction))
+ flashMode = QLatin1String("red-eye");
+ else if (mode.testFlag(QCameraExposure::FlashVideoLight))
+ flashMode = QLatin1String("torch");
+ else // FlashOff
+ flashMode = QLatin1String("off");
+
+ m_session->camera()->setFlashMode(flashMode);
+}
+
+bool QAndroidCameraExposureControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const
+{
+ return m_session->camera() ? m_supportedFlashModes.contains(mode) : false;
+}
+
+bool QAndroidCameraExposureControl::isFlashReady() const
+{
+ // Android doesn't have an API for that
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h
new file mode 100644
index 000000000..133221b13
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraexposurecontrol_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAMERAEXPOSURECONTROL_H
+#define QANDROIDCAMERAEXPOSURECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraexposurecontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCameraExposureControl : public QCameraExposureControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraExposureControl(QAndroidCameraSession *session);
+
+ bool isParameterSupported(ExposureParameter parameter) const override;
+ QVariantList supportedParameterRange(ExposureParameter parameter, bool *continuous) const override;
+
+ QVariant requestedValue(ExposureParameter parameter) const override;
+ QVariant actualValue(ExposureParameter parameter) const override;
+ bool setValue(ExposureParameter parameter, const QVariant& value) override;
+
+ QCameraExposure::FlashModes flashMode() const override;
+ void setFlashMode(QCameraExposure::FlashModes mode) override;
+ bool isFlashModeSupported(QCameraExposure::FlashModes mode) const override;
+ bool isFlashReady() const override;
+
+private Q_SLOTS:
+ void onCameraOpened();
+
+private:
+ QAndroidCameraSession *m_session;
+
+ QVariantList m_supportedExposureCompensations;
+ QVariantList m_supportedExposureModes;
+
+ int m_minExposureCompensationIndex;
+ int m_maxExposureCompensationIndex;
+ qreal m_exposureCompensationStep;
+
+ qreal m_requestedExposureCompensation;
+ qreal m_actualExposureCompensation;
+ QCameraExposure::ExposureMode m_requestedExposureMode;
+ QCameraExposure::ExposureMode m_actualExposureMode;
+
+ QList<QCameraExposure::FlashModes> m_supportedFlashModes;
+ QCameraExposure::FlashModes m_flashMode;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAEXPOSURECONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp
new file mode 100644
index 000000000..0fa9b055a
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol.cpp
@@ -0,0 +1,383 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcamerafocuscontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.h"
+
+#include "qandroidmultimediautils_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+static QRect adjustedArea(const QRectF &area)
+{
+ // Qt maps focus points in the range (0.0, 0.0) -> (1.0, 1.0)
+ // Android maps focus points in the range (-1000, -1000) -> (1000, 1000)
+ // Converts an area in Qt coordinates to Android coordinates
+ return QRect(-1000 + qRound(area.x() * 2000),
+ -1000 + qRound(area.y() * 2000),
+ qRound(area.width() * 2000),
+ qRound(area.height() * 2000))
+ .intersected(QRect(-1000, -1000, 2000, 2000));
+}
+
+QAndroidCameraFocusControl::QAndroidCameraFocusControl(QAndroidCameraSession *session)
+ : QCameraFocusControl()
+ , m_session(session)
+ , m_focusMode(QCameraFocus::AutoFocus)
+ , m_focusPointMode(QCameraFocus::FocusPointAuto)
+ , m_actualFocusPoint(0.5, 0.5)
+ , m_continuousPictureFocusSupported(false)
+ , m_continuousVideoFocusSupported(false)
+{
+ connect(m_session, SIGNAL(opened()),
+ this, SLOT(onCameraOpened()));
+ connect(m_session, SIGNAL(captureModeChanged(QCamera::CaptureModes)),
+ this, SLOT(onCameraCaptureModeChanged()));
+}
+
+QCameraFocus::FocusModes QAndroidCameraFocusControl::focusMode() const
+{
+ return m_focusMode;
+}
+
+void QAndroidCameraFocusControl::setFocusMode(QCameraFocus::FocusModes mode)
+{
+ if (!m_session->camera()) {
+ setFocusModeHelper(mode);
+ return;
+ }
+
+ if (isFocusModeSupported(mode)) {
+ QString focusMode = QLatin1String("fixed");
+
+ if (mode.testFlag(QCameraFocus::HyperfocalFocus)) {
+ focusMode = QLatin1String("edof");
+ } else if (mode.testFlag(QCameraFocus::ManualFocus)) {
+ focusMode = QLatin1String("fixed");
+ } else if (mode.testFlag(QCameraFocus::AutoFocus)) {
+ focusMode = QLatin1String("auto");
+ } else if (mode.testFlag(QCameraFocus::MacroFocus)) {
+ focusMode = QLatin1String("macro");
+ } else if (mode.testFlag(QCameraFocus::ContinuousFocus)) {
+ if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
+ || !m_continuousPictureFocusSupported) {
+ focusMode = QLatin1String("continuous-video");
+ } else {
+ focusMode = QLatin1String("continuous-picture");
+ }
+ } else if (mode.testFlag(QCameraFocus::InfinityFocus)) {
+ focusMode = QLatin1String("infinity");
+ }
+
+ m_session->camera()->setFocusMode(focusMode);
+
+ // reset focus position
+ m_session->camera()->cancelAutoFocus();
+
+ setFocusModeHelper(mode);
+ }
+}
+
+bool QAndroidCameraFocusControl::isFocusModeSupported(QCameraFocus::FocusModes mode) const
+{
+ return m_session->camera() ? m_supportedFocusModes.contains(mode) : false;
+}
+
+QCameraFocus::FocusPointMode QAndroidCameraFocusControl::focusPointMode() const
+{
+ return m_focusPointMode;
+}
+
+void QAndroidCameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode)
+{
+ if (!m_session->camera()) {
+ setFocusPointModeHelper(mode);
+ return;
+ }
+
+ if (isFocusPointModeSupported(mode)) {
+ if (mode == QCameraFocus::FocusPointCustom) {
+ m_actualFocusPoint = m_customFocusPoint;
+ } else {
+ // FocusPointAuto | FocusPointCenter
+ // note: there is no way to know the actual focus point in FocusPointAuto mode,
+ // so just report the focus point to be at the center of the frame
+ m_actualFocusPoint = QPointF(0.5, 0.5);
+ }
+
+ setFocusPointModeHelper(mode);
+
+ updateFocusZones();
+ setCameraFocusArea();
+ }
+}
+
+bool QAndroidCameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const
+{
+ return m_session->camera() ? m_supportedFocusPointModes.contains(mode) : false;
+}
+
+QPointF QAndroidCameraFocusControl::customFocusPoint() const
+{
+ return m_customFocusPoint;
+}
+
+void QAndroidCameraFocusControl::setCustomFocusPoint(const QPointF &point)
+{
+ if (m_customFocusPoint != point) {
+ m_customFocusPoint = point;
+ emit customFocusPointChanged(m_customFocusPoint);
+ }
+
+ if (m_session->camera() && m_focusPointMode == QCameraFocus::FocusPointCustom) {
+ m_actualFocusPoint = m_customFocusPoint;
+ updateFocusZones();
+ setCameraFocusArea();
+ }
+}
+
+QCameraFocusZoneList QAndroidCameraFocusControl::focusZones() const
+{
+ return m_focusZones;
+}
+
+void QAndroidCameraFocusControl::onCameraOpened()
+{
+ connect(m_session->camera(), SIGNAL(previewSizeChanged()),
+ this, SLOT(onViewportSizeChanged()));
+ connect(m_session->camera(), SIGNAL(autoFocusStarted()),
+ this, SLOT(onAutoFocusStarted()));
+ connect(m_session->camera(), SIGNAL(autoFocusComplete(bool)),
+ this, SLOT(onAutoFocusComplete(bool)));
+
+ m_supportedFocusModes.clear();
+ m_continuousPictureFocusSupported = false;
+ m_continuousVideoFocusSupported = false;
+ m_supportedFocusPointModes.clear();
+
+ QStringList focusModes = m_session->camera()->getSupportedFocusModes();
+ for (int i = 0; i < focusModes.size(); ++i) {
+ const QString &focusMode = focusModes.at(i);
+ if (focusMode == QLatin1String("auto")) {
+ m_supportedFocusModes << QCameraFocus::AutoFocus;
+ } else if (focusMode == QLatin1String("continuous-picture")) {
+ m_supportedFocusModes << QCameraFocus::ContinuousFocus;
+ m_continuousPictureFocusSupported = true;
+ } else if (focusMode == QLatin1String("continuous-video")) {
+ m_supportedFocusModes << QCameraFocus::ContinuousFocus;
+ m_continuousVideoFocusSupported = true;
+ } else if (focusMode == QLatin1String("edof")) {
+ m_supportedFocusModes << QCameraFocus::HyperfocalFocus;
+ } else if (focusMode == QLatin1String("fixed")) {
+ m_supportedFocusModes << QCameraFocus::ManualFocus;
+ } else if (focusMode == QLatin1String("infinity")) {
+ m_supportedFocusModes << QCameraFocus::InfinityFocus;
+ } else if (focusMode == QLatin1String("macro")) {
+ m_supportedFocusModes << QCameraFocus::MacroFocus;
+ }
+ }
+
+ m_supportedFocusPointModes << QCameraFocus::FocusPointAuto;
+ if (m_session->camera()->getMaxNumFocusAreas() > 0)
+ m_supportedFocusPointModes << QCameraFocus::FocusPointCenter << QCameraFocus::FocusPointCustom;
+
+ if (!m_supportedFocusModes.contains(m_focusMode))
+ setFocusModeHelper(QCameraFocus::AutoFocus);
+ if (!m_supportedFocusPointModes.contains(m_focusPointMode))
+ setFocusPointModeHelper(QCameraFocus::FocusPointAuto);
+
+ setFocusMode(m_focusMode);
+ setCustomFocusPoint(m_customFocusPoint);
+ setFocusPointMode(m_focusPointMode);
+
+ if (m_session->camera()->isZoomSupported()) {
+ m_zoomRatios = m_session->camera()->getZoomRatios();
+ qreal maxZoom = m_zoomRatios.last() / qreal(100);
+ if (m_maximumZoom != maxZoom) {
+ m_maximumZoom = maxZoom;
+ emit maximumDigitalZoomChanged(m_maximumZoom);
+ }
+ zoomTo(1, m_requestedZoom);
+ } else {
+ m_zoomRatios.clear();
+ if (!qFuzzyCompare(m_maximumZoom, qreal(1))) {
+ m_maximumZoom = 1.0;
+ emit maximumDigitalZoomChanged(m_maximumZoom);
+ }
+ }
+}
+
+void QAndroidCameraFocusControl::updateFocusZones(QCameraFocusZone::FocusZoneStatus status)
+{
+ if (!m_session->camera())
+ return;
+
+ // create a focus zone (50x50 pixel) around the focus point
+ m_focusZones.clear();
+
+ if (!m_actualFocusPoint.isNull()) {
+ QSize viewportSize = m_session->camera()->previewSize();
+
+ if (!viewportSize.isValid())
+ return;
+
+ QSizeF focusSize(50.f / viewportSize.width(), 50.f / viewportSize.height());
+ float x = qBound(qreal(0),
+ m_actualFocusPoint.x() - (focusSize.width() / 2),
+ 1.f - focusSize.width());
+ float y = qBound(qreal(0),
+ m_actualFocusPoint.y() - (focusSize.height() / 2),
+ 1.f - focusSize.height());
+
+ QRectF area(QPointF(x, y), focusSize);
+
+ m_focusZones.append(QCameraFocusZone(area, status));
+ }
+
+ emit focusZonesChanged();
+}
+
+void QAndroidCameraFocusControl::setCameraFocusArea()
+{
+ QList<QRect> areas;
+ if (m_focusPointMode != QCameraFocus::FocusPointAuto) {
+ // in FocusPointAuto mode, leave the area list empty
+ // to let the driver choose the focus point.
+
+ for (int i = 0; i < m_focusZones.size(); ++i)
+ areas.append(adjustedArea(m_focusZones.at(i).area()));
+
+ }
+ m_session->camera()->setFocusAreas(areas);
+}
+
+void QAndroidCameraFocusControl::onViewportSizeChanged()
+{
+ QCameraFocusZone::FocusZoneStatus status = QCameraFocusZone::Selected;
+ if (!m_focusZones.isEmpty())
+ status = m_focusZones.at(0).status();
+ updateFocusZones(status);
+ setCameraFocusArea();
+}
+
+void QAndroidCameraFocusControl::onCameraCaptureModeChanged()
+{
+ if (m_session->camera() && m_focusMode == QCameraFocus::ContinuousFocus) {
+ QString focusMode;
+ if ((m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported)
+ || !m_continuousPictureFocusSupported) {
+ focusMode = QLatin1String("continuous-video");
+ } else {
+ focusMode = QLatin1String("continuous-picture");
+ }
+ m_session->camera()->setFocusMode(focusMode);
+ m_session->camera()->cancelAutoFocus();
+ }
+}
+
+void QAndroidCameraFocusControl::onAutoFocusStarted()
+{
+ updateFocusZones(QCameraFocusZone::Selected);
+}
+
+void QAndroidCameraFocusControl::onAutoFocusComplete(bool success)
+{
+ if (success)
+ updateFocusZones(QCameraFocusZone::Focused);
+}
+
+
+qreal QAndroidCameraFocusControl::maximumOpticalZoom() const
+{
+ // Optical zoom not supported
+ return 1.0;
+}
+
+qreal QAndroidCameraFocusControl::maximumDigitalZoom() const
+{
+ return m_maximumZoom;
+}
+
+qreal QAndroidCameraFocusControl::requestedOpticalZoom() const
+{
+ // Optical zoom not supported
+ return 1.0;
+}
+
+qreal QAndroidCameraFocusControl::requestedDigitalZoom() const
+{
+ return m_requestedZoom;
+}
+
+qreal QAndroidCameraFocusControl::currentOpticalZoom() const
+{
+ // Optical zoom not supported
+ return 1.0;
+}
+
+qreal QAndroidCameraFocusControl::currentDigitalZoom() const
+{
+ return m_currentZoom;
+}
+
+void QAndroidCameraFocusControl::zoomTo(qreal optical, qreal digital)
+{
+ Q_UNUSED(optical);
+
+ if (!qFuzzyCompare(m_requestedZoom, digital)) {
+ m_requestedZoom = digital;
+ emit requestedDigitalZoomChanged(m_requestedZoom);
+ }
+
+ if (m_session->camera()) {
+ digital = qBound(qreal(1), digital, m_maximumZoom);
+ int validZoomIndex = qt_findClosestValue(m_zoomRatios, qRound(digital * 100));
+ qreal newZoom = m_zoomRatios.at(validZoomIndex) / qreal(100);
+ if (!qFuzzyCompare(m_currentZoom, newZoom)) {
+ m_session->camera()->setZoom(validZoomIndex);
+ m_currentZoom = newZoom;
+ emit currentDigitalZoomChanged(m_currentZoom);
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h
new file mode 100644
index 000000000..4cc931993
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerafocuscontrol_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAMERAFOCUSCONTROL_H
+#define QANDROIDCAMERAFOCUSCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcamerafocuscontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCameraFocusControl : public QCameraFocusControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraFocusControl(QAndroidCameraSession *session);
+
+ QCameraFocus::FocusModes focusMode() const override;
+ void setFocusMode(QCameraFocus::FocusModes mode) override;
+ bool isFocusModeSupported(QCameraFocus::FocusModes mode) const override;
+ QCameraFocus::FocusPointMode focusPointMode() const override;
+ void setFocusPointMode(QCameraFocus::FocusPointMode mode) override;
+ bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const override;
+ QPointF customFocusPoint() const override;
+ void setCustomFocusPoint(const QPointF &point) override;
+ QCameraFocusZoneList focusZones() const override;
+
+ qreal maximumOpticalZoom() const override;
+ qreal maximumDigitalZoom() const override;
+ qreal requestedOpticalZoom() const override;
+ qreal requestedDigitalZoom() const override;
+ qreal currentOpticalZoom() const override;
+ qreal currentDigitalZoom() const override;
+ void zoomTo(qreal optical, qreal digital) override;
+
+private Q_SLOTS:
+ void onCameraOpened();
+ void onViewportSizeChanged();
+ void onCameraCaptureModeChanged();
+ void onAutoFocusStarted();
+ void onAutoFocusComplete(bool success);
+
+private:
+ inline void setFocusModeHelper(QCameraFocus::FocusModes mode)
+ {
+ if (m_focusMode != mode) {
+ m_focusMode = mode;
+ emit focusModeChanged(mode);
+ }
+ }
+
+ inline void setFocusPointModeHelper(QCameraFocus::FocusPointMode mode)
+ {
+ if (m_focusPointMode != mode) {
+ m_focusPointMode = mode;
+ emit focusPointModeChanged(mode);
+ }
+ }
+
+ void updateFocusZones(QCameraFocusZone::FocusZoneStatus status = QCameraFocusZone::Selected);
+ void setCameraFocusArea();
+
+ QAndroidCameraSession *m_session;
+
+ QCameraFocus::FocusModes m_focusMode;
+ QCameraFocus::FocusPointMode m_focusPointMode;
+ QPointF m_actualFocusPoint;
+ QPointF m_customFocusPoint;
+ QCameraFocusZoneList m_focusZones;
+
+ QList<QCameraFocus::FocusModes> m_supportedFocusModes;
+ bool m_continuousPictureFocusSupported;
+ bool m_continuousVideoFocusSupported;
+
+ QList<QCameraFocus::FocusPointMode> m_supportedFocusPointModes;
+
+ qreal m_maximumZoom;
+ QList<int> m_zoomRatios;
+ qreal m_requestedZoom;
+ qreal m_currentZoom;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAFOCUSCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp
new file mode 100644
index 000000000..d230daa5c
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcameraimagecapturecontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCameraImageCaptureControl::QAndroidCameraImageCaptureControl(QAndroidCameraSession *session)
+ : QCameraImageCaptureControl()
+ , m_session(session)
+{
+ connect(m_session, SIGNAL(readyForCaptureChanged(bool)), this, SIGNAL(readyForCaptureChanged(bool)));
+ connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
+ connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
+ connect(m_session, SIGNAL(imageMetadataAvailable(int,QString,QVariant)), this, SIGNAL(imageMetadataAvailable(int,QString,QVariant)));
+ connect(m_session, SIGNAL(imageAvailable(int,QVideoFrame)), this, SIGNAL(imageAvailable(int,QVideoFrame)));
+ connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString)));
+ connect(m_session, SIGNAL(imageCaptureError(int,int,QString)), this, SIGNAL(error(int,int,QString)));
+}
+
+bool QAndroidCameraImageCaptureControl::isReadyForCapture() const
+{
+ return m_session->isReadyForCapture();
+}
+
+QCameraImageCapture::DriveMode QAndroidCameraImageCaptureControl::driveMode() const
+{
+ return m_session->driveMode();
+}
+
+void QAndroidCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode)
+{
+ m_session->setDriveMode(mode);
+}
+
+int QAndroidCameraImageCaptureControl::capture(const QString &fileName)
+{
+ return m_session->capture(fileName);
+}
+
+void QAndroidCameraImageCaptureControl::cancelCapture()
+{
+ m_session->cancelCapture();
+}
+
+QCameraImageCapture::CaptureDestinations QAndroidCameraImageCaptureControl::captureDestination() const
+{
+ return m_session->captureDestination();;
+}
+
+void QAndroidCameraImageCaptureControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ m_session->setCaptureDestination(destination);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h
new file mode 100644
index 000000000..071e8a314
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimagecapturecontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAMERAIMAGECAPTURECONTROL_H
+#define QANDROIDCAMERAIMAGECAPTURECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraimagecapturecontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCameraImageCaptureControl : public QCameraImageCaptureControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraImageCaptureControl(QAndroidCameraSession *session);
+
+ bool isReadyForCapture() const override;
+
+ QCameraImageCapture::DriveMode driveMode() const override;
+ void setDriveMode(QCameraImageCapture::DriveMode mode) override;
+
+ int capture(const QString &fileName) override;
+ void cancelCapture() override;
+
+ QCameraImageCapture::CaptureDestinations captureDestination() const override;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override;
+
+private:
+ QAndroidCameraSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAIMAGECAPTURECONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp
new file mode 100644
index 000000000..f35e6cf6e
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcameraimageprocessingcontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCameraImageProcessingControl::QAndroidCameraImageProcessingControl(QAndroidCameraSession *session)
+ : QCameraImageProcessingControl()
+ , m_session(session)
+ , m_whiteBalanceMode(QCameraImageProcessing::WhiteBalanceAuto)
+{
+ connect(m_session, SIGNAL(opened()),
+ this, SLOT(onCameraOpened()));
+}
+
+bool QAndroidCameraImageProcessingControl::isParameterSupported(ProcessingParameter parameter) const
+{
+ return parameter == QCameraImageProcessingControl::WhiteBalancePreset
+ && m_session->camera()
+ && !m_supportedWhiteBalanceModes.isEmpty();
+}
+
+bool QAndroidCameraImageProcessingControl::isParameterValueSupported(ProcessingParameter parameter,
+ const QVariant &value) const
+{
+ return parameter == QCameraImageProcessingControl::WhiteBalancePreset
+ && m_session->camera()
+ && m_supportedWhiteBalanceModes.contains(value.value<QCameraImageProcessing::WhiteBalanceMode>());
+}
+
+QVariant QAndroidCameraImageProcessingControl::parameter(ProcessingParameter parameter) const
+{
+ if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
+ return QVariant();
+
+ return QVariant::fromValue(m_whiteBalanceMode);
+}
+
+void QAndroidCameraImageProcessingControl::setParameter(ProcessingParameter parameter, const QVariant &value)
+{
+ if (parameter != QCameraImageProcessingControl::WhiteBalancePreset)
+ return;
+
+ QCameraImageProcessing::WhiteBalanceMode mode = value.value<QCameraImageProcessing::WhiteBalanceMode>();
+
+ if (m_session->camera())
+ setWhiteBalanceModeHelper(mode);
+ else
+ m_whiteBalanceMode = mode;
+}
+
+void QAndroidCameraImageProcessingControl::setWhiteBalanceModeHelper(QCameraImageProcessing::WhiteBalanceMode mode)
+{
+ QString wb = m_supportedWhiteBalanceModes.value(mode, QString());
+ if (!wb.isEmpty()) {
+ m_session->camera()->setWhiteBalance(wb);
+ m_whiteBalanceMode = mode;
+ }
+}
+
+void QAndroidCameraImageProcessingControl::onCameraOpened()
+{
+ m_supportedWhiteBalanceModes.clear();
+ QStringList whiteBalanceModes = m_session->camera()->getSupportedWhiteBalance();
+ for (int i = 0; i < whiteBalanceModes.size(); ++i) {
+ const QString &wb = whiteBalanceModes.at(i);
+ if (wb == QLatin1String("auto")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceAuto,
+ QStringLiteral("auto"));
+ } else if (wb == QLatin1String("cloudy-daylight")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceCloudy,
+ QStringLiteral("cloudy-daylight"));
+ } else if (wb == QLatin1String("daylight")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceSunlight,
+ QStringLiteral("daylight"));
+ } else if (wb == QLatin1String("fluorescent")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceFluorescent,
+ QStringLiteral("fluorescent"));
+ } else if (wb == QLatin1String("incandescent")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceTungsten,
+ QStringLiteral("incandescent"));
+ } else if (wb == QLatin1String("shade")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceShade,
+ QStringLiteral("shade"));
+ } else if (wb == QLatin1String("twilight")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceSunset,
+ QStringLiteral("twilight"));
+ } else if (wb == QLatin1String("warm-fluorescent")) {
+ m_supportedWhiteBalanceModes.insert(QCameraImageProcessing::WhiteBalanceFlash,
+ QStringLiteral("warm-fluorescent"));
+ }
+ }
+
+ if (!m_supportedWhiteBalanceModes.contains(m_whiteBalanceMode))
+ m_whiteBalanceMode = QCameraImageProcessing::WhiteBalanceAuto;
+
+ setWhiteBalanceModeHelper(m_whiteBalanceMode);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h
new file mode 100644
index 000000000..370c8dfb6
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameraimageprocessingcontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAMERAIMAGEPROCESSINGCONTROL_H
+#define QANDROIDCAMERAIMAGEPROCESSINGCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcameraimageprocessingcontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCameraImageProcessingControl : public QCameraImageProcessingControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraImageProcessingControl(QAndroidCameraSession *session);
+
+ bool isParameterSupported(ProcessingParameter) const override;
+ bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const override;
+ QVariant parameter(ProcessingParameter parameter) const override;
+ void setParameter(ProcessingParameter parameter, const QVariant &value) override;
+
+private Q_SLOTS:
+ void onCameraOpened();
+
+private:
+ void setWhiteBalanceModeHelper(QCameraImageProcessing::WhiteBalanceMode mode);
+
+ QAndroidCameraSession *m_session;
+
+ QCameraImageProcessing::WhiteBalanceMode m_whiteBalanceMode;
+
+ QMap<QCameraImageProcessing::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAIMAGEPROCESSINGCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
new file mode 100644
index 000000000..f261d86e2
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp
@@ -0,0 +1,939 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcamerasession_p.h"
+
+#include "androidcamera_p.h"
+#include "androidmultimediautils_p.h"
+#include "qandroidvideooutput_p.h"
+#include "qandroidmediavideoprobecontrol_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidcameravideorenderercontrol_p.h"
+#include <qabstractvideosurface.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+#include <qfile.h>
+#include <qguiapplication.h>
+#include <qdebug.h>
+#include <qvideoframe.h>
+#include <private/qmemoryvideobuffer_p.h>
+#include <QtCore/private/qjnihelpers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QList<AndroidCameraInfo>, g_availableCameras)
+
+QAndroidCameraSession::QAndroidCameraSession(QObject *parent)
+ : QObject(parent)
+ , m_selectedCamera(0)
+ , m_camera(0)
+ , m_nativeOrientation(0)
+ , m_videoOutput(0)
+ , m_captureMode(QCamera::CaptureStillImage)
+ , m_state(QCamera::UnloadedState)
+ , m_savedState(-1)
+ , m_status(QCamera::UnloadedStatus)
+ , m_previewStarted(false)
+ , m_captureDestination(QCameraImageCapture::CaptureToFile)
+ , m_captureImageDriveMode(QCameraImageCapture::SingleImageCapture)
+ , m_lastImageCaptureId(0)
+ , m_readyForCapture(false)
+ , m_captureCanceled(false)
+ , m_currentImageCaptureId(-1)
+ , m_previewCallback(0)
+ , m_keepActive(false)
+{
+ m_mediaStorageLocation.addStorageLocation(
+ QMediaStorageLocation::Pictures,
+ AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM));
+
+ if (qApp) {
+ connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
+ this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
+ }
+}
+
+QAndroidCameraSession::~QAndroidCameraSession()
+{
+ close();
+}
+
+void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode)
+{
+ if (m_captureMode == mode || !isCaptureModeSupported(mode))
+ return;
+
+ m_captureMode = mode;
+ emit captureModeChanged(m_captureMode);
+
+ if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage))
+ applyViewfinderSettings(m_actualImageSettings.resolution());
+}
+
+bool QAndroidCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ if (mode & (QCamera::CaptureStillImage & QCamera::CaptureVideo))
+ return false;
+
+ return true;
+}
+
+void QAndroidCameraSession::setState(QCamera::State state)
+{
+ if (m_state == state)
+ return;
+
+ m_state = state;
+ emit stateChanged(m_state);
+
+ // If the application is inactive, the camera shouldn't be started. Save the desired state
+ // instead and it will be set when the application becomes active.
+ if (qApp->applicationState() == Qt::ApplicationActive)
+ setStateHelper(state);
+ else
+ m_savedState = state;
+}
+
+void QAndroidCameraSession::setStateHelper(QCamera::State state)
+{
+ switch (state) {
+ case QCamera::UnloadedState:
+ close();
+ break;
+ case QCamera::LoadedState:
+ case QCamera::ActiveState:
+ if (!m_camera && !open()) {
+ m_state = QCamera::UnloadedState;
+ emit stateChanged(m_state);
+ emit error(QCamera::CameraError, QStringLiteral("Failed to open camera"));
+ m_status = QCamera::UnloadedStatus;
+ emit statusChanged(m_status);
+ return;
+ }
+ if (state == QCamera::ActiveState)
+ startPreview();
+ else if (state == QCamera::LoadedState)
+ stopPreview();
+ break;
+ }
+}
+
+void QAndroidCameraSession::updateAvailableCameras()
+{
+ g_availableCameras->clear();
+
+ const int numCameras = AndroidCamera::getNumberOfCameras();
+ for (int i = 0; i < numCameras; ++i) {
+ AndroidCameraInfo info;
+ AndroidCamera::getCameraInfo(i, &info);
+
+ if (!info.name.isNull())
+ g_availableCameras->append(info);
+ }
+}
+
+const QList<AndroidCameraInfo> &QAndroidCameraSession::availableCameras()
+{
+ if (g_availableCameras->isEmpty())
+ updateAvailableCameras();
+
+ return *g_availableCameras;
+}
+
+bool QAndroidCameraSession::open()
+{
+ close();
+
+ m_status = QCamera::LoadingStatus;
+ emit statusChanged(m_status);
+
+ m_camera = AndroidCamera::open(m_selectedCamera);
+
+ if (m_camera) {
+ connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed()));
+ connect(m_camera, SIGNAL(lastPreviewFrameFetched(QVideoFrame)),
+ this, SLOT(onLastPreviewFrameFetched(QVideoFrame)),
+ Qt::DirectConnection);
+ connect(m_camera, SIGNAL(newPreviewFrame(QVideoFrame)),
+ this, SLOT(onNewPreviewFrame(QVideoFrame)),
+ Qt::DirectConnection);
+ connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray)));
+ connect(m_camera, SIGNAL(previewStarted()), this, SLOT(onCameraPreviewStarted()));
+ connect(m_camera, SIGNAL(previewStopped()), this, SLOT(onCameraPreviewStopped()));
+ connect(m_camera, &AndroidCamera::previewFailedToStart, this, &QAndroidCameraSession::onCameraPreviewFailedToStart);
+ connect(m_camera, &AndroidCamera::takePictureFailed, this, &QAndroidCameraSession::onCameraTakePictureFailed);
+
+ m_nativeOrientation = m_camera->getNativeOrientation();
+
+ m_status = QCamera::LoadedStatus;
+
+ if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
+ m_camera->setPreviewFormat(AndroidCamera::NV21);
+
+ m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
+
+ emit opened();
+ emit statusChanged(m_status);
+ }
+
+ return m_camera != 0;
+}
+
+void QAndroidCameraSession::close()
+{
+ if (!m_camera)
+ return;
+
+ stopPreview();
+
+ m_status = QCamera::UnloadingStatus;
+ emit statusChanged(m_status);
+
+ m_readyForCapture = false;
+ m_currentImageCaptureId = -1;
+ m_currentImageCaptureFileName.clear();
+ m_actualImageSettings = m_requestedImageSettings;
+ m_actualViewfinderSettings = m_requestedViewfinderSettings;
+
+ m_camera->release();
+ delete m_camera;
+ m_camera = 0;
+
+ m_status = QCamera::UnloadedStatus;
+ emit statusChanged(m_status);
+}
+
+void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output)
+{
+ if (m_videoOutput) {
+ m_videoOutput->stop();
+ m_videoOutput->reset();
+ }
+
+ if (output) {
+ m_videoOutput = output;
+ if (m_videoOutput->isReady())
+ onVideoOutputReady(true);
+ else
+ connect(m_videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
+ } else {
+ m_videoOutput = 0;
+ }
+}
+
+void QAndroidCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings)
+{
+ if (m_requestedViewfinderSettings == settings)
+ return;
+
+ m_requestedViewfinderSettings = m_actualViewfinderSettings = settings;
+
+ if (m_readyForCapture)
+ applyViewfinderSettings();
+}
+
+void QAndroidCameraSession::applyViewfinderSettings(const QSize &captureSize, bool restartPreview)
+{
+ if (!m_camera)
+ return;
+
+ const QSize currentViewfinderResolution = m_camera->previewSize();
+ const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat();
+ const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange();
+
+ // -- adjust resolution
+ QSize adjustedViewfinderResolution;
+ const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0;
+ if (m_captureMode.testFlag(QCamera::CaptureVideo)
+ && validCaptureSize
+ && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) {
+ // According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means
+ // the preview size cannot be different from the capture size
+ adjustedViewfinderResolution = captureSize;
+ } else {
+ qreal captureAspectRatio = 0;
+ if (validCaptureSize)
+ captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height());
+
+ const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes();
+
+ const QSize vfRes = m_requestedViewfinderSettings.resolution();
+ if (vfRes.width() > 0 && vfRes.height() > 0
+ && (!validCaptureSize || qAbs(captureAspectRatio - (qreal(vfRes.width()) / vfRes.height())) < 0.01)
+ && previewSizes.contains(vfRes)) {
+ adjustedViewfinderResolution = vfRes;
+ } else if (validCaptureSize) {
+ // search for viewfinder resolution with the same aspect ratio
+ qreal minAspectDiff = 1;
+ QSize closestResolution;
+ for (int i = previewSizes.count() - 1; i >= 0; --i) {
+ const QSize &size = previewSizes.at(i);
+ const qreal sizeAspect = qreal(size.width()) / size.height();
+ if (qFuzzyCompare(captureAspectRatio, sizeAspect)) {
+ adjustedViewfinderResolution = size;
+ break;
+ } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) {
+ closestResolution = size;
+ minAspectDiff = qAbs(sizeAspect - captureAspectRatio);
+ }
+ }
+ if (!adjustedViewfinderResolution.isValid()) {
+ qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio.");
+ if (closestResolution.isValid()) {
+ adjustedViewfinderResolution = closestResolution;
+ qWarning("Using closest viewfinder resolution.");
+ } else {
+ return;
+ }
+ }
+ } else {
+ adjustedViewfinderResolution = previewSizes.last();
+ }
+ }
+ m_actualViewfinderSettings.setResolution(adjustedViewfinderResolution);
+
+ // -- adjust pixel format
+
+ AndroidCamera::ImageFormat adjustedPreviewFormat = AndroidCamera::NV21;
+ if (m_requestedViewfinderSettings.pixelFormat() != QVideoFrame::Format_Invalid) {
+ const AndroidCamera::ImageFormat f = AndroidImageFormatFromQtPixelFormat(m_requestedViewfinderSettings.pixelFormat());
+ if (f == AndroidCamera::UnknownImageFormat || !m_camera->getSupportedPreviewFormats().contains(f))
+ qWarning("Unsupported viewfinder pixel format");
+ else
+ adjustedPreviewFormat = f;
+ }
+ m_actualViewfinderSettings.setPixelFormat(QtPixelFormatFromAndroidImageFormat(adjustedPreviewFormat));
+
+ // -- adjust FPS
+
+ AndroidCamera::FpsRange adjustedFps = currentFpsRange;
+ const AndroidCamera::FpsRange requestedFpsRange = AndroidCamera::FpsRange::makeFromQReal(m_requestedViewfinderSettings.minimumFrameRate(),
+ m_requestedViewfinderSettings.maximumFrameRate());
+ if (requestedFpsRange.min > 0 || requestedFpsRange.max > 0) {
+ int minDist = INT_MAX;
+ const QList<AndroidCamera::FpsRange> supportedFpsRanges = m_camera->getSupportedPreviewFpsRange();
+ auto it = supportedFpsRanges.rbegin(), end = supportedFpsRanges.rend();
+ for (; it != end; ++it) {
+ int dist = (requestedFpsRange.min > 0 ? qAbs(requestedFpsRange.min - it->min) : 0)
+ + (requestedFpsRange.max > 0 ? qAbs(requestedFpsRange.max - it->max) : 0);
+ if (dist < minDist) {
+ minDist = dist;
+ adjustedFps = *it;
+ if (minDist == 0)
+ break; // exact match
+ }
+ }
+ }
+ m_actualViewfinderSettings.setMinimumFrameRate(adjustedFps.getMinReal());
+ m_actualViewfinderSettings.setMaximumFrameRate(adjustedFps.getMaxReal());
+
+ // -- Set values on camera
+
+ if (currentViewfinderResolution != adjustedViewfinderResolution
+ || currentPreviewFormat != adjustedPreviewFormat
+ || currentFpsRange.min != adjustedFps.min
+ || currentFpsRange.max != adjustedFps.max) {
+
+ if (m_videoOutput)
+ m_videoOutput->setVideoSize(adjustedViewfinderResolution);
+
+ // if preview is started, we have to stop it first before changing its size
+ if (m_previewStarted && restartPreview)
+ m_camera->stopPreview();
+
+ m_camera->setPreviewSize(adjustedViewfinderResolution);
+ m_camera->setPreviewFormat(adjustedPreviewFormat);
+ m_camera->setPreviewFpsRange(adjustedFps);
+
+ // restart preview
+ if (m_previewStarted && restartPreview)
+ m_camera->startPreview();
+ }
+}
+
+QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const
+{
+ return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>();
+}
+
+QList<QVideoFrame::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const
+{
+ QList<QVideoFrame::PixelFormat> formats;
+
+ if (!m_camera)
+ return formats;
+
+ const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats();
+
+ formats.reserve(nativeFormats.size());
+
+ for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) {
+ QVideoFrame::PixelFormat format = QtPixelFormatFromAndroidImageFormat(nativeFormat);
+ if (format != QVideoFrame::Format_Invalid)
+ formats.append(format);
+ }
+
+ return formats;
+}
+
+QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const
+{
+ return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>();
+}
+
+struct NullSurface : QAbstractVideoSurface
+{
+ NullSurface(QObject *parent = nullptr) : QAbstractVideoSurface(parent) { }
+ QList<QVideoFrame::PixelFormat> supportedPixelFormats(
+ QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override
+ {
+ QList<QVideoFrame::PixelFormat> result;
+ if (type == QAbstractVideoBuffer::NoHandle)
+ result << QVideoFrame::Format_NV21;
+
+ return result;
+ }
+
+ bool present(const QVideoFrame &) { return false; }
+};
+
+bool QAndroidCameraSession::startPreview()
+{
+ if (!m_camera)
+ return false;
+
+ if (m_previewStarted)
+ return true;
+
+ if (m_videoOutput) {
+ if (!m_videoOutput->isReady())
+ return true; // delay starting until the video output is ready
+
+ Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder());
+
+ if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture()))
+ || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder())))
+ return false;
+ } else {
+ auto control = new QAndroidCameraVideoRendererControl(this, this);
+ control->setSurface(new NullSurface(this));
+ qWarning() << "Starting camera without viewfinder available";
+
+ return true;
+ }
+
+ m_status = QCamera::StartingStatus;
+ emit statusChanged(m_status);
+
+ applyImageSettings();
+ applyViewfinderSettings(m_captureMode.testFlag(QCamera::CaptureStillImage) ? m_actualImageSettings.resolution()
+ : QSize());
+
+ AndroidMultimediaUtils::enableOrientationListener(true);
+
+ // Before API level 24 the orientation was always 0, which is what we're expecting, so
+ // we'll enforce that here.
+ if (QtAndroidPrivate::androidSdkVersion() > 23)
+ m_camera->setDisplayOrientation(0);
+
+ m_camera->startPreview();
+ m_previewStarted = true;
+
+ return true;
+}
+
+void QAndroidCameraSession::stopPreview()
+{
+ if (!m_camera || !m_previewStarted)
+ return;
+
+ m_status = QCamera::StoppingStatus;
+ emit statusChanged(m_status);
+
+ AndroidMultimediaUtils::enableOrientationListener(false);
+
+ m_camera->stopPreview();
+ m_camera->setPreviewSize(QSize());
+ m_camera->setPreviewTexture(0);
+ m_camera->setPreviewDisplay(0);
+
+ if (m_videoOutput) {
+ m_videoOutput->stop();
+ m_videoOutput->reset();
+ }
+ m_previewStarted = false;
+}
+
+void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings)
+{
+ if (m_requestedImageSettings == settings)
+ return;
+
+ m_requestedImageSettings = m_actualImageSettings = settings;
+
+ applyImageSettings();
+
+ if (m_readyForCapture && m_captureMode.testFlag(QCamera::CaptureStillImage))
+ applyViewfinderSettings(m_actualImageSettings.resolution());
+}
+
+int QAndroidCameraSession::currentCameraRotation() const
+{
+ if (!m_camera)
+ return 0;
+
+ // subtract natural camera orientation and physical device orientation
+ int rotation = 0;
+ int deviceOrientation = (AndroidMultimediaUtils::getDeviceOrientation() + 45) / 90 * 90;
+ if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
+ rotation = (m_nativeOrientation - deviceOrientation + 360) % 360;
+ else // back-facing camera
+ rotation = (m_nativeOrientation + deviceOrientation) % 360;
+
+ return rotation;
+}
+
+void QAndroidCameraSession::addProbe(QAndroidMediaVideoProbeControl *probe)
+{
+ m_videoProbesMutex.lock();
+ if (probe)
+ m_videoProbes << probe;
+ if (m_camera)
+ m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
+ m_videoProbesMutex.unlock();
+}
+
+void QAndroidCameraSession::removeProbe(QAndroidMediaVideoProbeControl *probe)
+{
+ m_videoProbesMutex.lock();
+ m_videoProbes.remove(probe);
+ if (m_camera)
+ m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
+ m_videoProbesMutex.unlock();
+}
+
+void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format)
+{
+ if (format == AndroidCamera::UnknownImageFormat)
+ return;
+
+ m_camera->setPreviewFormat(format);
+}
+
+void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback)
+{
+ m_videoProbesMutex.lock();
+ m_previewCallback = callback;
+ if (m_camera)
+ m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback);
+ m_videoProbesMutex.unlock();
+}
+
+void QAndroidCameraSession::applyImageSettings()
+{
+ if (!m_camera)
+ return;
+
+ if (m_actualImageSettings.codec().isEmpty())
+ m_actualImageSettings.setCodec(QLatin1String("jpeg"));
+
+ const QSize requestedResolution = m_requestedImageSettings.resolution();
+ const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes();
+ if (!requestedResolution.isValid()) {
+ // if the viewfinder resolution is explicitly set, pick the highest available capture
+ // resolution with the same aspect ratio
+ if (m_requestedViewfinderSettings.resolution().isValid()) {
+ const QSize vfResolution = m_actualViewfinderSettings.resolution();
+ const qreal vfAspectRatio = qreal(vfResolution.width()) / vfResolution.height();
+
+ auto it = supportedResolutions.rbegin(), end = supportedResolutions.rend();
+ for (; it != end; ++it) {
+ if (qAbs(vfAspectRatio - (qreal(it->width()) / it->height())) < 0.01) {
+ m_actualImageSettings.setResolution(*it);
+ break;
+ }
+ }
+ } else {
+ // otherwise, use the highest supported one
+ m_actualImageSettings.setResolution(supportedResolutions.last());
+ }
+ } else if (!supportedResolutions.contains(requestedResolution)) {
+ // if the requested resolution is not supported, find the closest one
+ int reqPixelCount = requestedResolution.width() * requestedResolution.height();
+ QList<int> supportedPixelCounts;
+ for (int i = 0; i < supportedResolutions.size(); ++i) {
+ const QSize &s = supportedResolutions.at(i);
+ supportedPixelCounts.append(s.width() * s.height());
+ }
+ int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
+ m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex));
+ }
+ m_camera->setPictureSize(m_actualImageSettings.resolution());
+
+ int jpegQuality = 100;
+ switch (m_requestedImageSettings.quality()) {
+ case QMultimedia::VeryLowQuality:
+ jpegQuality = 20;
+ break;
+ case QMultimedia::LowQuality:
+ jpegQuality = 40;
+ break;
+ case QMultimedia::NormalQuality:
+ jpegQuality = 60;
+ break;
+ case QMultimedia::HighQuality:
+ jpegQuality = 80;
+ break;
+ case QMultimedia::VeryHighQuality:
+ jpegQuality = 100;
+ break;
+ }
+ m_camera->setJpegQuality(jpegQuality);
+}
+
+bool QAndroidCameraSession::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const
+{
+ return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer);
+}
+
+QCameraImageCapture::CaptureDestinations QAndroidCameraSession::captureDestination() const
+{
+ return m_captureDestination;
+}
+
+void QAndroidCameraSession::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination)
+{
+ m_captureDestination = destination;
+}
+
+bool QAndroidCameraSession::isReadyForCapture() const
+{
+ return m_status == QCamera::ActiveStatus && m_readyForCapture;
+}
+
+void QAndroidCameraSession::setReadyForCapture(bool ready)
+{
+ if (m_readyForCapture == ready)
+ return;
+
+ m_readyForCapture = ready;
+ emit readyForCaptureChanged(ready);
+}
+
+QCameraImageCapture::DriveMode QAndroidCameraSession::driveMode() const
+{
+ return m_captureImageDriveMode;
+}
+
+void QAndroidCameraSession::setDriveMode(QCameraImageCapture::DriveMode mode)
+{
+ m_captureImageDriveMode = mode;
+}
+
+int QAndroidCameraSession::capture(const QString &fileName)
+{
+ ++m_lastImageCaptureId;
+
+ if (!isReadyForCapture()) {
+ emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotReadyError,
+ tr("Camera not ready"));
+ return m_lastImageCaptureId;
+ }
+
+ if (m_captureImageDriveMode == QCameraImageCapture::SingleImageCapture) {
+ setReadyForCapture(false);
+
+ m_currentImageCaptureId = m_lastImageCaptureId;
+ m_currentImageCaptureFileName = fileName;
+
+ applyImageSettings();
+ applyViewfinderSettings(m_actualImageSettings.resolution());
+
+ // adjust picture rotation depending on the device orientation
+ m_camera->setRotation(currentCameraRotation());
+
+ m_camera->takePicture();
+ } else {
+ //: Drive mode is the camera's shutter mode, for example single shot, continuos exposure, etc.
+ emit imageCaptureError(m_lastImageCaptureId, QCameraImageCapture::NotSupportedFeatureError,
+ tr("Drive mode not supported"));
+ }
+
+ return m_lastImageCaptureId;
+}
+
+void QAndroidCameraSession::cancelCapture()
+{
+ if (m_readyForCapture)
+ return;
+
+ m_captureCanceled = true;
+}
+
+void QAndroidCameraSession::onCameraTakePictureFailed()
+{
+ emit imageCaptureError(m_currentImageCaptureId, QCameraImageCapture::ResourceError,
+ tr("Failed to capture image"));
+
+ // Preview needs to be restarted and the preview call back must be setup again
+ m_camera->startPreview();
+}
+
+void QAndroidCameraSession::onCameraPictureExposed()
+{
+ if (m_captureCanceled || !m_camera)
+ return;
+
+ emit imageExposed(m_currentImageCaptureId);
+ m_camera->fetchLastPreviewFrame();
+}
+
+void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
+{
+ if (m_captureCanceled || !m_camera)
+ return;
+
+ QtConcurrent::run(&QAndroidCameraSession::processPreviewImage, this,
+ m_currentImageCaptureId,
+ frame,
+ m_camera->getRotation());
+}
+
+void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation)
+{
+ // Preview display of front-facing cameras is flipped horizontally, but the frame data
+ // we get here is not. Flip it ourselves if the camera is front-facing to match what the user
+ // sees on the viewfinder.
+ QTransform transform;
+ if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
+ transform.scale(-1, 1);
+ transform.rotate(rotation);
+
+ emit imageCaptured(id, frame.image().transformed(transform));
+}
+
+void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame)
+{
+ if (!m_camera)
+ return;
+
+ m_videoProbesMutex.lock();
+
+ for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes))
+ probe->newFrameProbed(frame);
+
+ if (m_previewCallback)
+ m_previewCallback->onFrameAvailable(frame);
+
+ m_videoProbesMutex.unlock();
+}
+
+void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data)
+{
+ if (!m_captureCanceled) {
+ // Loading and saving the captured image can be slow, do it in a separate thread
+ QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this,
+ m_currentImageCaptureId,
+ data,
+ m_actualImageSettings.resolution(),
+ m_captureDestination,
+ m_currentImageCaptureFileName);
+ }
+
+ m_captureCanceled = false;
+
+ // Preview needs to be restarted after taking a picture
+ if (m_camera)
+ m_camera->startPreview();
+}
+
+void QAndroidCameraSession::onCameraPreviewStarted()
+{
+ if (m_status == QCamera::StartingStatus) {
+ m_status = QCamera::ActiveStatus;
+ emit statusChanged(m_status);
+ }
+
+ setReadyForCapture(true);
+}
+
+void QAndroidCameraSession::onCameraPreviewFailedToStart()
+{
+ if (m_status == QCamera::StartingStatus) {
+ Q_EMIT error(QCamera::CameraError, tr("Camera preview failed to start."));
+
+ AndroidMultimediaUtils::enableOrientationListener(false);
+ m_camera->setPreviewSize(QSize());
+ m_camera->setPreviewTexture(0);
+ if (m_videoOutput) {
+ m_videoOutput->stop();
+ m_videoOutput->reset();
+ }
+ m_previewStarted = false;
+
+ m_status = QCamera::LoadedStatus;
+ emit statusChanged(m_status);
+
+ setReadyForCapture(false);
+ }
+}
+
+void QAndroidCameraSession::onCameraPreviewStopped()
+{
+ if (m_status == QCamera::StoppingStatus) {
+ m_status = QCamera::LoadedStatus;
+ emit statusChanged(m_status);
+ }
+
+ setReadyForCapture(false);
+}
+
+void QAndroidCameraSession::processCapturedImage(int id,
+ const QByteArray &data,
+ const QSize &resolution,
+ QCameraImageCapture::CaptureDestinations dest,
+ const QString &fileName)
+{
+
+
+ if (dest & QCameraImageCapture::CaptureToFile) {
+ const QString actualFileName = m_mediaStorageLocation.generateFileName(fileName,
+ QMediaStorageLocation::Pictures,
+ QLatin1String("IMG_"),
+ QLatin1String("jpg"));
+
+ QFile file(actualFileName);
+ if (file.open(QFile::WriteOnly)) {
+ if (file.write(data) == data.size()) {
+ // if the picture is saved into the standard picture location, register it
+ // with the Android media scanner so it appears immediately in apps
+ // such as the gallery.
+ QString standardLoc = AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM);
+ if (actualFileName.startsWith(standardLoc))
+ AndroidMultimediaUtils::registerMediaFile(actualFileName);
+
+ emit imageSaved(id, actualFileName);
+ } else {
+ emit imageCaptureError(id, QCameraImageCapture::OutOfSpaceError, file.errorString());
+ }
+ } else {
+ const QString errorMessage = tr("Could not open destination file: %1").arg(actualFileName);
+ emit imageCaptureError(id, QCameraImageCapture::ResourceError, errorMessage);
+ }
+ }
+
+ if (dest & QCameraImageCapture::CaptureToBuffer) {
+ QVideoFrame frame(new QMemoryVideoBuffer(data, -1), resolution, QVideoFrame::Format_Jpeg);
+ emit imageAvailable(id, frame);
+ }
+}
+
+QVideoFrame::PixelFormat QAndroidCameraSession::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format)
+{
+ switch (format) {
+ case AndroidCamera::RGB565:
+ return QVideoFrame::Format_RGB565;
+ case AndroidCamera::NV21:
+ return QVideoFrame::Format_NV21;
+ case AndroidCamera::YUY2:
+ return QVideoFrame::Format_YUYV;
+ case AndroidCamera::JPEG:
+ return QVideoFrame::Format_Jpeg;
+ case AndroidCamera::YV12:
+ return QVideoFrame::Format_YV12;
+ default:
+ return QVideoFrame::Format_Invalid;
+ }
+}
+
+AndroidCamera::ImageFormat QAndroidCameraSession::AndroidImageFormatFromQtPixelFormat(QVideoFrame::PixelFormat format)
+{
+ switch (format) {
+ case QVideoFrame::Format_RGB565:
+ return AndroidCamera::RGB565;
+ case QVideoFrame::Format_NV21:
+ return AndroidCamera::NV21;
+ case QVideoFrame::Format_YUYV:
+ return AndroidCamera::YUY2;
+ case QVideoFrame::Format_Jpeg:
+ return AndroidCamera::JPEG;
+ case QVideoFrame::Format_YV12:
+ return AndroidCamera::YV12;
+ default:
+ return AndroidCamera::UnknownImageFormat;
+ }
+}
+
+void QAndroidCameraSession::onVideoOutputReady(bool ready)
+{
+ if (ready && m_state == QCamera::ActiveState)
+ startPreview();
+}
+
+void QAndroidCameraSession::onApplicationStateChanged(Qt::ApplicationState state)
+{
+ switch (state) {
+ case Qt::ApplicationInactive:
+ if (!m_keepActive && m_state != QCamera::UnloadedState) {
+ m_savedState = m_state;
+ close();
+ m_state = QCamera::UnloadedState;
+ emit stateChanged(m_state);
+ }
+ break;
+ case Qt::ApplicationActive:
+ if (m_savedState != -1) {
+ setStateHelper(QCamera::State(m_savedState));
+ m_savedState = -1;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool QAndroidCameraSession::requestRecordingPermission()
+{
+ m_keepActive = true;
+ const bool result = qt_androidRequestRecordingPermission();
+ m_keepActive = false;
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
new file mode 100644
index 000000000..5d4c06738
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAMERASESSION_H
+#define QANDROIDCAMERASESSION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qcamera.h>
+#include <qmediaencodersettings.h>
+#include <QCameraImageCapture>
+#include <QSet>
+#include <QMutex>
+#include <private/qmediastoragelocation_p.h>
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidVideoOutput;
+class QAndroidMediaVideoProbeControl;
+
+class QAndroidCameraSession : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraSession(QObject *parent = 0);
+ ~QAndroidCameraSession();
+
+ static const QList<AndroidCameraInfo> &availableCameras();
+
+ void setSelectedCamera(int cameraId) { m_selectedCamera = cameraId; }
+ AndroidCamera *camera() const { return m_camera; }
+
+ QCamera::State state() const { return m_state; }
+ void setState(QCamera::State state);
+
+ QCamera::Status status() const { return m_status; }
+
+ QCamera::CaptureModes captureMode() const { return m_captureMode; }
+ void setCaptureMode(QCamera::CaptureModes mode);
+ bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
+
+ QCameraViewfinderSettings viewfinderSettings() const { return m_actualViewfinderSettings; }
+ void setViewfinderSettings(const QCameraViewfinderSettings &settings);
+ void applyViewfinderSettings(const QSize &captureSize = QSize(), bool restartPreview = true);
+
+ QAndroidVideoOutput *videoOutput() const { return m_videoOutput; }
+ void setVideoOutput(QAndroidVideoOutput *output);
+
+ QList<QSize> getSupportedPreviewSizes() const;
+ QList<QVideoFrame::PixelFormat> getSupportedPixelFormats() const;
+ QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange() const;
+
+ QImageEncoderSettings imageSettings() const { return m_actualImageSettings; }
+ void setImageSettings(const QImageEncoderSettings &settings);
+
+ bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const;
+ QCameraImageCapture::CaptureDestinations captureDestination() const;
+ void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination);
+
+ bool isReadyForCapture() const;
+ void setReadyForCapture(bool ready);
+ QCameraImageCapture::DriveMode driveMode() const;
+ void setDriveMode(QCameraImageCapture::DriveMode mode);
+ int capture(const QString &fileName);
+ void cancelCapture();
+
+ int currentCameraRotation() const;
+
+ void addProbe(QAndroidMediaVideoProbeControl *probe);
+ void removeProbe(QAndroidMediaVideoProbeControl *probe);
+
+ void setPreviewFormat(AndroidCamera::ImageFormat format);
+
+ struct PreviewCallback
+ {
+ virtual void onFrameAvailable(const QVideoFrame &frame) = 0;
+ };
+ void setPreviewCallback(PreviewCallback *callback);
+ bool requestRecordingPermission();
+
+Q_SIGNALS:
+ void statusChanged(QCamera::Status status);
+ void stateChanged(QCamera::State);
+ void error(int error, const QString &errorString);
+ void captureModeChanged(QCamera::CaptureModes);
+ void opened();
+
+ void captureDestinationChanged(QCameraImageCapture::CaptureDestinations destination);
+
+ void readyForCaptureChanged(bool);
+ void imageExposed(int id);
+ void imageCaptured(int id, const QImage &preview);
+ void imageMetadataAvailable(int id, const QString &key, const QVariant &value);
+ void imageAvailable(int id, const QVideoFrame &buffer);
+ void imageSaved(int id, const QString &fileName);
+ void imageCaptureError(int id, int error, const QString &errorString);
+
+private Q_SLOTS:
+ void onVideoOutputReady(bool ready);
+
+ void onApplicationStateChanged(Qt::ApplicationState state);
+
+ void onCameraTakePictureFailed();
+ void onCameraPictureExposed();
+ void onCameraPictureCaptured(const QByteArray &data);
+ void onLastPreviewFrameFetched(const QVideoFrame &frame);
+ void onNewPreviewFrame(const QVideoFrame &frame);
+ void onCameraPreviewStarted();
+ void onCameraPreviewFailedToStart();
+ void onCameraPreviewStopped();
+
+private:
+ static void updateAvailableCameras();
+
+ bool open();
+ void close();
+
+ bool startPreview();
+ void stopPreview();
+
+ void applyImageSettings();
+
+ void processPreviewImage(int id, const QVideoFrame &frame, int rotation);
+ void processCapturedImage(int id,
+ const QByteArray &data,
+ const QSize &resolution,
+ QCameraImageCapture::CaptureDestinations dest,
+ const QString &fileName);
+
+ static QVideoFrame::PixelFormat QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat);
+ static AndroidCamera::ImageFormat AndroidImageFormatFromQtPixelFormat(QVideoFrame::PixelFormat);
+
+ void setStateHelper(QCamera::State state);
+
+ int m_selectedCamera;
+ AndroidCamera *m_camera;
+ int m_nativeOrientation;
+ QAndroidVideoOutput *m_videoOutput;
+
+ QCamera::CaptureModes m_captureMode;
+ QCamera::State m_state;
+ int m_savedState;
+ QCamera::Status m_status;
+ bool m_previewStarted;
+
+ QCameraViewfinderSettings m_requestedViewfinderSettings;
+ QCameraViewfinderSettings m_actualViewfinderSettings;
+
+ QImageEncoderSettings m_requestedImageSettings;
+ QImageEncoderSettings m_actualImageSettings;
+ QCameraImageCapture::CaptureDestinations m_captureDestination;
+ QCameraImageCapture::DriveMode m_captureImageDriveMode;
+ int m_lastImageCaptureId;
+ bool m_readyForCapture;
+ bool m_captureCanceled;
+ int m_currentImageCaptureId;
+ QString m_currentImageCaptureFileName;
+
+ QMediaStorageLocation m_mediaStorageLocation;
+
+ QSet<QAndroidMediaVideoProbeControl *> m_videoProbes;
+ QMutex m_videoProbesMutex;
+ PreviewCallback *m_previewCallback;
+ bool m_keepActive;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERASESSION_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp
new file mode 100644
index 000000000..6084ed43f
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol.cpp
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcameravideorenderercontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "qandroidvideooutput_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidmultimediautils_p.h"
+#include <qabstractvideosurface.h>
+#include <qvideosurfaceformat.h>
+#include <qcoreapplication.h>
+#include <qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraDataVideoOutput : public QAndroidVideoOutput
+ , public QAndroidCameraSession::PreviewCallback
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control);
+ ~QAndroidCameraDataVideoOutput() override;
+
+ AndroidSurfaceHolder *surfaceHolder() override;
+
+ bool isReady() override;
+
+ void stop() override;
+
+private Q_SLOTS:
+ void onSurfaceCreated();
+ void configureFormat();
+
+private:
+ void onFrameAvailable(const QVideoFrame &frame);
+ void presentFrame();
+ bool event(QEvent *);
+
+ QAndroidCameraVideoRendererControl *m_control;
+ AndroidSurfaceView *m_surfaceView;
+ QMutex m_mutex;
+ QVideoFrame::PixelFormat m_pixelFormat;
+ QVideoFrame m_lastFrame;
+};
+
+QAndroidCameraDataVideoOutput::QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control)
+ : QAndroidVideoOutput(control)
+ , m_control(control)
+ , m_pixelFormat(QVideoFrame::Format_Invalid)
+{
+ // The camera preview cannot be started unless we set a SurfaceTexture or a
+ // SurfaceHolder. In this case we don't actually care about either of these, but since
+ // we need to, we setup an offscreen dummy SurfaceView in order to be able to start
+ // the camera preview. We'll then be able to use setPreviewCallbackWithBuffer() to
+ // get the raw data.
+
+ m_surfaceView = new AndroidSurfaceView;
+
+ connect(m_surfaceView, &AndroidSurfaceView::surfaceCreated,
+ this, &QAndroidCameraDataVideoOutput::onSurfaceCreated);
+
+ m_surfaceView->setGeometry(-1, -1, 1, 1);
+ m_surfaceView->setVisible(true);
+
+ connect(m_control->cameraSession(), &QAndroidCameraSession::opened,
+ this, &QAndroidCameraDataVideoOutput::configureFormat);
+ connect(m_control->surface(), &QAbstractVideoSurface::supportedFormatsChanged,
+ this, &QAndroidCameraDataVideoOutput::configureFormat);
+ configureFormat();
+}
+
+QAndroidCameraDataVideoOutput::~QAndroidCameraDataVideoOutput()
+{
+ m_control->cameraSession()->setPreviewCallback(nullptr);
+ delete m_surfaceView;
+}
+
+AndroidSurfaceHolder *QAndroidCameraDataVideoOutput::surfaceHolder()
+{
+ return m_surfaceView->holder();
+}
+
+bool QAndroidCameraDataVideoOutput::isReady()
+{
+ return m_surfaceView->holder() && m_surfaceView->holder()->isSurfaceCreated();
+}
+
+void QAndroidCameraDataVideoOutput::onSurfaceCreated()
+{
+ emit readyChanged(true);
+}
+
+void QAndroidCameraDataVideoOutput::configureFormat()
+{
+ m_pixelFormat = QVideoFrame::Format_Invalid;
+
+ if (!m_control->cameraSession()->camera())
+ return;
+
+ QList<QVideoFrame::PixelFormat> surfaceFormats = m_control->surface()->supportedPixelFormats();
+ QList<AndroidCamera::ImageFormat> previewFormats = m_control->cameraSession()->camera()->getSupportedPreviewFormats();
+ for (int i = 0; i < surfaceFormats.size(); ++i) {
+ QVideoFrame::PixelFormat pixFormat = surfaceFormats.at(i);
+ AndroidCamera::ImageFormat f = qt_androidImageFormatFromPixelFormat(pixFormat);
+ if (previewFormats.contains(f)) {
+ m_pixelFormat = pixFormat;
+ break;
+ }
+ }
+
+ if (m_pixelFormat == QVideoFrame::Format_Invalid) {
+ m_control->cameraSession()->setPreviewCallback(nullptr);
+ qWarning("The video surface is not compatible with any format supported by the camera");
+ } else {
+ m_control->cameraSession()->setPreviewCallback(this);
+
+ if (m_control->cameraSession()->status() > QCamera::LoadedStatus)
+ m_control->cameraSession()->camera()->stopPreview();
+
+ m_control->cameraSession()->setPreviewFormat(qt_androidImageFormatFromPixelFormat(m_pixelFormat));
+
+ if (m_control->cameraSession()->status() > QCamera::LoadedStatus)
+ m_control->cameraSession()->camera()->startPreview();
+ }
+}
+
+void QAndroidCameraDataVideoOutput::stop()
+{
+ m_mutex.lock();
+ m_lastFrame = QVideoFrame();
+ m_mutex.unlock();
+
+ if (m_control->surface() && m_control->surface()->isActive())
+ m_control->surface()->stop();
+}
+
+void QAndroidCameraDataVideoOutput::onFrameAvailable(const QVideoFrame &frame)
+{
+ m_mutex.lock();
+ m_lastFrame = frame;
+ m_mutex.unlock();
+
+ if (thread() == QThread::currentThread())
+ presentFrame();
+ else
+ QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
+}
+
+bool QAndroidCameraDataVideoOutput::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ presentFrame();
+ return true;
+ }
+
+ return QObject::event(e);
+}
+
+void QAndroidCameraDataVideoOutput::presentFrame()
+{
+ Q_ASSERT(thread() == QThread::currentThread());
+
+ QMutexLocker locker(&m_mutex);
+
+ if (m_control->surface() && m_lastFrame.isValid() && m_lastFrame.pixelFormat() == m_pixelFormat) {
+
+ if (m_control->surface()->isActive() && (m_control->surface()->surfaceFormat().pixelFormat() != m_lastFrame.pixelFormat()
+ || m_control->surface()->surfaceFormat().frameSize() != m_lastFrame.size())) {
+ m_control->surface()->stop();
+ }
+
+ if (!m_control->surface()->isActive()) {
+ QVideoSurfaceFormat format(m_lastFrame.size(), m_lastFrame.pixelFormat(), m_lastFrame.handleType());
+ // Front camera frames are automatically mirrored when using SurfaceTexture or SurfaceView,
+ // but the buffers we get from the data callback are not. Tell the QAbstractVideoSurface
+ // that it needs to mirror the frames.
+ if (m_control->cameraSession()->camera()->getFacing() == AndroidCamera::CameraFacingFront)
+ format.setProperty("mirrored", true);
+
+ m_control->surface()->start(format);
+ }
+
+ if (m_control->surface()->isActive())
+ m_control->surface()->present(m_lastFrame);
+ }
+
+ m_lastFrame = QVideoFrame();
+}
+
+
+QAndroidCameraVideoRendererControl::QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent)
+ : QVideoRendererControl(parent)
+ , m_cameraSession(session)
+ , m_surface(0)
+ , m_textureOutput(0)
+ , m_dataOutput(0)
+{
+}
+
+QAndroidCameraVideoRendererControl::~QAndroidCameraVideoRendererControl()
+{
+ m_cameraSession->setVideoOutput(0);
+}
+
+QAbstractVideoSurface *QAndroidCameraVideoRendererControl::surface() const
+{
+ return m_surface;
+}
+
+void QAndroidCameraVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
+{
+ if (m_surface == surface)
+ return;
+
+ m_surface = surface;
+ QAndroidVideoOutput *oldOutput = m_textureOutput ? static_cast<QAndroidVideoOutput*>(m_textureOutput)
+ : static_cast<QAndroidVideoOutput*>(m_dataOutput);
+ QAndroidVideoOutput *newOutput = 0;
+
+ if (m_surface) {
+ if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()) {
+ if (!m_textureOutput) {
+ m_dataOutput = 0;
+ newOutput = m_textureOutput = new QAndroidTextureVideoOutput(this);
+ }
+ } else if (!m_dataOutput) {
+ m_textureOutput = 0;
+ newOutput = m_dataOutput = new QAndroidCameraDataVideoOutput(this);
+ }
+
+ if (m_textureOutput)
+ m_textureOutput->setSurface(m_surface);
+ }
+
+ if (newOutput != oldOutput) {
+ m_cameraSession->setVideoOutput(newOutput);
+ delete oldOutput;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "qandroidcameravideorenderercontrol.moc"
+
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h
new file mode 100644
index 000000000..0deaac943
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcameravideorenderercontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAMERAVIDEORENDERERCONTROL_H
+#define QANDROIDCAMERAVIDEORENDERERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qvideorenderercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+class QAndroidTextureVideoOutput;
+class QAndroidCameraDataVideoOutput;
+
+class QAndroidCameraVideoRendererControl : public QVideoRendererControl
+{
+ Q_OBJECT
+public:
+ QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent = 0);
+ ~QAndroidCameraVideoRendererControl() override;
+
+ QAbstractVideoSurface *surface() const override;
+ void setSurface(QAbstractVideoSurface *surface) override;
+
+ QAndroidCameraSession *cameraSession() const { return m_cameraSession; }
+
+private:
+ QAndroidCameraSession *m_cameraSession;
+ QAbstractVideoSurface *m_surface;
+ QAndroidTextureVideoOutput *m_textureOutput;
+ QAndroidCameraDataVideoOutput *m_dataOutput;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAMERAVIDEORENDERERCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp
new file mode 100644
index 000000000..79808956e
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcaptureservice_p.h"
+
+#include "qandroidmediarecordercontrol_p.h"
+#include "qandroidcapturesession_p.h"
+#include "qandroidcameracontrol_p.h"
+#include "qandroidvideodeviceselectorcontrol_p.h"
+#include "qandroidaudioinputselectorcontrol_p.h"
+#include "qandroidcamerasession_p.h"
+#include "qandroidcameravideorenderercontrol_p.h"
+#include "qandroidcameraexposurecontrol_p.h"
+#include "qandroidcamerafocuscontrol_p.h"
+#include "qandroidcameraimageprocessingcontrol_p.h"
+#include "qandroidimageencodercontrol_p.h"
+#include "qandroidcameraimagecapturecontrol_p.h"
+#include "qandroidaudioencodersettingscontrol_p.h"
+#include "qandroidvideoencodersettingscontrol_p.h"
+#include "qandroidmediacontainercontrol_p.h"
+#include "qandroidmediavideoprobecontrol_p.h"
+
+#include <qmediaserviceproviderplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCaptureService::QAndroidCaptureService(const QString &service, QObject *parent)
+ : QMediaService(parent)
+ , m_service(service)
+ , m_videoRendererControl(0)
+{
+ if (m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
+ m_cameraSession = new QAndroidCameraSession;
+ m_cameraControl = new QAndroidCameraControl(m_cameraSession);
+ m_videoInputControl = new QAndroidVideoDeviceSelectorControl(m_cameraSession);
+ m_cameraExposureControl = new QAndroidCameraExposureControl(m_cameraSession);
+ m_cameraFocusControl = new QAndroidCameraFocusControl(m_cameraSession);
+ m_cameraImageProcessingControl = new QAndroidCameraImageProcessingControl(m_cameraSession);
+ m_imageEncoderControl = new QAndroidImageEncoderControl(m_cameraSession);
+ m_imageCaptureControl = new QAndroidCameraImageCaptureControl(m_cameraSession);
+ m_audioInputControl = 0;
+ } else {
+ m_cameraSession = 0;
+ m_cameraControl = 0;
+ m_videoInputControl = 0;
+ m_cameraExposureControl = 0;
+ m_cameraFocusControl = 0;
+ m_cameraImageProcessingControl = 0;
+ m_imageEncoderControl = 0;
+ m_imageCaptureControl = 0;
+ m_videoEncoderSettingsControl = 0;
+ }
+
+ m_captureSession = new QAndroidCaptureSession(m_cameraSession);
+ m_recorderControl = new QAndroidMediaRecorderControl(m_captureSession);
+ m_audioEncoderSettingsControl = new QAndroidAudioEncoderSettingsControl(m_captureSession);
+ m_mediaContainerControl = new QAndroidMediaContainerControl(m_captureSession);
+
+ if (m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)) {
+ m_videoEncoderSettingsControl = new QAndroidVideoEncoderSettingsControl(m_captureSession);
+ } else {
+ m_audioInputControl = new QAndroidAudioInputSelectorControl(m_captureSession);
+ m_captureSession->setAudioInput(m_audioInputControl->defaultInput());
+ }
+}
+
+QAndroidCaptureService::~QAndroidCaptureService()
+{
+ delete m_audioEncoderSettingsControl;
+ delete m_videoEncoderSettingsControl;
+ delete m_mediaContainerControl;
+ delete m_recorderControl;
+ delete m_captureSession;
+ delete m_cameraControl;
+ delete m_audioInputControl;
+ delete m_videoInputControl;
+ delete m_videoRendererControl;
+ delete m_cameraExposureControl;
+ delete m_cameraFocusControl;
+ delete m_cameraImageProcessingControl;
+ delete m_imageEncoderControl;
+ delete m_imageCaptureControl;
+ delete m_cameraSession;
+}
+
+QObject *QAndroidCaptureService::requestControl(const char *name)
+{
+ if (qstrcmp(name, QMediaRecorderControl_iid) == 0)
+ return m_recorderControl;
+
+ if (qstrcmp(name, QMediaContainerControl_iid) == 0)
+ return m_mediaContainerControl;
+
+ if (qstrcmp(name, QAudioEncoderSettingsControl_iid) == 0)
+ return m_audioEncoderSettingsControl;
+
+ if (qstrcmp(name, QVideoEncoderSettingsControl_iid) == 0)
+ return m_videoEncoderSettingsControl;
+
+ if (qstrcmp(name, QCameraControl_iid) == 0)
+ return m_cameraControl;
+
+ if (qstrcmp(name, QAudioInputSelectorControl_iid) == 0)
+ return m_audioInputControl;
+
+ if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0)
+ return m_videoInputControl;
+
+ if (qstrcmp(name, QCameraExposureControl_iid) == 0)
+ return m_cameraExposureControl;
+
+ if (qstrcmp(name, QCameraFocusControl_iid) == 0)
+ return m_cameraFocusControl;
+
+ if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0)
+ return m_cameraImageProcessingControl;
+
+ if (qstrcmp(name, QImageEncoderControl_iid) == 0)
+ return m_imageEncoderControl;
+
+ if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
+ return m_imageCaptureControl;
+
+ if (qstrcmp(name, QVideoRendererControl_iid) == 0
+ && m_service == QLatin1String(Q_MEDIASERVICE_CAMERA)
+ && !m_videoRendererControl) {
+ m_videoRendererControl = new QAndroidCameraVideoRendererControl(m_cameraSession);
+ return m_videoRendererControl;
+ }
+
+ if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) {
+ QAndroidMediaVideoProbeControl *videoProbe = 0;
+ if (m_cameraSession) {
+ videoProbe = new QAndroidMediaVideoProbeControl(this);
+ m_cameraSession->addProbe(videoProbe);
+ }
+ return videoProbe;
+ }
+
+ return 0;
+}
+
+void QAndroidCaptureService::releaseControl(QObject *control)
+{
+ if (control) {
+ if (control == m_videoRendererControl) {
+ delete m_videoRendererControl;
+ m_videoRendererControl = 0;
+ return;
+ }
+
+ QAndroidMediaVideoProbeControl *videoProbe = qobject_cast<QAndroidMediaVideoProbeControl *>(control);
+ if (videoProbe) {
+ if (m_cameraSession)
+ m_cameraSession->removeProbe(videoProbe);
+ delete videoProbe;
+ return;
+ }
+ }
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h
new file mode 100644
index 000000000..e202c40c2
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcaptureservice_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAPTURESERVICE_H
+#define QANDROIDCAPTURESERVICE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediaservice.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaRecorderControl;
+class QAndroidCaptureSession;
+class QAndroidCameraControl;
+class QAndroidVideoDeviceSelectorControl;
+class QAndroidAudioInputSelectorControl;
+class QAndroidCameraSession;
+class QAndroidCameraVideoRendererControl;
+class QAndroidCameraExposureControl;
+class QAndroidCameraFocusControl;
+class QAndroidCameraImageProcessingControl;
+class QAndroidImageEncoderControl;
+class QAndroidCameraImageCaptureControl;
+class QAndroidAudioEncoderSettingsControl;
+class QAndroidVideoEncoderSettingsControl;
+class QAndroidMediaContainerControl;
+
+class QAndroidCaptureService : public QMediaService
+{
+ Q_OBJECT
+
+public:
+ explicit QAndroidCaptureService(const QString &service, QObject *parent = 0);
+ virtual ~QAndroidCaptureService();
+
+ QObject *requestControl(const char *name);
+ void releaseControl(QObject *);
+
+private:
+ QString m_service;
+
+ QAndroidMediaRecorderControl *m_recorderControl;
+ QAndroidCaptureSession *m_captureSession;
+ QAndroidCameraControl *m_cameraControl;
+ QAndroidVideoDeviceSelectorControl *m_videoInputControl;
+ QAndroidAudioInputSelectorControl *m_audioInputControl;
+ QAndroidCameraSession *m_cameraSession;
+ QAndroidCameraVideoRendererControl *m_videoRendererControl;
+ QAndroidCameraExposureControl *m_cameraExposureControl;
+ QAndroidCameraFocusControl *m_cameraFocusControl;
+ QAndroidCameraImageProcessingControl *m_cameraImageProcessingControl;
+ QAndroidImageEncoderControl *m_imageEncoderControl;
+ QAndroidCameraImageCaptureControl *m_imageCaptureControl;
+ QAndroidAudioEncoderSettingsControl *m_audioEncoderSettingsControl;
+ QAndroidVideoEncoderSettingsControl *m_videoEncoderSettingsControl;
+ QAndroidMediaContainerControl *m_mediaContainerControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAPTURESERVICE_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
new file mode 100644
index 000000000..67f748994
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp
@@ -0,0 +1,594 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidcapturesession_p.h"
+
+#include "androidcamera_p.h"
+#include "qandroidcamerasession_p.h"
+#include "androidmultimediautils_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidvideooutput_p.h"
+#include "qandroidglobal_p.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidCaptureSession::QAndroidCaptureSession(QAndroidCameraSession *cameraSession)
+ : QObject()
+ , m_mediaRecorder(0)
+ , m_cameraSession(cameraSession)
+ , m_audioSource(AndroidMediaRecorder::DefaultAudioSource)
+ , m_duration(0)
+ , m_state(QMediaRecorder::StoppedState)
+ , m_status(QMediaRecorder::UnloadedStatus)
+ , m_containerFormatDirty(true)
+ , m_videoSettingsDirty(true)
+ , m_audioSettingsDirty(true)
+ , m_outputFormat(AndroidMediaRecorder::DefaultOutputFormat)
+ , m_audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
+ , m_videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
+{
+ m_mediaStorageLocation.addStorageLocation(
+ QMediaStorageLocation::Movies,
+ AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM));
+
+ m_mediaStorageLocation.addStorageLocation(
+ QMediaStorageLocation::Sounds,
+ AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::Sounds));
+
+ if (cameraSession) {
+ connect(cameraSession, SIGNAL(opened()), this, SLOT(onCameraOpened()));
+ connect(cameraSession, &QAndroidCameraSession::statusChanged, this,
+ [this](QCamera::Status status) {
+ if (status == QCamera::UnavailableStatus) {
+ setState(QMediaRecorder::StoppedState);
+ setStatus(QMediaRecorder::UnavailableStatus);
+ return;
+ }
+
+ // Stop recording when stopping the camera.
+ if (status == QCamera::StoppingStatus) {
+ setState(QMediaRecorder::StoppedState);
+ setStatus(QMediaRecorder::UnloadedStatus);
+ return;
+ }
+
+ if (status == QCamera::LoadingStatus)
+ setStatus(QMediaRecorder::LoadingStatus);
+ });
+ connect(cameraSession, &QAndroidCameraSession::captureModeChanged, this,
+ [this](QCamera::CaptureModes mode) {
+ if (!mode.testFlag(QCamera::CaptureVideo)) {
+ setState(QMediaRecorder::StoppedState);
+ setStatus(QMediaRecorder::UnloadedStatus);
+ }
+ });
+ connect(cameraSession, &QAndroidCameraSession::readyForCaptureChanged, this,
+ [this](bool ready) {
+ if (ready)
+ setStatus(QMediaRecorder::LoadedStatus);
+ });
+ } else {
+ // Audio-only recording.
+ setStatus(QMediaRecorder::LoadedStatus);
+ }
+
+ m_notifyTimer.setInterval(1000);
+ connect(&m_notifyTimer, SIGNAL(timeout()), this, SLOT(updateDuration()));
+}
+
+QAndroidCaptureSession::~QAndroidCaptureSession()
+{
+ stop();
+ delete m_mediaRecorder;
+}
+
+void QAndroidCaptureSession::setAudioInput(const QString &input)
+{
+ if (m_audioInput == input)
+ return;
+
+ m_audioInput = input;
+
+ if (m_audioInput == QLatin1String("default"))
+ m_audioSource = AndroidMediaRecorder::DefaultAudioSource;
+ else if (m_audioInput == QLatin1String("mic"))
+ m_audioSource = AndroidMediaRecorder::Mic;
+ else if (m_audioInput == QLatin1String("voice_uplink"))
+ m_audioSource = AndroidMediaRecorder::VoiceUplink;
+ else if (m_audioInput == QLatin1String("voice_downlink"))
+ m_audioSource = AndroidMediaRecorder::VoiceDownlink;
+ else if (m_audioInput == QLatin1String("voice_call"))
+ m_audioSource = AndroidMediaRecorder::VoiceCall;
+ else if (m_audioInput == QLatin1String("voice_recognition"))
+ m_audioSource = AndroidMediaRecorder::VoiceRecognition;
+ else
+ m_audioSource = AndroidMediaRecorder::DefaultAudioSource;
+
+ emit audioInputChanged(m_audioInput);
+}
+
+QUrl QAndroidCaptureSession::outputLocation() const
+{
+ return m_actualOutputLocation;
+}
+
+bool QAndroidCaptureSession::setOutputLocation(const QUrl &location)
+{
+ if (m_requestedOutputLocation == location)
+ return false;
+
+ m_actualOutputLocation = QUrl();
+ m_requestedOutputLocation = location;
+
+ if (m_requestedOutputLocation.isEmpty())
+ return true;
+
+ if (m_requestedOutputLocation.isValid()
+ && (m_requestedOutputLocation.isLocalFile() || m_requestedOutputLocation.isRelative())) {
+ return true;
+ }
+
+ m_requestedOutputLocation = QUrl();
+ return false;
+}
+
+QMediaRecorder::State QAndroidCaptureSession::state() const
+{
+ return m_state;
+}
+
+void QAndroidCaptureSession::setState(QMediaRecorder::State state)
+{
+ if (m_state == state)
+ return;
+
+ switch (state) {
+ case QMediaRecorder::StoppedState:
+ stop();
+ break;
+ case QMediaRecorder::RecordingState:
+ start();
+ break;
+ case QMediaRecorder::PausedState:
+ // Not supported by Android API
+ qWarning("QMediaRecorder::PausedState is not supported on Android");
+ break;
+ }
+}
+
+void QAndroidCaptureSession::start()
+{
+ if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus)
+ return;
+
+ setStatus(QMediaRecorder::StartingStatus);
+
+ if (m_mediaRecorder) {
+ m_mediaRecorder->release();
+ delete m_mediaRecorder;
+ }
+
+ const bool granted = m_cameraSession
+ ? m_cameraSession->requestRecordingPermission()
+ : qt_androidRequestRecordingPermission();
+ if (!granted) {
+ setStatus(QMediaRecorder::UnavailableStatus);
+ Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("Permission denied."));
+ return;
+ }
+
+ m_mediaRecorder = new AndroidMediaRecorder;
+ connect(m_mediaRecorder, SIGNAL(error(int,int)), this, SLOT(onError(int,int)));
+ connect(m_mediaRecorder, SIGNAL(info(int,int)), this, SLOT(onInfo(int,int)));
+
+ // Set audio/video sources
+ if (m_cameraSession) {
+ updateViewfinder();
+ m_cameraSession->camera()->unlock();
+ m_mediaRecorder->setCamera(m_cameraSession->camera());
+ m_mediaRecorder->setAudioSource(AndroidMediaRecorder::Camcorder);
+ m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera);
+ } else {
+ m_mediaRecorder->setAudioSource(m_audioSource);
+ }
+
+ // Set output format
+ m_mediaRecorder->setOutputFormat(m_outputFormat);
+
+ // Set audio encoder settings
+ m_mediaRecorder->setAudioChannels(m_audioSettings.channelCount());
+ m_mediaRecorder->setAudioEncodingBitRate(m_audioSettings.bitRate());
+ m_mediaRecorder->setAudioSamplingRate(m_audioSettings.sampleRate());
+ m_mediaRecorder->setAudioEncoder(m_audioEncoder);
+
+ // Set video encoder settings
+ if (m_cameraSession) {
+ m_mediaRecorder->setVideoSize(m_videoSettings.resolution());
+ m_mediaRecorder->setVideoFrameRate(qRound(m_videoSettings.frameRate()));
+ m_mediaRecorder->setVideoEncodingBitRate(m_videoSettings.bitRate());
+ m_mediaRecorder->setVideoEncoder(m_videoEncoder);
+
+ m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation());
+ }
+
+ // Set output file
+ QString filePath = m_mediaStorageLocation.generateFileName(
+ m_requestedOutputLocation.isLocalFile() ? m_requestedOutputLocation.toLocalFile()
+ : m_requestedOutputLocation.toString(),
+ m_cameraSession ? QMediaStorageLocation::Movies
+ : QMediaStorageLocation::Sounds,
+ m_cameraSession ? QLatin1String("VID_")
+ : QLatin1String("REC_"),
+ m_containerFormat);
+
+ m_usedOutputLocation = QUrl::fromLocalFile(filePath);
+ m_mediaRecorder->setOutputFile(filePath);
+
+ // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay()
+ // is not necessary when the Camera already has a Surface, it doesn't actually work on some
+ // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare()
+ // and start() if MediaRecorder.setPreviewDispaly() is not called.
+ if (m_cameraSession) {
+ // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same
+ // one that is set on the Camera or it will crash, hence the reset().
+ m_cameraSession->videoOutput()->reset();
+ if (m_cameraSession->videoOutput()->surfaceTexture())
+ m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture());
+ else if (m_cameraSession->videoOutput()->surfaceHolder())
+ m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder());
+ }
+
+ if (!m_mediaRecorder->prepare()) {
+ emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder."));
+ if (m_cameraSession)
+ restartViewfinder();
+ return;
+ }
+
+ if (!m_mediaRecorder->start()) {
+ emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder."));
+ if (m_cameraSession)
+ restartViewfinder();
+ return;
+ }
+
+ m_elapsedTime.start();
+ m_notifyTimer.start();
+ updateDuration();
+
+ if (m_cameraSession) {
+ m_cameraSession->setReadyForCapture(false);
+
+ // Preview frame callback is cleared when setting up the camera with the media recorder.
+ // We need to reset it.
+ m_cameraSession->camera()->setupPreviewFrameCallback();
+ }
+
+ m_state = QMediaRecorder::RecordingState;
+ emit stateChanged(m_state);
+ setStatus(QMediaRecorder::RecordingStatus);
+}
+
+void QAndroidCaptureSession::stop(bool error)
+{
+ if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0)
+ return;
+
+ setStatus(QMediaRecorder::FinalizingStatus);
+
+ m_mediaRecorder->stop();
+ m_notifyTimer.stop();
+ updateDuration();
+ m_elapsedTime.invalidate();
+ m_mediaRecorder->release();
+ delete m_mediaRecorder;
+ m_mediaRecorder = 0;
+
+ if (m_cameraSession && m_cameraSession->status() == QCamera::ActiveStatus) {
+ // Viewport needs to be restarted after recording
+ restartViewfinder();
+ }
+
+ if (!error) {
+ // if the media is saved into the standard media location, register it
+ // with the Android media scanner so it appears immediately in apps
+ // such as the gallery.
+ QString mediaPath = m_usedOutputLocation.toLocalFile();
+ QString standardLoc = m_cameraSession ? AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::DCIM)
+ : AndroidMultimediaUtils::getDefaultMediaDirectory(AndroidMultimediaUtils::Sounds);
+ if (mediaPath.startsWith(standardLoc))
+ AndroidMultimediaUtils::registerMediaFile(mediaPath);
+
+ m_actualOutputLocation = m_usedOutputLocation;
+ emit actualLocationChanged(m_actualOutputLocation);
+ }
+
+ m_state = QMediaRecorder::StoppedState;
+ emit stateChanged(m_state);
+ if (!m_cameraSession)
+ setStatus(QMediaRecorder::LoadedStatus);
+}
+
+void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status)
+{
+ if (m_status == status)
+ return;
+
+ m_status = status;
+ emit statusChanged(m_status);
+}
+
+QMediaRecorder::Status QAndroidCaptureSession::status() const
+{
+ return m_status;
+}
+
+qint64 QAndroidCaptureSession::duration() const
+{
+ return m_duration;
+}
+
+void QAndroidCaptureSession::setContainerFormat(const QString &format)
+{
+ if (m_containerFormat == format)
+ return;
+
+ m_containerFormat = format;
+ m_containerFormatDirty = true;
+}
+
+void QAndroidCaptureSession::setAudioSettings(const QAudioEncoderSettings &settings)
+{
+ if (m_audioSettings == settings)
+ return;
+
+ m_audioSettings = settings;
+ m_audioSettingsDirty = true;
+}
+
+void QAndroidCaptureSession::setVideoSettings(const QVideoEncoderSettings &settings)
+{
+ if (!m_cameraSession || m_videoSettings == settings)
+ return;
+
+ m_videoSettings = settings;
+ m_videoSettingsDirty = true;
+}
+
+void QAndroidCaptureSession::applySettings()
+{
+ // container settings
+ if (m_containerFormatDirty) {
+ if (m_containerFormat.isEmpty()) {
+ m_containerFormat = m_defaultSettings.outputFileExtension;
+ m_outputFormat = m_defaultSettings.outputFormat;
+ } else if (m_containerFormat == QLatin1String("3gp")) {
+ m_outputFormat = AndroidMediaRecorder::THREE_GPP;
+ } else if (!m_cameraSession && m_containerFormat == QLatin1String("amr")) {
+ m_outputFormat = AndroidMediaRecorder::AMR_NB_Format;
+ } else if (!m_cameraSession && m_containerFormat == QLatin1String("awb")) {
+ m_outputFormat = AndroidMediaRecorder::AMR_WB_Format;
+ } else {
+ m_containerFormat = QStringLiteral("mp4");
+ m_outputFormat = AndroidMediaRecorder::MPEG_4;
+ }
+
+ m_containerFormatDirty = false;
+ }
+
+ // audio settings
+ if (m_audioSettingsDirty) {
+ if (m_audioSettings.channelCount() <= 0)
+ m_audioSettings.setChannelCount(m_defaultSettings.audioChannels);
+ if (m_audioSettings.bitRate() <= 0)
+ m_audioSettings.setBitRate(m_defaultSettings.audioBitRate);
+ if (m_audioSettings.sampleRate() <= 0)
+ m_audioSettings.setSampleRate(m_defaultSettings.audioSampleRate);
+
+ if (m_audioSettings.codec().isEmpty())
+ m_audioEncoder = m_defaultSettings.audioEncoder;
+ else if (m_audioSettings.codec() == QLatin1String("aac"))
+ m_audioEncoder = AndroidMediaRecorder::AAC;
+ else if (m_audioSettings.codec() == QLatin1String("amr-nb"))
+ m_audioEncoder = AndroidMediaRecorder::AMR_NB_Encoder;
+ else if (m_audioSettings.codec() == QLatin1String("amr-wb"))
+ m_audioEncoder = AndroidMediaRecorder::AMR_WB_Encoder;
+ else
+ m_audioEncoder = m_defaultSettings.audioEncoder;
+
+ m_audioSettingsDirty = false;
+ }
+
+ // video settings
+ if (m_cameraSession && m_cameraSession->camera() && m_videoSettingsDirty) {
+ if (m_videoSettings.resolution().isEmpty()) {
+ m_videoSettings.setResolution(m_defaultSettings.videoResolution);
+ } else if (!m_supportedResolutions.contains(m_videoSettings.resolution())) {
+ // if the requested resolution is not supported, find the closest one
+ QSize reqSize = m_videoSettings.resolution();
+ int reqPixelCount = reqSize.width() * reqSize.height();
+ QList<int> supportedPixelCounts;
+ for (int i = 0; i < m_supportedResolutions.size(); ++i) {
+ const QSize &s = m_supportedResolutions.at(i);
+ supportedPixelCounts.append(s.width() * s.height());
+ }
+ int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
+ m_videoSettings.setResolution(m_supportedResolutions.at(closestIndex));
+ }
+
+ if (m_videoSettings.frameRate() <= 0)
+ m_videoSettings.setFrameRate(m_defaultSettings.videoFrameRate);
+ if (m_videoSettings.bitRate() <= 0)
+ m_videoSettings.setBitRate(m_defaultSettings.videoBitRate);
+
+ if (m_videoSettings.codec().isEmpty())
+ m_videoEncoder = m_defaultSettings.videoEncoder;
+ else if (m_videoSettings.codec() == QLatin1String("h263"))
+ m_videoEncoder = AndroidMediaRecorder::H263;
+ else if (m_videoSettings.codec() == QLatin1String("h264"))
+ m_videoEncoder = AndroidMediaRecorder::H264;
+ else if (m_videoSettings.codec() == QLatin1String("mpeg4_sp"))
+ m_videoEncoder = AndroidMediaRecorder::MPEG_4_SP;
+ else
+ m_videoEncoder = m_defaultSettings.videoEncoder;
+
+ m_videoSettingsDirty = false;
+ }
+}
+
+void QAndroidCaptureSession::updateViewfinder()
+{
+ m_cameraSession->camera()->stopPreviewSynchronous();
+ m_cameraSession->applyViewfinderSettings(m_videoSettings.resolution(), false);
+}
+
+void QAndroidCaptureSession::restartViewfinder()
+{
+ if (!m_cameraSession)
+ return;
+
+ m_cameraSession->camera()->reconnect();
+
+ // This is not necessary on most devices, but it crashes on some if we don't stop the
+ // preview and reset the preview display on the camera when recording is over.
+ m_cameraSession->camera()->stopPreviewSynchronous();
+ m_cameraSession->videoOutput()->reset();
+ if (m_cameraSession->videoOutput()->surfaceTexture())
+ m_cameraSession->camera()->setPreviewTexture(m_cameraSession->videoOutput()->surfaceTexture());
+ else if (m_cameraSession->videoOutput()->surfaceHolder())
+ m_cameraSession->camera()->setPreviewDisplay(m_cameraSession->videoOutput()->surfaceHolder());
+
+ m_cameraSession->camera()->startPreview();
+ m_cameraSession->setReadyForCapture(true);
+}
+
+void QAndroidCaptureSession::updateDuration()
+{
+ if (m_elapsedTime.isValid())
+ m_duration = m_elapsedTime.elapsed();
+
+ emit durationChanged(m_duration);
+}
+
+void QAndroidCaptureSession::onCameraOpened()
+{
+ m_supportedResolutions.clear();
+ m_supportedFramerates.clear();
+
+ // get supported resolutions from predefined profiles
+ for (int i = 0; i < 8; ++i) {
+ CaptureProfile profile = getProfile(i);
+ if (!profile.isNull) {
+ if (i == AndroidCamcorderProfile::QUALITY_HIGH)
+ m_defaultSettings = profile;
+
+ if (!m_supportedResolutions.contains(profile.videoResolution))
+ m_supportedResolutions.append(profile.videoResolution);
+ if (!m_supportedFramerates.contains(profile.videoFrameRate))
+ m_supportedFramerates.append(profile.videoFrameRate);
+ }
+ }
+
+ std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
+ std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end());
+
+ applySettings();
+}
+
+QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(int id)
+{
+ CaptureProfile profile;
+ const bool hasProfile = AndroidCamcorderProfile::hasProfile(m_cameraSession->camera()->cameraId(),
+ AndroidCamcorderProfile::Quality(id));
+
+ if (hasProfile) {
+ AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(m_cameraSession->camera()->cameraId(),
+ AndroidCamcorderProfile::Quality(id));
+
+ profile.outputFormat = AndroidMediaRecorder::OutputFormat(camProfile.getValue(AndroidCamcorderProfile::fileFormat));
+ profile.audioEncoder = AndroidMediaRecorder::AudioEncoder(camProfile.getValue(AndroidCamcorderProfile::audioCodec));
+ profile.audioBitRate = camProfile.getValue(AndroidCamcorderProfile::audioBitRate);
+ profile.audioChannels = camProfile.getValue(AndroidCamcorderProfile::audioChannels);
+ profile.audioSampleRate = camProfile.getValue(AndroidCamcorderProfile::audioSampleRate);
+ profile.videoEncoder = AndroidMediaRecorder::VideoEncoder(camProfile.getValue(AndroidCamcorderProfile::videoCodec));
+ profile.videoBitRate = camProfile.getValue(AndroidCamcorderProfile::videoBitRate);
+ profile.videoFrameRate = camProfile.getValue(AndroidCamcorderProfile::videoFrameRate);
+ profile.videoResolution = QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth),
+ camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight));
+
+ if (profile.outputFormat == AndroidMediaRecorder::MPEG_4)
+ profile.outputFileExtension = QStringLiteral("mp4");
+ else if (profile.outputFormat == AndroidMediaRecorder::THREE_GPP)
+ profile.outputFileExtension = QStringLiteral("3gp");
+ else if (profile.outputFormat == AndroidMediaRecorder::AMR_NB_Format)
+ profile.outputFileExtension = QStringLiteral("amr");
+ else if (profile.outputFormat == AndroidMediaRecorder::AMR_WB_Format)
+ profile.outputFileExtension = QStringLiteral("awb");
+
+ profile.isNull = false;
+ }
+
+ return profile;
+}
+
+void QAndroidCaptureSession::onError(int what, int extra)
+{
+ Q_UNUSED(what);
+ Q_UNUSED(extra);
+ stop(true);
+ emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
+}
+
+void QAndroidCaptureSession::onInfo(int what, int extra)
+{
+ Q_UNUSED(extra);
+ if (what == 800) {
+ // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
+ setState(QMediaRecorder::StoppedState);
+ emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached."));
+ } else if (what == 801) {
+ // MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
+ setState(QMediaRecorder::StoppedState);
+ emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached."));
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
new file mode 100644
index 000000000..7ea469334
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDCAPTURESESSION_H
+#define QANDROIDCAPTURESESSION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <qmediarecorder.h>
+#include <qurl.h>
+#include <qelapsedtimer.h>
+#include <qtimer.h>
+#include <private/qmediastoragelocation_p.h>
+#include "androidmediarecorder_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidCaptureSession : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QAndroidCaptureSession(QAndroidCameraSession *cameraSession = 0);
+ ~QAndroidCaptureSession();
+
+ QList<QSize> supportedResolutions() const { return m_supportedResolutions; }
+ QList<qreal> supportedFrameRates() const { return m_supportedFramerates; }
+
+ QString audioInput() const { return m_audioInput; }
+ void setAudioInput(const QString &input);
+
+ QUrl outputLocation() const;
+ bool setOutputLocation(const QUrl &location);
+
+ QMediaRecorder::State state() const;
+ void setState(QMediaRecorder::State state);
+
+ QMediaRecorder::Status status() const;
+
+ qint64 duration() const;
+
+ QString containerFormat() const { return m_containerFormat; }
+ void setContainerFormat(const QString &format);
+
+ QAudioEncoderSettings audioSettings() const { return m_audioSettings; }
+ void setAudioSettings(const QAudioEncoderSettings &settings);
+
+ QVideoEncoderSettings videoSettings() const { return m_videoSettings; }
+ void setVideoSettings(const QVideoEncoderSettings &settings);
+
+ void applySettings();
+
+Q_SIGNALS:
+ void audioInputChanged(const QString& name);
+ void stateChanged(QMediaRecorder::State state);
+ void statusChanged(QMediaRecorder::Status status);
+ void durationChanged(qint64 position);
+ void actualLocationChanged(const QUrl &location);
+ void error(int error, const QString &errorString);
+
+private Q_SLOTS:
+ void updateDuration();
+ void onCameraOpened();
+
+ void onError(int what, int extra);
+ void onInfo(int what, int extra);
+
+private:
+ struct CaptureProfile {
+ AndroidMediaRecorder::OutputFormat outputFormat;
+ QString outputFileExtension;
+
+ AndroidMediaRecorder::AudioEncoder audioEncoder;
+ int audioBitRate;
+ int audioChannels;
+ int audioSampleRate;
+
+ AndroidMediaRecorder::VideoEncoder videoEncoder;
+ int videoBitRate;
+ int videoFrameRate;
+ QSize videoResolution;
+
+ bool isNull;
+
+ CaptureProfile()
+ : outputFormat(AndroidMediaRecorder::MPEG_4)
+ , outputFileExtension(QLatin1String("mp4"))
+ , audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
+ , audioBitRate(128000)
+ , audioChannels(2)
+ , audioSampleRate(44100)
+ , videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
+ , videoBitRate(1)
+ , videoFrameRate(-1)
+ , videoResolution(320, 240)
+ , isNull(true)
+ { }
+ };
+
+ CaptureProfile getProfile(int id);
+
+ void start();
+ void stop(bool error = false);
+
+ void setStatus(QMediaRecorder::Status status);
+
+ void updateViewfinder();
+ void restartViewfinder();
+
+ AndroidMediaRecorder *m_mediaRecorder;
+ QAndroidCameraSession *m_cameraSession;
+
+ QString m_audioInput;
+ AndroidMediaRecorder::AudioSource m_audioSource;
+
+ QMediaStorageLocation m_mediaStorageLocation;
+
+ QElapsedTimer m_elapsedTime;
+ QTimer m_notifyTimer;
+ qint64 m_duration;
+
+ QMediaRecorder::State m_state;
+ QMediaRecorder::Status m_status;
+ QUrl m_requestedOutputLocation;
+ QUrl m_usedOutputLocation;
+ QUrl m_actualOutputLocation;
+
+ CaptureProfile m_defaultSettings;
+
+ QString m_containerFormat;
+ QAudioEncoderSettings m_audioSettings;
+ QVideoEncoderSettings m_videoSettings;
+ bool m_containerFormatDirty;
+ bool m_videoSettingsDirty;
+ bool m_audioSettingsDirty;
+ AndroidMediaRecorder::OutputFormat m_outputFormat;
+ AndroidMediaRecorder::AudioEncoder m_audioEncoder;
+ AndroidMediaRecorder::VideoEncoder m_videoEncoder;
+
+ QList<QSize> m_supportedResolutions;
+ QList<qreal> m_supportedFramerates;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDCAPTURESESSION_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp
new file mode 100644
index 000000000..1f30ea95f
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidimageencodercontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidImageEncoderControl::QAndroidImageEncoderControl(QAndroidCameraSession *session)
+ : QImageEncoderControl()
+ , m_session(session)
+{
+ connect(m_session, SIGNAL(opened()),
+ this, SLOT(onCameraOpened()));
+}
+
+QStringList QAndroidImageEncoderControl::supportedImageCodecs() const
+{
+ return QStringList() << QLatin1String("jpeg");
+}
+
+QString QAndroidImageEncoderControl::imageCodecDescription(const QString &codecName) const
+{
+ if (codecName == QLatin1String("jpeg"))
+ return tr("JPEG image");
+
+ return QString();
+}
+
+QList<QSize> QAndroidImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings, bool *continuous) const
+{
+ Q_UNUSED(settings);
+
+ if (continuous)
+ *continuous = false;
+
+ return m_supportedResolutions;
+}
+
+QImageEncoderSettings QAndroidImageEncoderControl::imageSettings() const
+{
+ return m_session->imageSettings();
+}
+
+void QAndroidImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings)
+{
+ m_session->setImageSettings(settings);
+}
+
+void QAndroidImageEncoderControl::onCameraOpened()
+{
+ m_supportedResolutions = m_session->camera()->getSupportedPictureSizes();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h
new file mode 100644
index 000000000..9e65660ff
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidimageencodercontrol_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDIMAGEENCODERCONTROL_H
+#define QANDROIDIMAGEENCODERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qimageencodercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidImageEncoderControl : public QImageEncoderControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidImageEncoderControl(QAndroidCameraSession *session);
+
+ QStringList supportedImageCodecs() const override;
+ QString imageCodecDescription(const QString &codecName) const override;
+ QList<QSize> supportedResolutions(const QImageEncoderSettings &settings, bool *continuous = 0) const override;
+ QImageEncoderSettings imageSettings() const override;
+ void setImageSettings(const QImageEncoderSettings &settings) override;
+
+private Q_SLOTS:
+ void onCameraOpened();
+
+private:
+ QAndroidCameraSession *m_session;
+
+ QList<QSize> m_supportedResolutions;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDIMAGEENCODERCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp
new file mode 100644
index 000000000..45fe7092c
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediacontainercontrol_p.h"
+
+#include "qandroidcapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaContainerControl::QAndroidMediaContainerControl(QAndroidCaptureSession *session)
+ : QMediaContainerControl()
+ , m_session(session)
+{
+}
+
+QStringList QAndroidMediaContainerControl::supportedContainers() const
+{
+ return QStringList() << QLatin1String("mp4")
+ << QLatin1String("3gp")
+ << QLatin1String("amr")
+ << QLatin1String("awb");
+}
+
+QString QAndroidMediaContainerControl::containerFormat() const
+{
+ return m_session->containerFormat();
+}
+
+void QAndroidMediaContainerControl::setContainerFormat(const QString &format)
+{
+ m_session->setContainerFormat(format);
+}
+
+QString QAndroidMediaContainerControl::containerDescription(const QString &formatMimeType) const
+{
+ if (formatMimeType == QLatin1String("mp4"))
+ return tr("MPEG4 media file format");
+ else if (formatMimeType == QLatin1String("3gp"))
+ return tr("3GPP media file format");
+ else if (formatMimeType == QLatin1String("amr"))
+ return tr("AMR NB file format");
+ else if (formatMimeType == QLatin1String("awb"))
+ return tr("AMR WB file format");
+
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h
new file mode 100644
index 000000000..00f09970f
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediacontainercontrol_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIACONTAINERCONTROL_H
+#define QANDROIDMEDIACONTAINERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediacontainercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCaptureSession;
+
+class QAndroidMediaContainerControl : public QMediaContainerControl
+{
+ Q_OBJECT
+public:
+ QAndroidMediaContainerControl(QAndroidCaptureSession *session);
+
+ QStringList supportedContainers() const override;
+ QString containerFormat() const override;
+ void setContainerFormat(const QString &format) override;
+ QString containerDescription(const QString &formatMimeType) const override;
+
+private:
+ QAndroidCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIACONTAINERCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp
new file mode 100644
index 000000000..7a1913afc
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediarecordercontrol_p.h"
+
+#include "qandroidcapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaRecorderControl::QAndroidMediaRecorderControl(QAndroidCaptureSession *session)
+ : QMediaRecorderControl()
+ , m_session(session)
+{
+ connect(m_session, SIGNAL(stateChanged(QMediaRecorder::State)), this, SIGNAL(stateChanged(QMediaRecorder::State)));
+ connect(m_session, SIGNAL(statusChanged(QMediaRecorder::Status)), this, SIGNAL(statusChanged(QMediaRecorder::Status)));
+ connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64)));
+ connect(m_session, SIGNAL(actualLocationChanged(QUrl)), this, SIGNAL(actualLocationChanged(QUrl)));
+ connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
+}
+
+QUrl QAndroidMediaRecorderControl::outputLocation() const
+{
+ return m_session->outputLocation();
+}
+
+bool QAndroidMediaRecorderControl::setOutputLocation(const QUrl &location)
+{
+ return m_session->setOutputLocation(location);
+}
+
+QMediaRecorder::State QAndroidMediaRecorderControl::state() const
+{
+ return m_session->state();
+}
+
+QMediaRecorder::Status QAndroidMediaRecorderControl::status() const
+{
+ return m_session->status();
+}
+
+qint64 QAndroidMediaRecorderControl::duration() const
+{
+ return m_session->duration();
+}
+
+bool QAndroidMediaRecorderControl::isMuted() const
+{
+ // No API for this in Android
+ return false;
+}
+
+qreal QAndroidMediaRecorderControl::volume() const
+{
+ // No API for this in Android
+ return 1.0;
+}
+
+void QAndroidMediaRecorderControl::applySettings()
+{
+ m_session->applySettings();
+}
+
+void QAndroidMediaRecorderControl::setState(QMediaRecorder::State state)
+{
+ m_session->setState(state);
+}
+
+void QAndroidMediaRecorderControl::setMuted(bool muted)
+{
+ // No API for this in Android
+ Q_UNUSED(muted);
+ qWarning("QMediaRecorder::setMuted() is not supported on Android.");
+}
+
+void QAndroidMediaRecorderControl::setVolume(qreal volume)
+{
+ // No API for this in Android
+ Q_UNUSED(volume);
+ qWarning("QMediaRecorder::setVolume() is not supported on Android.");
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h
new file mode 100644
index 000000000..4f6685d28
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediarecordercontrol_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIARECORDERCONTROL_H
+#define QANDROIDMEDIARECORDERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediarecordercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCaptureSession;
+
+class QAndroidMediaRecorderControl : public QMediaRecorderControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMediaRecorderControl(QAndroidCaptureSession *session);
+
+ QUrl outputLocation() const override;
+ bool setOutputLocation(const QUrl &location) override;
+ QMediaRecorder::State state() const override;
+ QMediaRecorder::Status status() const override;
+ qint64 duration() const override;
+ bool isMuted() const override;
+ qreal volume() const override;
+ void applySettings() override;
+
+public Q_SLOTS:
+ void setState(QMediaRecorder::State state) override;
+ void setMuted(bool muted) override;
+ void setVolume(qreal volume) override;
+
+private:
+ QAndroidCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIARECORDERCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp
new file mode 100644
index 000000000..86321271a
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Integrated Computer Solutions, Inc
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediavideoprobecontrol_p.h"
+#include <qvideoframe.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaVideoProbeControl::QAndroidMediaVideoProbeControl(QObject *parent) :
+ QMediaVideoProbeControl(parent)
+{
+}
+
+QAndroidMediaVideoProbeControl::~QAndroidMediaVideoProbeControl()
+{
+
+}
+
+void QAndroidMediaVideoProbeControl::newFrameProbed(const QVideoFrame &frame)
+{
+ emit videoFrameProbed(frame);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h
new file mode 100644
index 000000000..324370e97
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidmediavideoprobecontrol_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Integrated Computer Solutions, Inc
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIAVIDEOPROBECONTROL_H
+#define QANDROIDMEDIAVIDEOPROBECONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qmediavideoprobecontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaVideoProbeControl : public QMediaVideoProbeControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMediaVideoProbeControl(QObject *parent = 0);
+ virtual ~QAndroidMediaVideoProbeControl();
+
+ void newFrameProbed(const QVideoFrame& frame);
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAVIDEOPROBECONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp
new file mode 100644
index 000000000..5403647f7
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidvideodeviceselectorcontrol_p.h"
+
+#include "qandroidcamerasession_p.h"
+#include "androidcamera_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidVideoDeviceSelectorControl::QAndroidVideoDeviceSelectorControl(QAndroidCameraSession *session)
+ : QVideoDeviceSelectorControl(0)
+ , m_selectedDevice(0)
+ , m_cameraSession(session)
+{
+}
+
+QAndroidVideoDeviceSelectorControl::~QAndroidVideoDeviceSelectorControl()
+{
+}
+
+int QAndroidVideoDeviceSelectorControl::deviceCount() const
+{
+ return QAndroidCameraSession::availableCameras().count();
+}
+
+QString QAndroidVideoDeviceSelectorControl::deviceName(int index) const
+{
+ if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
+ return QString();
+
+ return QString::fromLatin1(QAndroidCameraSession::availableCameras().at(index).name);
+}
+
+QString QAndroidVideoDeviceSelectorControl::deviceDescription(int index) const
+{
+ if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
+ return QString();
+
+ return QAndroidCameraSession::availableCameras().at(index).description;
+}
+
+QCamera::Position QAndroidVideoDeviceSelectorControl::cameraPosition(int index) const
+{
+ if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
+ return QCamera::UnspecifiedPosition;
+
+ return QAndroidCameraSession::availableCameras().at(index).position;
+}
+
+int QAndroidVideoDeviceSelectorControl::cameraOrientation(int index) const
+{
+ if (index < 0 || index >= QAndroidCameraSession::availableCameras().count())
+ return QCamera::UnspecifiedPosition;
+
+ return QAndroidCameraSession::availableCameras().at(index).orientation;
+}
+
+int QAndroidVideoDeviceSelectorControl::defaultDevice() const
+{
+ return 0;
+}
+
+int QAndroidVideoDeviceSelectorControl::selectedDevice() const
+{
+ return m_selectedDevice;
+}
+
+void QAndroidVideoDeviceSelectorControl::setSelectedDevice(int index)
+{
+ if (index != m_selectedDevice) {
+ m_selectedDevice = index;
+ m_cameraSession->setSelectedCamera(m_selectedDevice);
+ emit selectedDeviceChanged(index);
+ emit selectedDeviceChanged(deviceName(index));
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h
new file mode 100644
index 000000000..d074e7441
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideodeviceselectorcontrol_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDVIDEODEVICESELECTORCONTROL_H
+#define QANDROIDVIDEODEVICESELECTORCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qvideodeviceselectorcontrol.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCameraSession;
+
+class QAndroidVideoDeviceSelectorControl : public QVideoDeviceSelectorControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidVideoDeviceSelectorControl(QAndroidCameraSession *session);
+ ~QAndroidVideoDeviceSelectorControl();
+
+ int deviceCount() const;
+
+ QString deviceName(int index) const;
+ QString deviceDescription(int index) const;
+ QCamera::Position cameraPosition(int index) const;
+ int cameraOrientation(int index) const;
+
+ int defaultDevice() const;
+ int selectedDevice() const;
+ void setSelectedDevice(int index);
+
+private:
+ int m_selectedDevice;
+
+ QAndroidCameraSession *m_cameraSession;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDVIDEODEVICESELECTORCONTROL_H
diff --git a/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp
new file mode 100644
index 000000000..ef3559c0d
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidvideoencodersettingscontrol_p.h"
+
+#include "qandroidcapturesession_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidVideoEncoderSettingsControl::QAndroidVideoEncoderSettingsControl(QAndroidCaptureSession *session)
+ : QVideoEncoderSettingsControl()
+ , m_session(session)
+{
+}
+
+QList<QSize> QAndroidVideoEncoderSettingsControl::supportedResolutions(const QVideoEncoderSettings &, bool *continuous) const
+{
+ if (continuous)
+ *continuous = false;
+
+ return m_session->supportedResolutions();
+}
+
+QList<qreal> QAndroidVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &, bool *continuous) const
+{
+ if (continuous)
+ *continuous = false;
+
+ return m_session->supportedFrameRates();
+}
+
+QStringList QAndroidVideoEncoderSettingsControl::supportedVideoCodecs() const
+{
+ return QStringList() << QLatin1String("h263")
+ << QLatin1String("h264")
+ << QLatin1String("mpeg4_sp");
+}
+
+QString QAndroidVideoEncoderSettingsControl::videoCodecDescription(const QString &codecName) const
+{
+ if (codecName == QLatin1String("h263"))
+ return tr("H.263 compression");
+ else if (codecName == QLatin1String("h264"))
+ return tr("H.264 compression");
+ else if (codecName == QLatin1String("mpeg4_sp"))
+ return tr("MPEG-4 SP compression");
+
+ return QString();
+}
+
+QVideoEncoderSettings QAndroidVideoEncoderSettingsControl::videoSettings() const
+{
+ return m_session->videoSettings();
+}
+
+void QAndroidVideoEncoderSettingsControl::setVideoSettings(const QVideoEncoderSettings &settings)
+{
+ m_session->setVideoSettings(settings);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h
new file mode 100644
index 000000000..1488790a8
--- /dev/null
+++ b/src/multimedia/platform/android/mediacapture/qandroidvideoencodersettingscontrol_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDVIDEOENCODERSETTINGSCONTROL_H
+#define QANDROIDVIDEOENCODERSETTINGSCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qvideoencodersettingscontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidCaptureSession;
+
+class QAndroidVideoEncoderSettingsControl : public QVideoEncoderSettingsControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidVideoEncoderSettingsControl(QAndroidCaptureSession *session);
+
+ QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
+ QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const override;
+ QStringList supportedVideoCodecs() const override;
+ QString videoCodecDescription(const QString &codecName) const override;
+ QVideoEncoderSettings videoSettings() const override;
+ void setVideoSettings(const QVideoEncoderSettings &settings) override;
+
+private:
+ QAndroidCaptureSession *m_session;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDVIDEOENCODERSETTINGSCONTROL_H
diff --git a/src/multimedia/platform/android/mediaplayer/mediaplayer.pri b/src/multimedia/platform/android/mediaplayer/mediaplayer.pri
new file mode 100644
index 000000000..33348b38b
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/mediaplayer.pri
@@ -0,0 +1,13 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qandroidmediaplayercontrol_p.h \
+ $$PWD/qandroidmediaservice_p.h \
+ $$PWD/qandroidmetadatareadercontrol_p.h \
+ $$PWD/qandroidmediaplayervideorenderercontrol_p.h
+
+SOURCES += \
+ $$PWD/qandroidmediaplayercontrol.cpp \
+ $$PWD/qandroidmediaservice.cpp \
+ $$PWD/qandroidmetadatareadercontrol.cpp \
+ $$PWD/qandroidmediaplayervideorenderercontrol.cpp
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp
new file mode 100644
index 000000000..7e4f32e4a
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -0,0 +1,835 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaplayercontrol_p.h"
+#include "androidmediaplayer_p.h"
+#include "qandroidvideooutput_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class StateChangeNotifier
+{
+public:
+ StateChangeNotifier(QAndroidMediaPlayerControl *mp)
+ : mControl(mp)
+ , mPreviousState(mp->state())
+ , mPreviousMediaStatus(mp->mediaStatus())
+ {
+ ++mControl->mActiveStateChangeNotifiers;
+ }
+
+ ~StateChangeNotifier()
+ {
+ if (--mControl->mActiveStateChangeNotifiers)
+ return;
+
+ if (mPreviousMediaStatus != mControl->mediaStatus())
+ Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
+
+ if (mPreviousState != mControl->state())
+ Q_EMIT mControl->stateChanged(mControl->state());
+ }
+
+private:
+ QAndroidMediaPlayerControl *mControl;
+ QMediaPlayer::State mPreviousState;
+ QMediaPlayer::MediaStatus mPreviousMediaStatus;
+};
+
+
+QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
+ : QMediaPlayerControl(parent),
+ mMediaPlayer(new AndroidMediaPlayer),
+ mCurrentState(QMediaPlayer::StoppedState),
+ mCurrentMediaStatus(QMediaPlayer::NoMedia),
+ mMediaStream(0),
+ mVideoOutput(0),
+ mSeekable(true),
+ mBufferPercent(-1),
+ mBufferFilled(false),
+ mAudioAvailable(false),
+ mVideoAvailable(false),
+ mBuffering(false),
+ mState(AndroidMediaPlayer::Uninitialized),
+ mPendingState(-1),
+ mPendingPosition(-1),
+ mPendingSetMedia(false),
+ mPendingVolume(-1),
+ mPendingMute(-1),
+ mReloadingMedia(false),
+ mActiveStateChangeNotifiers(0),
+ mPendingPlaybackRate(1.0),
+ mHasPendingPlaybackRate(false)
+{
+ connect(mMediaPlayer,SIGNAL(bufferingChanged(qint32)),
+ this,SLOT(onBufferingChanged(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(stateChanged(qint32)),
+ this,SLOT(onStateChanged(qint32)));
+ connect(mMediaPlayer,SIGNAL(videoSizeChanged(qint32,qint32)),
+ this,SLOT(onVideoSizeChanged(qint32,qint32)));
+ connect(mMediaPlayer,SIGNAL(progressChanged(qint64)),
+ this,SIGNAL(positionChanged(qint64)));
+ connect(mMediaPlayer,SIGNAL(durationChanged(qint64)),
+ this,SIGNAL(durationChanged(qint64)));
+}
+
+QAndroidMediaPlayerControl::~QAndroidMediaPlayerControl()
+{
+ mMediaPlayer->release();
+ delete mMediaPlayer;
+}
+
+QMediaPlayer::State QAndroidMediaPlayerControl::state() const
+{
+ return mCurrentState;
+}
+
+QMediaPlayer::MediaStatus QAndroidMediaPlayerControl::mediaStatus() const
+{
+ return mCurrentMediaStatus;
+}
+
+qint64 QAndroidMediaPlayerControl::duration() const
+{
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ return 0;
+ }
+
+ return mMediaPlayer->getDuration();
+}
+
+qint64 QAndroidMediaPlayerControl::position() const
+{
+ if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
+ return duration();
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted))) {
+ return mMediaPlayer->getCurrentPosition();
+ }
+
+ return (mPendingPosition == -1) ? 0 : mPendingPosition;
+}
+
+void QAndroidMediaPlayerControl::setPosition(qint64 position)
+{
+ if (!mSeekable)
+ return;
+
+ const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
+
+ if (seekPosition == this->position())
+ return;
+
+ StateChangeNotifier notifier(this);
+
+ if (mCurrentMediaStatus == QMediaPlayer::EndOfMedia)
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingPosition = seekPosition;
+ } else {
+ mMediaPlayer->seekTo(seekPosition);
+
+ if (mPendingPosition != -1) {
+ mPendingPosition = -1;
+ }
+ }
+
+ Q_EMIT positionChanged(seekPosition);
+}
+
+int QAndroidMediaPlayerControl::volume() const
+{
+ return (mPendingVolume == -1) ? mMediaPlayer->volume() : mPendingVolume;
+}
+
+void QAndroidMediaPlayerControl::setVolume(int volume)
+{
+ if ((mState & (AndroidMediaPlayer::Idle
+ | AndroidMediaPlayer::Initialized
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ if (mPendingVolume != volume) {
+ mPendingVolume = volume;
+ Q_EMIT volumeChanged(volume);
+ }
+ return;
+ }
+
+ mMediaPlayer->setVolume(volume);
+
+ if (mPendingVolume != -1) {
+ mPendingVolume = -1;
+ return;
+ }
+
+ Q_EMIT volumeChanged(volume);
+}
+
+bool QAndroidMediaPlayerControl::isMuted() const
+{
+ return (mPendingMute == -1) ? mMediaPlayer->isMuted() : (mPendingMute == 1);
+}
+
+void QAndroidMediaPlayerControl::setMuted(bool muted)
+{
+ if ((mState & (AndroidMediaPlayer::Idle
+ | AndroidMediaPlayer::Initialized
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ if (mPendingMute != muted) {
+ mPendingMute = muted;
+ Q_EMIT mutedChanged(muted);
+ }
+ return;
+ }
+
+ mMediaPlayer->setMuted(muted);
+
+ if (mPendingMute != -1) {
+ mPendingMute = -1;
+ return;
+ }
+
+ Q_EMIT mutedChanged(muted);
+}
+
+void QAndroidMediaPlayerControl::setAudioRole(QAudio::Role role)
+{
+ mMediaPlayer->setAudioRole(role);
+}
+
+QList<QAudio::Role> QAndroidMediaPlayerControl::supportedAudioRoles() const
+{
+ return QList<QAudio::Role>()
+ << QAudio::VoiceCommunicationRole
+ << QAudio::MusicRole
+ << QAudio::VideoRole
+ << QAudio::SonificationRole
+ << QAudio::AlarmRole
+ << QAudio::NotificationRole
+ << QAudio::RingtoneRole
+ << QAudio::AccessibilityRole
+ << QAudio::GameRole;
+}
+
+void QAndroidMediaPlayerControl::setCustomAudioRole(const QString &role)
+{
+ mMediaPlayer->setCustomAudioRole(role);
+}
+
+QStringList QAndroidMediaPlayerControl::supportedCustomAudioRoles() const
+{
+ return QStringList()
+ << "CONTENT_TYPE_MOVIE"
+ << "CONTENT_TYPE_MUSIC"
+ << "CONTENT_TYPE_SONIFICATION"
+ << "CONTENT_TYPE_SPEECH"
+ << "USAGE_ALARM"
+ << "USAGE_ASSISTANCE_ACCESSIBILITY"
+ << "USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"
+ << "USAGE_ASSISTANCE_SONIFICATION"
+ << "USAGE_ASSISTANT"
+ << "USAGE_GAME"
+ << "USAGE_MEDIA"
+ << "USAGE_NOTIFICATION"
+ << "USAGE_NOTIFICATION_COMMUNICATION_DELAYED"
+ << "USAGE_NOTIFICATION_COMMUNICATION_INSTANT"
+ << "USAGE_NOTIFICATION_COMMUNICATION_REQUEST"
+ << "USAGE_NOTIFICATION_EVENT"
+ << "USAGE_NOTIFICATION_RINGTONE"
+ << "USAGE_VOICE_COMMUNICATION"
+ << "USAGE_VOICE_COMMUNICATION_SIGNALLING";
+}
+
+int QAndroidMediaPlayerControl::bufferStatus() const
+{
+ return mBufferFilled ? 100 : 0;
+}
+
+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
+{
+ if (mHasPendingPlaybackRate ||
+ (mState & (AndroidMediaPlayer::Initialized
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted
+ | AndroidMediaPlayer::Error)) == 0) {
+ return mPendingPlaybackRate;
+ }
+
+ return mMediaPlayer->playbackRate();
+}
+
+void QAndroidMediaPlayerControl::setPlaybackRate(qreal rate)
+{
+ if ((mState & (AndroidMediaPlayer::Initialized
+ | AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted
+ | AndroidMediaPlayer::Error)) == 0) {
+ if (mPendingPlaybackRate != rate) {
+ mPendingPlaybackRate = rate;
+ mHasPendingPlaybackRate = true;
+ Q_EMIT playbackRateChanged(rate);
+ }
+ return;
+ }
+
+ bool succeeded = mMediaPlayer->setPlaybackRate(rate);
+
+ if (mHasPendingPlaybackRate) {
+ mHasPendingPlaybackRate = false;
+ mPendingPlaybackRate = qreal(1.0);
+ if (!succeeded)
+ Q_EMIT playbackRateChanged(playbackRate());
+ } else if (succeeded) {
+ Q_EMIT playbackRateChanged(rate);
+ }
+}
+
+QUrl QAndroidMediaPlayerControl::media() const
+{
+ return mMediaContent;
+}
+
+const QIODevice *QAndroidMediaPlayerControl::mediaStream() const
+{
+ return mMediaStream;
+}
+
+void QAndroidMediaPlayerControl::setMedia(const QUrl &mediaContent,
+ QIODevice *stream)
+{
+ StateChangeNotifier notifier(this);
+
+ mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia;
+
+ if (!mReloadingMedia) {
+ mMediaContent = mediaContent;
+ mMediaStream = stream;
+ }
+
+ // Release the mediaplayer if it's not in in Idle or Uninitialized state
+ if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0)
+ mMediaPlayer->release();
+
+ if (mediaContent.isNull()) {
+ setMediaStatus(QMediaPlayer::NoMedia);
+ } else {
+ if (mVideoOutput && !mVideoOutput->isReady()) {
+ // 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;
+ }
+
+ if (mVideoSize.isValid() && mVideoOutput)
+ mVideoOutput->setVideoSize(mVideoSize);
+
+ if ((mMediaPlayer->display() == 0) && mVideoOutput)
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+ mMediaPlayer->setDataSource(mediaContent.request());
+ mMediaPlayer->prepareAsync();
+ }
+
+ if (!mReloadingMedia)
+ Q_EMIT mediaChanged(mMediaContent);
+
+ resetBufferingProgress();
+
+ mReloadingMedia = false;
+}
+
+void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput)
+{
+ if (mVideoOutput) {
+ mMediaPlayer->setDisplay(0);
+ mVideoOutput->stop();
+ mVideoOutput->reset();
+ }
+
+ mVideoOutput = videoOutput;
+
+ if (!mVideoOutput)
+ return;
+
+ if (mVideoOutput->isReady())
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+
+ connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool)));
+}
+
+void QAndroidMediaPlayerControl::play()
+{
+ StateChangeNotifier notifier(this);
+
+ // We need to prepare the mediaplayer again.
+ if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isNull()) {
+ setMedia(mMediaContent, mMediaStream);
+ }
+
+ if (!mMediaContent.isNull())
+ setState(QMediaPlayer::PlayingState);
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingState = QMediaPlayer::PlayingState;
+ return;
+ }
+
+ mMediaPlayer->play();
+}
+
+void QAndroidMediaPlayerControl::pause()
+{
+ StateChangeNotifier notifier(this);
+
+ setState(QMediaPlayer::PausedState);
+
+ if ((mState & (AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ mPendingState = QMediaPlayer::PausedState;
+ return;
+ }
+
+ mMediaPlayer->pause();
+}
+
+void QAndroidMediaPlayerControl::stop()
+{
+ StateChangeNotifier notifier(this);
+
+ setState(QMediaPlayer::StoppedState);
+
+ if ((mState & (AndroidMediaPlayer::Prepared
+ | AndroidMediaPlayer::Started
+ | AndroidMediaPlayer::Stopped
+ | AndroidMediaPlayer::Paused
+ | AndroidMediaPlayer::PlaybackCompleted)) == 0) {
+ if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0)
+ mPendingState = QMediaPlayer::StoppedState;
+ return;
+ }
+
+ mMediaPlayer->stop();
+}
+
+void QAndroidMediaPlayerControl::onInfo(qint32 what, qint32 extra)
+{
+ StateChangeNotifier notifier(this);
+
+ Q_UNUSED(extra);
+ switch (what) {
+ case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN:
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_VIDEO_TRACK_LAGGING:
+ // IGNORE
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_VIDEO_RENDERING_START:
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_START:
+ mPendingState = mCurrentState;
+ setState(QMediaPlayer::PausedState);
+ setMediaStatus(QMediaPlayer::StalledMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_END:
+ if (mCurrentState != QMediaPlayer::StoppedState)
+ flushPendingStates();
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING:
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_NOT_SEEKABLE:
+ setSeekable(false);
+ break;
+ case AndroidMediaPlayer::MEDIA_INFO_METADATA_UPDATE:
+ Q_EMIT metaDataUpdated();
+ break;
+ }
+}
+
+void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra)
+{
+ StateChangeNotifier notifier(this);
+
+ QString errorString;
+ QMediaPlayer::Error error = QMediaPlayer::ResourceError;
+
+ switch (what) {
+ case AndroidMediaPlayer::MEDIA_ERROR_UNKNOWN:
+ errorString = QLatin1String("Error:");
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_SERVER_DIED:
+ errorString = QLatin1String("Error: Server died");
+ error = QMediaPlayer::ServiceMissingError;
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_INVALID_STATE:
+ errorString = QLatin1String("Error: Invalid state");
+ error = QMediaPlayer::ServiceMissingError;
+ break;
+ }
+
+ switch (extra) {
+ case AndroidMediaPlayer::MEDIA_ERROR_IO: // Network OR file error
+ errorString += QLatin1String(" (I/O operation failed)");
+ error = QMediaPlayer::NetworkError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_MALFORMED:
+ errorString += QLatin1String(" (Malformed bitstream)");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_UNSUPPORTED:
+ errorString += QLatin1String(" (Unsupported media)");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_TIMED_OUT:
+ errorString += QLatin1String(" (Timed out)");
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
+ errorString += QLatin1String(" (Unable to start progressive playback')");
+ error = QMediaPlayer::FormatError;
+ setMediaStatus(QMediaPlayer::InvalidMedia);
+ break;
+ case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
+ errorString += QLatin1String(" (Unknown error/Insufficient resources)");
+ error = QMediaPlayer::ServiceMissingError;
+ break;
+ }
+
+ Q_EMIT QMediaPlayerControl::error(error, errorString);
+}
+
+void QAndroidMediaPlayerControl::onBufferingChanged(qint32 percent)
+{
+ StateChangeNotifier notifier(this);
+
+ mBuffering = percent != 100;
+ mBufferPercent = percent;
+
+ updateAvailablePlaybackRanges();
+
+ if (mCurrentState != QMediaPlayer::StoppedState)
+ setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : 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::onStateChanged(qint32 state)
+{
+ // If reloading, don't report state changes unless the new state is Prepared or Error.
+ if ((mState & AndroidMediaPlayer::Stopped)
+ && (state & (AndroidMediaPlayer::Prepared | AndroidMediaPlayer::Error | AndroidMediaPlayer::Uninitialized)) == 0) {
+ return;
+ }
+
+ StateChangeNotifier notifier(this);
+
+ mState = state;
+ switch (mState) {
+ case AndroidMediaPlayer::Idle:
+ break;
+ case AndroidMediaPlayer::Initialized:
+ break;
+ case AndroidMediaPlayer::Preparing:
+ if (!mReloadingMedia)
+ setMediaStatus(QMediaPlayer::LoadingMedia);
+ break;
+ case AndroidMediaPlayer::Prepared:
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ onBufferingChanged(100);
+ }
+ Q_EMIT metaDataUpdated();
+ setAudioAvailable(true);
+ flushPendingStates();
+ break;
+ case AndroidMediaPlayer::Started:
+ setState(QMediaPlayer::PlayingState);
+ if (mBuffering) {
+ setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
+ : QMediaPlayer::BufferingMedia);
+ } else {
+ setMediaStatus(QMediaPlayer::BufferedMedia);
+ }
+ Q_EMIT positionChanged(position());
+ break;
+ case AndroidMediaPlayer::Paused:
+ setState(QMediaPlayer::PausedState);
+ break;
+ case AndroidMediaPlayer::Error:
+ setState(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::UnknownMediaStatus);
+ mMediaPlayer->release();
+ Q_EMIT positionChanged(0);
+ break;
+ case AndroidMediaPlayer::Stopped:
+ setState(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::LoadedMedia);
+ Q_EMIT positionChanged(0);
+ break;
+ case AndroidMediaPlayer::PlaybackCompleted:
+ setState(QMediaPlayer::StoppedState);
+ setMediaStatus(QMediaPlayer::EndOfMedia);
+ break;
+ case AndroidMediaPlayer::Uninitialized:
+ // reset some properties (unless we reload the same media)
+ if (!mReloadingMedia) {
+ resetBufferingProgress();
+ mPendingPosition = -1;
+ mPendingSetMedia = false;
+ mPendingState = -1;
+
+ Q_EMIT durationChanged(0);
+ Q_EMIT positionChanged(0);
+
+ setAudioAvailable(false);
+ setVideoAvailable(false);
+ setSeekable(true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ((mState & (AndroidMediaPlayer::Stopped | AndroidMediaPlayer::Uninitialized)) != 0) {
+ mMediaPlayer->setDisplay(0);
+ if (mVideoOutput) {
+ mVideoOutput->stop();
+ mVideoOutput->reset();
+ }
+ }
+}
+
+void QAndroidMediaPlayerControl::onVideoOutputReady(bool ready)
+{
+ if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
+ mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
+
+ flushPendingStates();
+}
+
+void QAndroidMediaPlayerControl::setState(QMediaPlayer::State state)
+{
+ if (mCurrentState == state)
+ return;
+
+ if (mCurrentState == QMediaPlayer::StoppedState && state == QMediaPlayer::PausedState)
+ return;
+
+ mCurrentState = state;
+}
+
+void QAndroidMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status)
+{
+ if (mCurrentMediaStatus == status)
+ return;
+
+ mCurrentMediaStatus = status;
+
+ if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia)
+ Q_EMIT durationChanged(0);
+
+ if (status == QMediaPlayer::EndOfMedia)
+ Q_EMIT positionChanged(position());
+
+ updateBufferStatus();
+}
+
+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();
+}
+
+void QAndroidMediaPlayerControl::flushPendingStates()
+{
+ if (mPendingSetMedia) {
+ setMedia(mMediaContent, 0);
+ mPendingSetMedia = false;
+ return;
+ }
+
+ const int newState = mPendingState;
+ mPendingState = -1;
+
+ if (mPendingPosition != -1)
+ setPosition(mPendingPosition);
+ if (mPendingVolume != -1)
+ setVolume(mPendingVolume);
+ if (mPendingMute != -1)
+ setMuted((mPendingMute == 1));
+ if (mHasPendingPlaybackRate)
+ setPlaybackRate(mPendingPlaybackRate);
+
+ switch (newState) {
+ case QMediaPlayer::PlayingState:
+ play();
+ break;
+ case QMediaPlayer::PausedState:
+ pause();
+ break;
+ case QMediaPlayer::StoppedState:
+ stop();
+ break;
+ default:
+ break;
+ }
+}
+
+void QAndroidMediaPlayerControl::updateBufferStatus()
+{
+ bool bufferFilled = (mCurrentMediaStatus == QMediaPlayer::BufferedMedia
+ || mCurrentMediaStatus == QMediaPlayer::BufferingMedia);
+
+ if (mBufferFilled != bufferFilled) {
+ mBufferFilled = bufferFilled;
+ Q_EMIT bufferStatusChanged(bufferStatus());
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h
new file mode 100644
index 000000000..b7246acb4
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayercontrol_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIAPLAYERCONTROL_H
+#define QANDROIDMEDIAPLAYERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <QMediaPlayerControl>
+#include <qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMediaPlayer;
+class QAndroidVideoOutput;
+
+class QAndroidMediaPlayerControl : public QMediaPlayerControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMediaPlayerControl(QObject *parent = 0);
+ ~QAndroidMediaPlayerControl() override;
+
+ QMediaPlayer::State state() const override;
+ QMediaPlayer::MediaStatus mediaStatus() const override;
+ qint64 duration() const override;
+ qint64 position() const override;
+ int volume() const override;
+ bool isMuted() const override;
+ int bufferStatus() const override;
+ bool isAudioAvailable() const override;
+ bool isVideoAvailable() const override;
+ bool isSeekable() const override;
+ QMediaTimeRange availablePlaybackRanges() const override;
+ qreal playbackRate() const override;
+ void setPlaybackRate(qreal rate) override;
+ QUrl media() const override;
+ const QIODevice *mediaStream() const override;
+ void setMedia(const QUrl &mediaContent, QIODevice *stream) override;
+
+ void setAudioRole(QAudio::Role role) override;
+ QList<QAudio::Role> supportedAudioRoles() const override;
+ void setCustomAudioRole(const QString &role) override;
+ QStringList supportedCustomAudioRoles() const override;
+
+ void setVideoOutput(QAndroidVideoOutput *videoOutput);
+
+Q_SIGNALS:
+ void metaDataUpdated();
+
+public Q_SLOTS:
+ void setPosition(qint64 position) override;
+ void play() override;
+ void pause() override;
+ void stop() override;
+ void setVolume(int volume) override;
+ void setMuted(bool muted) override;
+
+private Q_SLOTS:
+ void onVideoOutputReady(bool ready);
+ void onError(qint32 what, qint32 extra);
+ void onInfo(qint32 what, qint32 extra);
+ void onBufferingChanged(qint32 percent);
+ void onVideoSizeChanged(qint32 width, qint32 height);
+ void onStateChanged(qint32 state);
+
+private:
+ AndroidMediaPlayer *mMediaPlayer;
+ QMediaPlayer::State mCurrentState;
+ QMediaPlayer::MediaStatus mCurrentMediaStatus;
+ QUrl mMediaContent;
+ QIODevice *mMediaStream;
+ QAndroidVideoOutput *mVideoOutput;
+ bool mSeekable;
+ int mBufferPercent;
+ bool mBufferFilled;
+ bool mAudioAvailable;
+ bool mVideoAvailable;
+ QSize mVideoSize;
+ bool mBuffering;
+ QMediaTimeRange mAvailablePlaybackRange;
+ int mState;
+ int mPendingState;
+ qint64 mPendingPosition;
+ bool mPendingSetMedia;
+ int mPendingVolume;
+ int mPendingMute;
+ bool mReloadingMedia;
+ int mActiveStateChangeNotifiers;
+ qreal mPendingPlaybackRate;
+ bool mHasPendingPlaybackRate; // we need this because the rate can theoretically be negative
+
+ void setState(QMediaPlayer::State state);
+ void setMediaStatus(QMediaPlayer::MediaStatus status);
+ void setSeekable(bool seekable);
+ void setAudioAvailable(bool available);
+ void setVideoAvailable(bool available);
+ void updateAvailablePlaybackRanges();
+ void resetBufferingProgress();
+ void flushPendingStates();
+ void updateBufferStatus();
+
+ friend class StateChangeNotifier;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAPLAYERCONTROL_H
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
new file mode 100644
index 000000000..aed7ba671
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaplayervideorenderercontrol_p.h"
+
+#include "qandroidmediaplayercontrol_p.h"
+#include "qandroidvideooutput_p.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/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h
new file mode 100644
index 000000000..91aed30fb
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayervideorenderercontrol_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
+#define QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qvideorenderercontrol.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaPlayerControl;
+class QAndroidTextureVideoOutput;
+
+class QAndroidMediaPlayerVideoRendererControl : public QVideoRendererControl
+{
+ Q_OBJECT
+public:
+ QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent = 0);
+ ~QAndroidMediaPlayerVideoRendererControl() override;
+
+ QAbstractVideoSurface *surface() const override;
+ void setSurface(QAbstractVideoSurface *surface) override;
+
+private:
+ QAndroidMediaPlayerControl *m_mediaPlayerControl;
+ QAbstractVideoSurface *m_surface;
+ QAndroidTextureVideoOutput *m_textureOutput;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp
new file mode 100644
index 000000000..7ccf1dc3a
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaservice_p.h"
+
+#include "qandroidmediaplayercontrol_p.h"
+#include "qandroidmetadatareadercontrol_p.h"
+#include "qandroidmediaplayervideorenderercontrol_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidMediaService::QAndroidMediaService(QObject *parent)
+ : QMediaService(parent)
+ , mAudioRoleControl(nullptr)
+ , mCustomAudioRoleControl(nullptr)
+ , mVideoRendererControl(0)
+{
+ mMediaControl = new QAndroidMediaPlayerControl;
+ mMetadataControl = new QAndroidMetaDataReaderControl;
+ connect(mMediaControl, SIGNAL(mediaChanged(QUrl)),
+ mMetadataControl, SLOT(onMediaChanged(QUrl)));
+ connect(mMediaControl, SIGNAL(metaDataUpdated()),
+ mMetadataControl, SLOT(onUpdateMetaData()));
+}
+
+QAndroidMediaService::~QAndroidMediaService()
+{
+ delete mVideoRendererControl;
+ delete mMetadataControl;
+ delete mMediaControl;
+}
+
+QObject *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 QAndroidMediaPlayerVideoRendererControl(mMediaControl);
+ return mVideoRendererControl;
+ }
+ }
+
+ return 0;
+}
+
+void QAndroidMediaService::releaseControl(QObject *control)
+{
+ if (control == mVideoRendererControl) {
+ delete mVideoRendererControl;
+ mVideoRendererControl = 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h
new file mode 100644
index 000000000..984c95ba3
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmediaservice_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIASERVICE_H
+#define QANDROIDMEDIASERVICE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QMediaService>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaPlayerControl;
+class QAndroidMetaDataReaderControl;
+class QAndroidAudioRoleControl;
+class QAndroidCustomAudioRoleControl;
+class QAndroidMediaPlayerVideoRendererControl;
+
+class QAndroidMediaService : public QMediaService
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMediaService(QObject *parent = 0);
+ ~QAndroidMediaService() override;
+
+ QObject *requestControl(const char *name) override;
+ void releaseControl(QObject *control) override;
+
+private:
+ QAndroidMediaPlayerControl *mMediaControl;
+ QAndroidMetaDataReaderControl *mMetadataControl;
+ QAndroidAudioRoleControl *mAudioRoleControl;
+ QAndroidCustomAudioRoleControl *mCustomAudioRoleControl;
+ QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIASERVICE_H
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp
new file mode 100644
index 000000000..31263335b
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmetadatareadercontrol_p.h"
+
+#include "androidmediametadataretriever_p.h"
+#include <QtMultimedia/qmediametadata.h>
+#include <qsize.h>
+#include <QDate>
+#include <QtCore/qlist.h>
+#include <QtConcurrent/qtconcurrentrun.h>
+
+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"
+};
+
+typedef QList<QAndroidMetaDataReaderControl *> AndroidMetaDataReaders;
+Q_GLOBAL_STATIC(AndroidMetaDataReaders, g_metaDataReaders)
+Q_GLOBAL_STATIC(QMutex, g_metaDataReadersMtx)
+
+QAndroidMetaDataReaderControl::QAndroidMetaDataReaderControl(QObject *parent)
+ : QMetaDataReaderControl(parent)
+ , m_available(false)
+{
+}
+
+QAndroidMetaDataReaderControl::~QAndroidMetaDataReaderControl()
+{
+ QMutexLocker l(g_metaDataReadersMtx());
+ const int idx = g_metaDataReaders->indexOf(this);
+ if (idx != -1)
+ g_metaDataReaders->remove(idx);
+}
+
+bool QAndroidMetaDataReaderControl::isMetaDataAvailable() const
+{
+ 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 QUrl &media)
+{
+ const QMutexLocker l(&m_mtx);
+ m_metadata.clear();
+ m_mediaContent = media;
+}
+
+void QAndroidMetaDataReaderControl::onUpdateMetaData()
+{
+ {
+ const QMutexLocker l(g_metaDataReadersMtx());
+ if (!g_metaDataReaders->contains(this))
+ g_metaDataReaders->append(this);
+ }
+
+ const QMutexLocker ml(&m_mtx);
+ if (m_mediaContent.isNull())
+ return;
+
+ const QUrl &url = m_mediaContent.request().url();
+ QtConcurrent::run(&extractMetadata, this, url);
+}
+
+void QAndroidMetaDataReaderControl::updateData(const QVariantMap &metadata, const QUrl &url)
+{
+ const QMutexLocker l(&m_mtx);
+
+ if (m_mediaContent.request().url() != url)
+ return;
+
+ const bool oldAvailable = m_available;
+ m_metadata = metadata;
+ m_available = !m_metadata.isEmpty();
+
+ if (m_available != oldAvailable)
+ Q_EMIT metaDataAvailableChanged(m_available);
+
+ Q_EMIT metaDataChanged();
+}
+
+void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderControl *caller,
+ const QUrl &url)
+{
+ QVariantMap metadata;
+
+ if (!url.isEmpty()) {
+ AndroidMediaMetadataRetriever retriever;
+ if (!retriever.setDataSource(url))
+ return;
+
+ QString mimeType = retriever.extractMetadata(AndroidMediaMetadataRetriever::MimeType);
+ if (!mimeType.isNull())
+ metadata.insert(QMediaMetaData::MediaType, mimeType);
+
+ 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('/', Qt::SkipEmptyParts));
+ }
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Author, string.split('/', Qt::SkipEmptyParts));
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate);
+ if (!string.isNull()) {
+ metadata.insert(isVideo ? QMediaMetaData::VideoBitRate
+ : QMediaMetaData::AudioBitRate,
+ string.toInt());
+ }
+
+ 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('/', Qt::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 = QStringView{string}.mid(1, string.length() - 2).toInt(&ok);
+ if (ok && genreId >= 0 && genreId <= 125)
+ string = QLatin1String(qt_ID3GenreNames[genreId]);
+ }
+ metadata.insert(QMediaMetaData::Genre, string);
+ }
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Title);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Title, string);
+
+ 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('/', Qt::SkipEmptyParts));
+
+ string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Year);
+ if (!string.isNull())
+ metadata.insert(QMediaMetaData::Year, string.toInt());
+ }
+
+ const QMutexLocker lock(g_metaDataReadersMtx());
+ if (!g_metaDataReaders->contains(caller))
+ return;
+
+ caller->updateData(metadata, url);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h
new file mode 100644
index 000000000..2ee857fa1
--- /dev/null
+++ b/src/multimedia/platform/android/mediaplayer/qandroidmetadatareadercontrol_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMETADATAREADERCONTROL_H
+#define QANDROIDMETADATAREADERCONTROL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QMetaDataReaderControl>
+#include <QUrl.h>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMediaMetadataRetriever;
+
+class QAndroidMetaDataReaderControl : public QMetaDataReaderControl
+{
+ Q_OBJECT
+public:
+ explicit QAndroidMetaDataReaderControl(QObject *parent = 0);
+ ~QAndroidMetaDataReaderControl() override;
+
+ bool isMetaDataAvailable() const override;
+
+ QVariant metaData(const QString &key) const override;
+ QStringList availableMetaData() const override;
+
+public Q_SLOTS:
+ void onMediaChanged(const QUrl &media);
+ void onUpdateMetaData();
+
+private:
+ void updateData(const QVariantMap &metadata, const QUrl &url);
+ static void extractMetadata(QAndroidMetaDataReaderControl *caller, const QUrl &url);
+
+ mutable QMutex m_mtx;
+ QUrl m_mediaContent;
+ bool m_available;
+ QVariantMap m_metadata;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMETADATAREADERCONTROL_H
diff --git a/src/multimedia/platform/android/qandroidmediaserviceplugin.cpp b/src/multimedia/platform/android/qandroidmediaserviceplugin.cpp
new file mode 100644
index 000000000..52cedb5bd
--- /dev/null
+++ b/src/multimedia/platform/android/qandroidmediaserviceplugin.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidmediaserviceplugin_p.h"
+
+#include "qandroidmediaservice_p.h"
+#include "qandroidcaptureservice_p.h"
+#include "qandroidaudioinputselectorcontrol_p.h"
+#include "qandroidcamerasession_p.h"
+#include "androidmediaplayer_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidcamera_p.h"
+#include "androidmultimediautils_p.h"
+#include "androidmediarecorder_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidglobal_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qtAndroidMediaPlugin, "qt.multimedia.android")
+
+QAndroidMediaServicePlugin::QAndroidMediaServicePlugin()
+{
+}
+
+QAndroidMediaServicePlugin::~QAndroidMediaServicePlugin()
+{
+}
+
+QMediaService *QAndroidMediaServicePlugin::create(const QString &key)
+{
+ if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
+ return new QAndroidMediaService;
+
+ if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)
+ || key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) {
+ return new QAndroidCaptureService(key);
+ }
+
+ qCWarning(qtAndroidMediaPlugin) << "Android service plugin: unsupported key:" << key;
+ return 0;
+}
+
+void QAndroidMediaServicePlugin::release(QMediaService *service)
+{
+ delete service;
+}
+
+QByteArray QAndroidMediaServicePlugin::defaultDevice(const QByteArray &service) const
+{
+ if (service == Q_MEDIASERVICE_CAMERA && !QAndroidCameraSession::availableCameras().isEmpty())
+ return QAndroidCameraSession::availableCameras().first().name;
+
+ return QByteArray();
+}
+
+QList<QByteArray> QAndroidMediaServicePlugin::devices(const QByteArray &service) const
+{
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ QList<QByteArray> devices;
+ const QList<AndroidCameraInfo> &cameras = QAndroidCameraSession::availableCameras();
+ for (int i = 0; i < cameras.count(); ++i)
+ devices.append(cameras.at(i).name);
+ return devices;
+ }
+
+ if (service == Q_MEDIASERVICE_AUDIOSOURCE)
+ return QAndroidAudioInputSelectorControl::availableDevices();
+
+ return QList<QByteArray>();
+}
+
+QString QAndroidMediaServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)
+{
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ const QList<AndroidCameraInfo> &cameras = QAndroidCameraSession::availableCameras();
+ for (int i = 0; i < cameras.count(); ++i) {
+ const AndroidCameraInfo &info = cameras.at(i);
+ if (info.name == device)
+ return info.description;
+ }
+ }
+
+ if (service == Q_MEDIASERVICE_AUDIOSOURCE)
+ return QAndroidAudioInputSelectorControl::availableDeviceDescription(device);
+
+ return QString();
+}
+
+QT_END_NAMESPACE
+
+Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
+{
+ static bool initialized = false;
+ if (initialized)
+ return JNI_VERSION_1_6;
+ initialized = true;
+
+ QT_USE_NAMESPACE
+ typedef union {
+ JNIEnv *nativeEnvironment;
+ void *venv;
+ } UnionJNIEnvToVoid;
+
+ UnionJNIEnvToVoid uenv;
+ uenv.venv = NULL;
+
+ if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
+ return JNI_ERR;
+
+ JNIEnv *jniEnv = uenv.nativeEnvironment;
+
+ if (!AndroidMediaPlayer::initJNI(jniEnv) ||
+ !AndroidCamera::initJNI(jniEnv) ||
+ !AndroidMediaRecorder::initJNI(jniEnv) ||
+ !AndroidSurfaceHolder::initJNI(jniEnv)) {
+ return JNI_ERR;
+ }
+
+ AndroidSurfaceTexture::initJNI(jniEnv);
+
+ return JNI_VERSION_1_6;
+}
diff --git a/src/multimedia/platform/android/qandroidmediaserviceplugin_p.h b/src/multimedia/platform/android/qandroidmediaserviceplugin_p.h
new file mode 100644
index 000000000..8c024cad7
--- /dev/null
+++ b/src/multimedia/platform/android/qandroidmediaserviceplugin_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDMEDIASERVICEPLUGIN_H
+#define QANDROIDMEDIASERVICEPLUGIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QMediaServiceProviderPlugin>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidMediaServicePlugin
+ : public QMediaServiceProviderPlugin
+ , public QMediaServiceSupportedDevicesInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QMediaServiceSupportedDevicesInterface)
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0"
+ FILE "android_mediaservice.json")
+
+public:
+ QAndroidMediaServicePlugin();
+ ~QAndroidMediaServicePlugin();
+
+ QMediaService* create(QString const& key) override;
+ void release(QMediaService *service) override;
+
+ QByteArray defaultDevice(const QByteArray &service) const override;
+ QList<QByteArray> devices(const QByteArray &service) const override;
+ QString deviceDescription(const QByteArray &service, const QByteArray &device) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDMEDIASERVICEPLUGIN_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
new file mode 100644
index 000000000..60fc4ec8a
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp
@@ -0,0 +1,1712 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidcamera_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidmultimediautils_p.h"
+#include "qandroidglobal_p.h"
+
+#include <qstringlist.h>
+#include <qdebug.h>
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qreadwritelock.h>
+#include <QtCore/qmutex.h>
+#include <QtMultimedia/private/qmemoryvideobuffer_p.h>
+
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+
+static const char QtCameraListenerClassName[] = "org/qtproject/qt/android/multimedia/QtCameraListener";
+
+typedef QHash<int, AndroidCamera *> CameraMap;
+Q_GLOBAL_STATIC(CameraMap, cameras)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+static inline bool exceptionCheckAndClear(JNIEnv *env)
+{
+ if (Q_UNLIKELY(env->ExceptionCheck())) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif // QT_DEBUG
+ env->ExceptionClear();
+ return true;
+ }
+
+ return false;
+}
+
+static QRect areaToRect(jobject areaObj)
+{
+ QJNIObjectPrivate area(areaObj);
+ QJNIObjectPrivate rect = area.getObjectField("rect", "Landroid/graphics/Rect;");
+
+ return QRect(rect.getField<jint>("left"),
+ rect.getField<jint>("top"),
+ rect.callMethod<jint>("width"),
+ rect.callMethod<jint>("height"));
+}
+
+static QJNIObjectPrivate rectToArea(const QRect &rect)
+{
+ QJNIObjectPrivate jrect("android/graphics/Rect",
+ "(IIII)V",
+ rect.left(), rect.top(), rect.right(), rect.bottom());
+
+ QJNIObjectPrivate area("android/hardware/Camera$Area",
+ "(Landroid/graphics/Rect;I)V",
+ jrect.object(), 500);
+
+ return area;
+}
+
+// native method for QtCameraLisener.java
+static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ Q_EMIT (*it)->autoFocusComplete(success);
+}
+
+static void notifyPictureExposed(JNIEnv* , jobject, int id)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ Q_EMIT (*it)->pictureExposed();
+}
+
+static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ const int arrayLength = env->GetArrayLength(data);
+ QByteArray bytes(arrayLength, Qt::Uninitialized);
+ env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
+ Q_EMIT (*it)->pictureCaptured(bytes);
+}
+
+static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
+ int width, int height, int format, int bpl)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ const int arrayLength = env->GetArrayLength(data);
+ if (arrayLength == 0)
+ return;
+
+ QByteArray bytes(arrayLength, Qt::Uninitialized);
+ env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
+
+ QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl),
+ QSize(width, height),
+ qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
+
+ Q_EMIT (*it)->newPreviewFrame(frame);
+}
+
+static void notifyFrameAvailable(JNIEnv *, jobject, int id)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ (*it)->fetchLastPreviewFrame();
+}
+
+class AndroidCameraPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ AndroidCameraPrivate();
+ ~AndroidCameraPrivate();
+
+ Q_INVOKABLE bool init(int cameraId);
+
+ Q_INVOKABLE void release();
+ Q_INVOKABLE bool lock();
+ Q_INVOKABLE bool unlock();
+ Q_INVOKABLE bool reconnect();
+
+ Q_INVOKABLE AndroidCamera::CameraFacing getFacing();
+ Q_INVOKABLE int getNativeOrientation();
+
+ Q_INVOKABLE QSize getPreferredPreviewSizeForVideo();
+ Q_INVOKABLE QList<QSize> getSupportedPreviewSizes();
+
+ Q_INVOKABLE QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange();
+
+ Q_INVOKABLE AndroidCamera::FpsRange getPreviewFpsRange();
+ Q_INVOKABLE void setPreviewFpsRange(int min, int max);
+
+ Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat();
+ Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt);
+ Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats();
+
+ Q_INVOKABLE QSize previewSize() const { return m_previewSize; }
+ Q_INVOKABLE QSize getPreviewSize();
+ Q_INVOKABLE void updatePreviewSize();
+ Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture);
+ Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder);
+ Q_INVOKABLE void setDisplayOrientation(int degrees);
+
+ Q_INVOKABLE bool isZoomSupported();
+ Q_INVOKABLE int getMaxZoom();
+ Q_INVOKABLE QList<int> getZoomRatios();
+ Q_INVOKABLE int getZoom();
+ Q_INVOKABLE void setZoom(int value);
+
+ Q_INVOKABLE QString getFlashMode();
+ Q_INVOKABLE void setFlashMode(const QString &value);
+
+ Q_INVOKABLE QString getFocusMode();
+ Q_INVOKABLE void setFocusMode(const QString &value);
+
+ Q_INVOKABLE int getMaxNumFocusAreas();
+ Q_INVOKABLE QList<QRect> getFocusAreas();
+ Q_INVOKABLE void setFocusAreas(const QList<QRect> &areas);
+
+ Q_INVOKABLE void autoFocus();
+ Q_INVOKABLE void cancelAutoFocus();
+
+ Q_INVOKABLE bool isAutoExposureLockSupported();
+ Q_INVOKABLE bool getAutoExposureLock();
+ Q_INVOKABLE void setAutoExposureLock(bool toggle);
+
+ Q_INVOKABLE bool isAutoWhiteBalanceLockSupported();
+ Q_INVOKABLE bool getAutoWhiteBalanceLock();
+ Q_INVOKABLE void setAutoWhiteBalanceLock(bool toggle);
+
+ Q_INVOKABLE int getExposureCompensation();
+ Q_INVOKABLE void setExposureCompensation(int value);
+ Q_INVOKABLE float getExposureCompensationStep();
+ Q_INVOKABLE int getMinExposureCompensation();
+ Q_INVOKABLE int getMaxExposureCompensation();
+
+ Q_INVOKABLE QString getSceneMode();
+ Q_INVOKABLE void setSceneMode(const QString &value);
+
+ Q_INVOKABLE QString getWhiteBalance();
+ Q_INVOKABLE void setWhiteBalance(const QString &value);
+
+ Q_INVOKABLE void updateRotation();
+
+ Q_INVOKABLE QList<QSize> getSupportedPictureSizes();
+ Q_INVOKABLE void setPictureSize(const QSize &size);
+ Q_INVOKABLE void setJpegQuality(int quality);
+
+ Q_INVOKABLE void startPreview();
+ Q_INVOKABLE void stopPreview();
+
+ Q_INVOKABLE void takePicture();
+
+ Q_INVOKABLE void setupPreviewFrameCallback();
+ Q_INVOKABLE void notifyNewFrames(bool notify);
+ Q_INVOKABLE void fetchLastPreviewFrame();
+
+ Q_INVOKABLE void applyParameters();
+
+ Q_INVOKABLE QStringList callParametersStringListMethod(const QByteArray &methodName);
+
+ int m_cameraId;
+ QRecursiveMutex m_parametersMutex;
+ QSize m_previewSize;
+ int m_rotation;
+ QJNIObjectPrivate m_info;
+ QJNIObjectPrivate m_parameters;
+ QJNIObjectPrivate m_camera;
+ QJNIObjectPrivate m_cameraListener;
+
+Q_SIGNALS:
+ void previewSizeChanged();
+ void previewStarted();
+ void previewFailedToStart();
+ void previewStopped();
+
+ void autoFocusStarted();
+
+ void whiteBalanceChanged();
+
+ void takePictureFailed();
+
+ void lastPreviewFrameFetched(const QVideoFrame &frame);
+};
+
+AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
+ : QObject(),
+ d_ptr(d),
+ m_worker(worker)
+
+{
+ qRegisterMetaType<QList<int> >();
+ qRegisterMetaType<QList<QSize> >();
+ qRegisterMetaType<QList<QRect> >();
+ qRegisterMetaType<ImageFormat>();
+
+ connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged);
+ connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted);
+ connect(d, &AndroidCameraPrivate::previewFailedToStart, this, &AndroidCamera::previewFailedToStart);
+ connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped);
+ connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted);
+ connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged);
+ connect(d, &AndroidCameraPrivate::takePictureFailed, this, &AndroidCamera::takePictureFailed);
+ connect(d, &AndroidCameraPrivate::lastPreviewFrameFetched, this, &AndroidCamera::lastPreviewFrameFetched);
+}
+
+AndroidCamera::~AndroidCamera()
+{
+ Q_D(AndroidCamera);
+ if (d->m_camera.isValid()) {
+ release();
+ QWriteLocker locker(rwLock);
+ cameras->remove(cameraId());
+ }
+
+ m_worker->exit();
+ m_worker->wait(5000);
+}
+
+AndroidCamera *AndroidCamera::open(int cameraId)
+{
+ if (!qt_androidRequestCameraPermission())
+ return nullptr;
+
+ AndroidCameraPrivate *d = new AndroidCameraPrivate();
+ QThread *worker = new QThread;
+ worker->start();
+ d->moveToThread(worker);
+ connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId));
+ if (!ok) {
+ worker->quit();
+ worker->wait(5000);
+ delete worker;
+ return 0;
+ }
+
+ AndroidCamera *q = new AndroidCamera(d, worker);
+ QWriteLocker locker(rwLock);
+ cameras->insert(cameraId, q);
+
+ return q;
+}
+
+int AndroidCamera::cameraId() const
+{
+ Q_D(const AndroidCamera);
+ return d->m_cameraId;
+}
+
+bool AndroidCamera::lock()
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+ return ok;
+}
+
+bool AndroidCamera::unlock()
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+ return ok;
+}
+
+bool AndroidCamera::reconnect()
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok));
+ return ok;
+}
+
+void AndroidCamera::release()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "release", Qt::BlockingQueuedConnection);
+}
+
+AndroidCamera::CameraFacing AndroidCamera::getFacing()
+{
+ Q_D(AndroidCamera);
+ return d->getFacing();
+}
+
+int AndroidCamera::getNativeOrientation()
+{
+ Q_D(AndroidCamera);
+ return d->getNativeOrientation();
+}
+
+QSize AndroidCamera::getPreferredPreviewSizeForVideo()
+{
+ Q_D(AndroidCamera);
+ return d->getPreferredPreviewSizeForVideo();
+}
+
+QList<QSize> AndroidCamera::getSupportedPreviewSizes()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPreviewSizes();
+}
+
+QList<AndroidCamera::FpsRange> AndroidCamera::getSupportedPreviewFpsRange()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPreviewFpsRange();
+}
+
+AndroidCamera::FpsRange AndroidCamera::getPreviewFpsRange()
+{
+ Q_D(AndroidCamera);
+ return d->getPreviewFpsRange();
+}
+
+void AndroidCamera::setPreviewFpsRange(FpsRange range)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setPreviewFpsRange", Q_ARG(int, range.min), Q_ARG(int, range.max));
+}
+
+AndroidCamera::ImageFormat AndroidCamera::getPreviewFormat()
+{
+ Q_D(AndroidCamera);
+ return d->getPreviewFormat();
+}
+
+void AndroidCamera::setPreviewFormat(ImageFormat fmt)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt));
+}
+
+QList<AndroidCamera::ImageFormat> AndroidCamera::getSupportedPreviewFormats()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPreviewFormats();
+}
+
+QSize AndroidCamera::previewSize() const
+{
+ Q_D(const AndroidCamera);
+ return d->m_previewSize;
+}
+
+QSize AndroidCamera::actualPreviewSize()
+{
+ Q_D(AndroidCamera);
+ return d->getPreviewSize();
+}
+
+void AndroidCamera::setPreviewSize(const QSize &size)
+{
+ Q_D(AndroidCamera);
+ d->m_parametersMutex.lock();
+ bool areParametersValid = d->m_parameters.isValid();
+ d->m_parametersMutex.unlock();
+ if (!areParametersValid)
+ return;
+
+ d->m_previewSize = size;
+ QMetaObject::invokeMethod(d, "updatePreviewSize");
+}
+
+bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture)
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d,
+ "setPreviewTexture",
+ Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(bool, ok),
+ Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0));
+ return ok;
+}
+
+bool AndroidCamera::setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder)
+{
+ Q_D(AndroidCamera);
+ bool ok = true;
+ QMetaObject::invokeMethod(d,
+ "setPreviewDisplay",
+ Qt::BlockingQueuedConnection,
+ Q_RETURN_ARG(bool, ok),
+ Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0));
+ return ok;
+}
+
+void AndroidCamera::setDisplayOrientation(int degrees)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setDisplayOrientation", Qt::QueuedConnection, Q_ARG(int, degrees));
+}
+
+bool AndroidCamera::isZoomSupported()
+{
+ Q_D(AndroidCamera);
+ return d->isZoomSupported();
+}
+
+int AndroidCamera::getMaxZoom()
+{
+ Q_D(AndroidCamera);
+ return d->getMaxZoom();
+}
+
+QList<int> AndroidCamera::getZoomRatios()
+{
+ Q_D(AndroidCamera);
+ return d->getZoomRatios();
+}
+
+int AndroidCamera::getZoom()
+{
+ Q_D(AndroidCamera);
+ return d->getZoom();
+}
+
+void AndroidCamera::setZoom(int value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setZoom", Q_ARG(int, value));
+}
+
+QStringList AndroidCamera::getSupportedFlashModes()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedFlashModes");
+}
+
+QString AndroidCamera::getFlashMode()
+{
+ Q_D(AndroidCamera);
+ return d->getFlashMode();
+}
+
+void AndroidCamera::setFlashMode(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setFlashMode", Q_ARG(QString, value));
+}
+
+QStringList AndroidCamera::getSupportedFocusModes()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedFocusModes");
+}
+
+QString AndroidCamera::getFocusMode()
+{
+ Q_D(AndroidCamera);
+ return d->getFocusMode();
+}
+
+void AndroidCamera::setFocusMode(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setFocusMode", Q_ARG(QString, value));
+}
+
+int AndroidCamera::getMaxNumFocusAreas()
+{
+ Q_D(AndroidCamera);
+ return d->getMaxNumFocusAreas();
+}
+
+QList<QRect> AndroidCamera::getFocusAreas()
+{
+ Q_D(AndroidCamera);
+ return d->getFocusAreas();
+}
+
+void AndroidCamera::setFocusAreas(const QList<QRect> &areas)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setFocusAreas", Q_ARG(QList<QRect>, areas));
+}
+
+void AndroidCamera::autoFocus()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "autoFocus");
+}
+
+void AndroidCamera::cancelAutoFocus()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "cancelAutoFocus", Qt::QueuedConnection);
+}
+
+bool AndroidCamera::isAutoExposureLockSupported()
+{
+ Q_D(AndroidCamera);
+ return d->isAutoExposureLockSupported();
+}
+
+bool AndroidCamera::getAutoExposureLock()
+{
+ Q_D(AndroidCamera);
+ return d->getAutoExposureLock();
+}
+
+void AndroidCamera::setAutoExposureLock(bool toggle)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setAutoExposureLock", Q_ARG(bool, toggle));
+}
+
+bool AndroidCamera::isAutoWhiteBalanceLockSupported()
+{
+ Q_D(AndroidCamera);
+ return d->isAutoWhiteBalanceLockSupported();
+}
+
+bool AndroidCamera::getAutoWhiteBalanceLock()
+{
+ Q_D(AndroidCamera);
+ return d->getAutoWhiteBalanceLock();
+}
+
+void AndroidCamera::setAutoWhiteBalanceLock(bool toggle)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setAutoWhiteBalanceLock", Q_ARG(bool, toggle));
+}
+
+int AndroidCamera::getExposureCompensation()
+{
+ Q_D(AndroidCamera);
+ return d->getExposureCompensation();
+}
+
+void AndroidCamera::setExposureCompensation(int value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setExposureCompensation", Q_ARG(int, value));
+}
+
+float AndroidCamera::getExposureCompensationStep()
+{
+ Q_D(AndroidCamera);
+ return d->getExposureCompensationStep();
+}
+
+int AndroidCamera::getMinExposureCompensation()
+{
+ Q_D(AndroidCamera);
+ return d->getMinExposureCompensation();
+}
+
+int AndroidCamera::getMaxExposureCompensation()
+{
+ Q_D(AndroidCamera);
+ return d->getMaxExposureCompensation();
+}
+
+QStringList AndroidCamera::getSupportedSceneModes()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedSceneModes");
+}
+
+QString AndroidCamera::getSceneMode()
+{
+ Q_D(AndroidCamera);
+ return d->getSceneMode();
+}
+
+void AndroidCamera::setSceneMode(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setSceneMode", Q_ARG(QString, value));
+}
+
+QStringList AndroidCamera::getSupportedWhiteBalance()
+{
+ Q_D(AndroidCamera);
+ return d->callParametersStringListMethod("getSupportedWhiteBalance");
+}
+
+QString AndroidCamera::getWhiteBalance()
+{
+ Q_D(AndroidCamera);
+ return d->getWhiteBalance();
+}
+
+void AndroidCamera::setWhiteBalance(const QString &value)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setWhiteBalance", Q_ARG(QString, value));
+}
+
+void AndroidCamera::setRotation(int rotation)
+{
+ Q_D(AndroidCamera);
+ //We need to do it here and not in worker class because we cache rotation
+ d->m_parametersMutex.lock();
+ bool areParametersValid = d->m_parameters.isValid();
+ d->m_parametersMutex.unlock();
+ if (!areParametersValid)
+ return;
+
+ d->m_rotation = rotation;
+ QMetaObject::invokeMethod(d, "updateRotation");
+}
+
+int AndroidCamera::getRotation() const
+{
+ Q_D(const AndroidCamera);
+ return d->m_rotation;
+}
+
+QList<QSize> AndroidCamera::getSupportedPictureSizes()
+{
+ Q_D(AndroidCamera);
+ return d->getSupportedPictureSizes();
+}
+
+void AndroidCamera::setPictureSize(const QSize &size)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setPictureSize", Q_ARG(QSize, size));
+}
+
+void AndroidCamera::setJpegQuality(int quality)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setJpegQuality", Q_ARG(int, quality));
+}
+
+void AndroidCamera::takePicture()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "takePicture", Qt::BlockingQueuedConnection);
+}
+
+void AndroidCamera::setupPreviewFrameCallback()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "setupPreviewFrameCallback");
+}
+
+void AndroidCamera::notifyNewFrames(bool notify)
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "notifyNewFrames", Q_ARG(bool, notify));
+}
+
+void AndroidCamera::fetchLastPreviewFrame()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "fetchLastPreviewFrame");
+}
+
+QJNIObjectPrivate AndroidCamera::getCameraObject()
+{
+ Q_D(AndroidCamera);
+ return d->m_camera;
+}
+
+int AndroidCamera::getNumberOfCameras()
+{
+ if (!qt_androidRequestCameraPermission())
+ return 0;
+
+ return QJNIObjectPrivate::callStaticMethod<jint>("android/hardware/Camera",
+ "getNumberOfCameras");
+}
+
+void AndroidCamera::getCameraInfo(int id, AndroidCameraInfo *info)
+{
+ Q_ASSERT(info);
+
+ QJNIObjectPrivate cameraInfo("android/hardware/Camera$CameraInfo");
+ QJNIObjectPrivate::callStaticMethod<void>("android/hardware/Camera",
+ "getCameraInfo",
+ "(ILandroid/hardware/Camera$CameraInfo;)V",
+ id, cameraInfo.object());
+
+ AndroidCamera::CameraFacing facing = AndroidCamera::CameraFacing(cameraInfo.getField<jint>("facing"));
+ // The orientation provided by Android is counter-clockwise, we need it clockwise
+ info->orientation = (360 - cameraInfo.getField<jint>("orientation")) % 360;
+
+ switch (facing) {
+ case AndroidCamera::CameraFacingBack:
+ info->name = QByteArray("back");
+ info->description = QStringLiteral("Rear-facing camera");
+ info->position = QCamera::BackFace;
+ break;
+ case AndroidCamera::CameraFacingFront:
+ info->name = QByteArray("front");
+ info->description = QStringLiteral("Front-facing camera");
+ info->position = QCamera::FrontFace;
+ break;
+ default:
+ break;
+ }
+}
+
+void AndroidCamera::startPreview()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "startPreview");
+}
+
+void AndroidCamera::stopPreview()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "stopPreview");
+}
+
+void AndroidCamera::stopPreviewSynchronous()
+{
+ Q_D(AndroidCamera);
+ QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection);
+}
+
+AndroidCameraPrivate::AndroidCameraPrivate()
+ : QObject()
+{
+}
+
+AndroidCameraPrivate::~AndroidCameraPrivate()
+{
+}
+
+static qint32 s_activeCameras = 0;
+
+bool AndroidCameraPrivate::init(int cameraId)
+{
+ m_cameraId = cameraId;
+ QJNIEnvironmentPrivate env;
+
+ const bool opened = s_activeCameras & (1 << cameraId);
+ if (opened)
+ return false;
+
+ m_camera = QJNIObjectPrivate::callStaticObjectMethod("android/hardware/Camera",
+ "open",
+ "(I)Landroid/hardware/Camera;",
+ cameraId);
+ if (exceptionCheckAndClear(env) || !m_camera.isValid())
+ return false;
+
+ m_cameraListener = QJNIObjectPrivate(QtCameraListenerClassName, "(I)V", m_cameraId);
+ m_info = QJNIObjectPrivate("android/hardware/Camera$CameraInfo");
+ m_camera.callStaticMethod<void>("android/hardware/Camera",
+ "getCameraInfo",
+ "(ILandroid/hardware/Camera$CameraInfo;)V",
+ cameraId,
+ m_info.object());
+
+ QJNIObjectPrivate params = m_camera.callObjectMethod("getParameters",
+ "()Landroid/hardware/Camera$Parameters;");
+ m_parameters = QJNIObjectPrivate(params);
+ s_activeCameras |= 1 << cameraId;
+
+ return true;
+}
+
+void AndroidCameraPrivate::release()
+{
+ m_previewSize = QSize();
+ m_parametersMutex.lock();
+ m_parameters = QJNIObjectPrivate();
+ m_parametersMutex.unlock();
+ if (m_camera.isValid()) {
+ m_camera.callMethod<void>("release");
+ s_activeCameras &= ~(1 << m_cameraId);
+ }
+}
+
+bool AndroidCameraPrivate::lock()
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("lock");
+ return !exceptionCheckAndClear(env);
+}
+
+bool AndroidCameraPrivate::unlock()
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("unlock");
+ return !exceptionCheckAndClear(env);
+}
+
+bool AndroidCameraPrivate::reconnect()
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("reconnect");
+ return !exceptionCheckAndClear(env);
+}
+
+AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing()
+{
+ return AndroidCamera::CameraFacing(m_info.getField<jint>("facing"));
+}
+
+int AndroidCameraPrivate::getNativeOrientation()
+{
+ return m_info.getField<jint>("orientation");
+}
+
+QSize AndroidCameraPrivate::getPreferredPreviewSizeForVideo()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return QSize();
+
+ QJNIObjectPrivate size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo",
+ "()Landroid/hardware/Camera$Size;");
+
+ if (!size.isValid())
+ return QSize();
+
+ return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
+}
+
+QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes()
+{
+ QList<QSize> list;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPreviewSizes",
+ "()Ljava/util/List;");
+ int count = sizeList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate size = sizeList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
+ }
+
+ std::sort(list.begin(), list.end(), qt_sizeLessThan);
+ }
+
+ return list;
+}
+
+QList<AndroidCamera::FpsRange> AndroidCameraPrivate::getSupportedPreviewFpsRange()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QJNIEnvironmentPrivate env;
+
+ QList<AndroidCamera::FpsRange> rangeList;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate rangeListNative = m_parameters.callObjectMethod("getSupportedPreviewFpsRange",
+ "()Ljava/util/List;");
+ int count = rangeListNative.callMethod<jint>("size");
+
+ rangeList.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate range = rangeListNative.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+
+ jintArray jRange = static_cast<jintArray>(range.object());
+ jint* rangeArray = env->GetIntArrayElements(jRange, 0);
+
+ AndroidCamera::FpsRange fpsRange;
+
+ fpsRange.min = rangeArray[0];
+ fpsRange.max = rangeArray[1];
+
+ env->ReleaseIntArrayElements(jRange, rangeArray, 0);
+
+ rangeList << fpsRange;
+ }
+ }
+
+ return rangeList;
+}
+
+AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QJNIEnvironmentPrivate env;
+
+ AndroidCamera::FpsRange range;
+
+ if (!m_parameters.isValid())
+ return range;
+
+ jintArray jRangeArray = env->NewIntArray(2);
+ m_parameters.callMethod<void>("getPreviewFpsRange", "([I)V", jRangeArray);
+
+ jint* jRangeElements = env->GetIntArrayElements(jRangeArray, 0);
+
+ range.min = jRangeElements[0];
+ range.max = jRangeElements[1];
+
+ env->ReleaseIntArrayElements(jRangeArray, jRangeElements, 0);
+ env->DeleteLocalRef(jRangeArray);
+
+ return range;
+}
+
+void AndroidCameraPrivate::setPreviewFpsRange(int min, int max)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ QJNIEnvironmentPrivate env;
+ m_parameters.callMethod<void>("setPreviewFpsRange", "(II)V", min, max);
+ exceptionCheckAndClear(env);
+}
+
+AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return AndroidCamera::UnknownImageFormat;
+
+ return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat"));
+}
+
+void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setPreviewFormat", "(I)V", jint(fmt));
+ applyParameters();
+}
+
+QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats()
+{
+ QList<AndroidCamera::ImageFormat> list;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate formatList = m_parameters.callObjectMethod("getSupportedPreviewFormats",
+ "()Ljava/util/List;");
+ int count = formatList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate format = formatList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue")));
+ }
+ }
+
+ return list;
+}
+
+QSize AndroidCameraPrivate::getPreviewSize()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return QSize();
+
+ QJNIObjectPrivate size = m_parameters.callObjectMethod("getPreviewSize",
+ "()Landroid/hardware/Camera$Size;");
+
+ if (!size.isValid())
+ return QSize();
+
+ return QSize(size.getField<jint>("width"), size.getField<jint>("height"));
+}
+
+void AndroidCameraPrivate::updatePreviewSize()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (m_previewSize.isValid()) {
+ m_parameters.callMethod<void>("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height());
+ applyParameters();
+ }
+
+ emit previewSizeChanged();
+}
+
+bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture)
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("setPreviewTexture",
+ "(Landroid/graphics/SurfaceTexture;)V",
+ static_cast<jobject>(surfaceTexture));
+ return !exceptionCheckAndClear(env);
+}
+
+bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder)
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("setPreviewDisplay",
+ "(Landroid/view/SurfaceHolder;)V",
+ static_cast<jobject>(surfaceHolder));
+ return !exceptionCheckAndClear(env);
+}
+
+void AndroidCameraPrivate::setDisplayOrientation(int degrees)
+{
+ m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees);
+}
+
+bool AndroidCameraPrivate::isZoomSupported()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("isZoomSupported");
+}
+
+int AndroidCameraPrivate::getMaxZoom()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMaxZoom");
+}
+
+QList<int> AndroidCameraPrivate::getZoomRatios()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QList<int> ratios;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate ratioList = m_parameters.callObjectMethod("getZoomRatios",
+ "()Ljava/util/List;");
+ int count = ratioList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate zoomRatio = ratioList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+
+ ratios.append(zoomRatio.callMethod<jint>("intValue"));
+ }
+ }
+
+ return ratios;
+}
+
+int AndroidCameraPrivate::getZoom()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getZoom");
+}
+
+void AndroidCameraPrivate::setZoom(int value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setZoom", "(I)V", value);
+ applyParameters();
+}
+
+QString AndroidCameraPrivate::getFlashMode()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate flashMode = m_parameters.callObjectMethod("getFlashMode",
+ "()Ljava/lang/String;");
+ if (flashMode.isValid())
+ value = flashMode.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setFlashMode(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setFlashMode",
+ "(Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(value).object());
+ applyParameters();
+}
+
+QString AndroidCameraPrivate::getFocusMode()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate focusMode = m_parameters.callObjectMethod("getFocusMode",
+ "()Ljava/lang/String;");
+ if (focusMode.isValid())
+ value = focusMode.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setFocusMode(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setFocusMode",
+ "(Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(value).object());
+ applyParameters();
+}
+
+int AndroidCameraPrivate::getMaxNumFocusAreas()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return 0;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMaxNumFocusAreas");
+}
+
+QList<QRect> AndroidCameraPrivate::getFocusAreas()
+{
+ QList<QRect> areas;
+
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return areas;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate list = m_parameters.callObjectMethod("getFocusAreas",
+ "()Ljava/util/List;");
+
+ if (list.isValid()) {
+ int count = list.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate area = list.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+
+ areas.append(areaToRect(area.object()));
+ }
+ }
+ }
+
+ return areas;
+}
+
+void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ QJNIObjectPrivate list;
+
+ if (!areas.isEmpty()) {
+ QJNIEnvironmentPrivate env;
+ QJNIObjectPrivate arrayList("java/util/ArrayList", "(I)V", areas.size());
+ for (int i = 0; i < areas.size(); ++i) {
+ arrayList.callMethod<jboolean>("add",
+ "(Ljava/lang/Object;)Z",
+ rectToArea(areas.at(i)).object());
+ exceptionCheckAndClear(env);
+ }
+ list = arrayList;
+ }
+
+ m_parameters.callMethod<void>("setFocusAreas", "(Ljava/util/List;)V", list.object());
+
+ applyParameters();
+}
+
+void AndroidCameraPrivate::autoFocus()
+{
+ QJNIEnvironmentPrivate env;
+
+ m_camera.callMethod<void>("autoFocus",
+ "(Landroid/hardware/Camera$AutoFocusCallback;)V",
+ m_cameraListener.object());
+
+ if (!exceptionCheckAndClear(env))
+ emit autoFocusStarted();
+}
+
+void AndroidCameraPrivate::cancelAutoFocus()
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("cancelAutoFocus");
+ exceptionCheckAndClear(env);
+}
+
+bool AndroidCameraPrivate::isAutoExposureLockSupported()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return false;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported");
+}
+
+bool AndroidCameraPrivate::getAutoExposureLock()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return false;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("getAutoExposureLock");
+}
+
+void AndroidCameraPrivate::setAutoExposureLock(bool toggle)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle);
+ applyParameters();
+}
+
+bool AndroidCameraPrivate::isAutoWhiteBalanceLockSupported()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return false;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported");
+}
+
+bool AndroidCameraPrivate::getAutoWhiteBalanceLock()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return false;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return false;
+
+ return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock");
+}
+
+void AndroidCameraPrivate::setAutoWhiteBalanceLock(bool toggle)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return;
+
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle);
+ applyParameters();
+}
+
+int AndroidCameraPrivate::getExposureCompensation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getExposureCompensation");
+}
+
+void AndroidCameraPrivate::setExposureCompensation(int value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setExposureCompensation", "(I)V", value);
+ applyParameters();
+}
+
+float AndroidCameraPrivate::getExposureCompensationStep()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jfloat>("getExposureCompensationStep");
+}
+
+int AndroidCameraPrivate::getMinExposureCompensation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMinExposureCompensation");
+}
+
+int AndroidCameraPrivate::getMaxExposureCompensation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return 0;
+
+ return m_parameters.callMethod<jint>("getMaxExposureCompensation");
+}
+
+QString AndroidCameraPrivate::getSceneMode()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate sceneMode = m_parameters.callObjectMethod("getSceneMode",
+ "()Ljava/lang/String;");
+ if (sceneMode.isValid())
+ value = sceneMode.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setSceneMode(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setSceneMode",
+ "(Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(value).object());
+ applyParameters();
+}
+
+QString AndroidCameraPrivate::getWhiteBalance()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QString value;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate wb = m_parameters.callObjectMethod("getWhiteBalance",
+ "()Ljava/lang/String;");
+ if (wb.isValid())
+ value = wb.toString();
+ }
+
+ return value;
+}
+
+void AndroidCameraPrivate::setWhiteBalance(const QString &value)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setWhiteBalance",
+ "(Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(value).object());
+ applyParameters();
+
+ emit whiteBalanceChanged();
+}
+
+void AndroidCameraPrivate::updateRotation()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ m_parameters.callMethod<void>("setRotation", "(I)V", m_rotation);
+ applyParameters();
+}
+
+QList<QSize> AndroidCameraPrivate::getSupportedPictureSizes()
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QList<QSize> list;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPictureSizes",
+ "()Ljava/util/List;");
+ int count = sizeList.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate size = sizeList.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height")));
+ }
+
+ std::sort(list.begin(), list.end(), qt_sizeLessThan);
+ }
+
+ return list;
+}
+
+void AndroidCameraPrivate::setPictureSize(const QSize &size)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setPictureSize", "(II)V", size.width(), size.height());
+ applyParameters();
+}
+
+void AndroidCameraPrivate::setJpegQuality(int quality)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ if (!m_parameters.isValid())
+ return;
+
+ m_parameters.callMethod<void>("setJpegQuality", "(I)V", quality);
+ applyParameters();
+}
+
+void AndroidCameraPrivate::startPreview()
+{
+ QJNIEnvironmentPrivate env;
+
+ setupPreviewFrameCallback();
+ m_camera.callMethod<void>("startPreview");
+
+ if (exceptionCheckAndClear(env))
+ emit previewFailedToStart();
+ else
+ emit previewStarted();
+}
+
+void AndroidCameraPrivate::stopPreview()
+{
+ QJNIEnvironmentPrivate env;
+
+ // cancel any pending new frame notification
+ m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", false);
+
+ m_camera.callMethod<void>("stopPreview");
+
+ exceptionCheckAndClear(env);
+ emit previewStopped();
+}
+
+void AndroidCameraPrivate::takePicture()
+{
+ QJNIEnvironmentPrivate env;
+
+ // We must clear the preview callback before calling takePicture(), otherwise the call will
+ // block and the camera server will be frozen until the next device restart...
+ // That problem only happens on some devices and on the emulator
+ m_cameraListener.callMethod<void>("clearPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
+
+ m_camera.callMethod<void>("takePicture", "(Landroid/hardware/Camera$ShutterCallback;"
+ "Landroid/hardware/Camera$PictureCallback;"
+ "Landroid/hardware/Camera$PictureCallback;)V",
+ m_cameraListener.object(),
+ jobject(0),
+ m_cameraListener.object());
+
+ if (exceptionCheckAndClear(env))
+ emit takePictureFailed();
+}
+
+void AndroidCameraPrivate::setupPreviewFrameCallback()
+{
+ m_cameraListener.callMethod<void>("setupPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
+}
+
+void AndroidCameraPrivate::notifyNewFrames(bool notify)
+{
+ m_cameraListener.callMethod<void>("notifyNewFrames", "(Z)V", notify);
+}
+
+void AndroidCameraPrivate::fetchLastPreviewFrame()
+{
+ QJNIEnvironmentPrivate env;
+ QJNIObjectPrivate data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B");
+
+ if (!data.isValid()) {
+ // If there's no buffer received yet, retry when the next one arrives
+ m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", true);
+ return;
+ }
+
+ const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
+ if (arrayLength == 0)
+ return;
+
+ QByteArray bytes(arrayLength, Qt::Uninitialized);
+ env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
+ 0,
+ arrayLength,
+ reinterpret_cast<jbyte *>(bytes.data()));
+
+ const int width = m_cameraListener.callMethod<jint>("previewWidth");
+ const int height = m_cameraListener.callMethod<jint>("previewHeight");
+ const int format = m_cameraListener.callMethod<jint>("previewFormat");
+ const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine");
+
+ QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl),
+ QSize(width, height),
+ qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)));
+
+ emit lastPreviewFrameFetched(frame);
+}
+
+void AndroidCameraPrivate::applyParameters()
+{
+ QJNIEnvironmentPrivate env;
+ m_camera.callMethod<void>("setParameters",
+ "(Landroid/hardware/Camera$Parameters;)V",
+ m_parameters.object());
+ exceptionCheckAndClear(env);
+}
+
+QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName)
+{
+ const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex);
+
+ QStringList stringList;
+
+ if (m_parameters.isValid()) {
+ QJNIObjectPrivate list = m_parameters.callObjectMethod(methodName.constData(),
+ "()Ljava/util/List;");
+
+ if (list.isValid()) {
+ int count = list.callMethod<jint>("size");
+ for (int i = 0; i < count; ++i) {
+ QJNIObjectPrivate string = list.callObjectMethod("get",
+ "(I)Ljava/lang/Object;",
+ i);
+ stringList.append(string.toString());
+ }
+ }
+ }
+
+ return stringList;
+}
+
+bool AndroidCamera::initJNI(JNIEnv *env)
+{
+ jclass clazz = QJNIEnvironmentPrivate::findClass(QtCameraListenerClassName,
+ env);
+
+ static const JNINativeMethod methods[] = {
+ {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
+ {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
+ {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
+ {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame},
+ {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable}
+ };
+
+ if (clazz && env->RegisterNatives(clazz,
+ methods,
+ sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "androidcamera.moc"
diff --git a/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h b/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h
new file mode 100644
index 000000000..5536e5919
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Ruslan Baratov
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDCAMERA_H
+#define ANDROIDCAMERA_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <QtCore/private/qjni_p.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <QtMultimedia/qcamera.h>
+
+QT_BEGIN_NAMESPACE
+
+class QThread;
+
+class AndroidCameraPrivate;
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+
+struct AndroidCameraInfo
+{
+ QByteArray name;
+ QString description;
+ QCamera::Position position;
+ int orientation;
+};
+Q_DECLARE_TYPEINFO(AndroidCameraInfo, Q_MOVABLE_TYPE);
+
+class AndroidCamera : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(CameraFacing)
+ Q_ENUMS(ImageFormat)
+public:
+ enum CameraFacing {
+ CameraFacingBack = 0,
+ CameraFacingFront = 1
+ };
+
+ enum ImageFormat { // same values as in android.graphics.ImageFormat Java class
+ UnknownImageFormat = 0,
+ RGB565 = 4,
+ NV16 = 16,
+ NV21 = 17,
+ YUY2 = 20,
+ JPEG = 256,
+ YV12 = 842094169
+ };
+
+ // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFpsRange%28%29
+ // "The values are multiplied by 1000 and represented in integers"
+ struct FpsRange {
+ int min;
+ int max;
+
+ FpsRange(): min(0), max(0) {}
+
+ qreal getMinReal() const { return min / 1000.0; }
+ qreal getMaxReal() const { return max / 1000.0; }
+
+ static FpsRange makeFromQReal(qreal min, qreal max)
+ {
+ FpsRange range;
+ range.min = static_cast<int>(min * 1000.0);
+ range.max = static_cast<int>(max * 1000.0);
+ return range;
+ }
+ };
+
+ ~AndroidCamera();
+
+ static AndroidCamera *open(int cameraId);
+
+ int cameraId() const;
+
+ bool lock();
+ bool unlock();
+ bool reconnect();
+ void release();
+
+ CameraFacing getFacing();
+ int getNativeOrientation();
+
+ QSize getPreferredPreviewSizeForVideo();
+ QList<QSize> getSupportedPreviewSizes();
+
+ QList<FpsRange> getSupportedPreviewFpsRange();
+
+ FpsRange getPreviewFpsRange();
+ void setPreviewFpsRange(FpsRange);
+
+ ImageFormat getPreviewFormat();
+ void setPreviewFormat(ImageFormat fmt);
+ QList<ImageFormat> getSupportedPreviewFormats();
+
+ QSize previewSize() const;
+ QSize actualPreviewSize();
+ void setPreviewSize(const QSize &size);
+ bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture);
+ bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder);
+ void setDisplayOrientation(int degrees);
+
+ bool isZoomSupported();
+ int getMaxZoom();
+ QList<int> getZoomRatios();
+ int getZoom();
+ void setZoom(int value);
+
+ QStringList getSupportedFlashModes();
+ QString getFlashMode();
+ void setFlashMode(const QString &value);
+
+ QStringList getSupportedFocusModes();
+ QString getFocusMode();
+ void setFocusMode(const QString &value);
+
+ int getMaxNumFocusAreas();
+ QList<QRect> getFocusAreas();
+ void setFocusAreas(const QList<QRect> &areas);
+
+ void autoFocus();
+ void cancelAutoFocus();
+
+ bool isAutoExposureLockSupported();
+ bool getAutoExposureLock();
+ void setAutoExposureLock(bool toggle);
+
+ bool isAutoWhiteBalanceLockSupported();
+ bool getAutoWhiteBalanceLock();
+ void setAutoWhiteBalanceLock(bool toggle);
+
+ int getExposureCompensation();
+ void setExposureCompensation(int value);
+ float getExposureCompensationStep();
+ int getMinExposureCompensation();
+ int getMaxExposureCompensation();
+
+ QStringList getSupportedSceneModes();
+ QString getSceneMode();
+ void setSceneMode(const QString &value);
+
+ QStringList getSupportedWhiteBalance();
+ QString getWhiteBalance();
+ void setWhiteBalance(const QString &value);
+
+ void setRotation(int rotation);
+ int getRotation() const;
+
+ QList<QSize> getSupportedPictureSizes();
+ void setPictureSize(const QSize &size);
+ void setJpegQuality(int quality);
+
+ void startPreview();
+ void stopPreview();
+ void stopPreviewSynchronous();
+
+ void takePicture();
+
+ void setupPreviewFrameCallback();
+ void notifyNewFrames(bool notify);
+ void fetchLastPreviewFrame();
+ QJNIObjectPrivate getCameraObject();
+
+ static int getNumberOfCameras();
+ static void getCameraInfo(int id, AndroidCameraInfo *info);
+ static bool requestCameraPermission();
+
+ static bool initJNI(JNIEnv *env);
+
+Q_SIGNALS:
+ void previewSizeChanged();
+ void previewStarted();
+ void previewFailedToStart();
+ void previewStopped();
+
+ void autoFocusStarted();
+ void autoFocusComplete(bool success);
+
+ void whiteBalanceChanged();
+
+ void takePictureFailed();
+ void pictureExposed();
+ void pictureCaptured(const QByteArray &data);
+ void lastPreviewFrameFetched(const QVideoFrame &frame);
+ void newPreviewFrame(const QVideoFrame &frame);
+
+private:
+ AndroidCamera(AndroidCameraPrivate *d, QThread *worker);
+
+ Q_DECLARE_PRIVATE(AndroidCamera)
+ AndroidCameraPrivate *d_ptr;
+ QScopedPointer<QThread> m_worker;
+};
+
+Q_DECLARE_METATYPE(AndroidCamera::ImageFormat)
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDCAMERA_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp
new file mode 100644
index 000000000..3fa480acb
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidmediametadataretriever_p.h"
+
+#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");
+}
+
+AndroidMediaMetadataRetriever::~AndroidMediaMetadataRetriever()
+{
+ release();
+}
+
+QString AndroidMediaMetadataRetriever::extractMetadata(MetadataKey key)
+{
+ QString value;
+
+ QJNIObjectPrivate metadata = m_metadataRetriever.callObjectMethod("extractMetadata",
+ "(I)Ljava/lang/String;",
+ jint(key));
+ if (metadata.isValid())
+ value = metadata.toString();
+
+ return value;
+}
+
+void AndroidMediaMetadataRetriever::release()
+{
+ if (!m_metadataRetriever.isValid())
+ return;
+
+ m_metadataRetriever.callMethod<void>("release");
+}
+
+bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url)
+{
+ if (!m_metadataRetriever.isValid())
+ return false;
+
+ QJNIEnvironmentPrivate env;
+
+ if (url.isLocalFile()) { // also includes qrc files (copied to a temp file by QMediaPlayer)
+ QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path());
+ QJNIObjectPrivate fileInputStream("java/io/FileInputStream",
+ "(Ljava/lang/String;)V",
+ string.object());
+
+ if (exceptionCheckAndClear(env))
+ return false;
+
+ 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(url.toString(QUrl::FullyEncoded));
+ 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(url.toString(QUrl::FullyEncoded));
+ 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 (exceptionCheckAndClear(env))
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h
new file mode 100644
index 000000000..8a7b75480
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDMEDIAMETADATARETRIEVER_H
+#define ANDROIDMEDIAMETADATARETRIEVER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qjni_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMediaMetadataRetriever
+{
+public:
+ enum MetadataKey {
+ Album = 1,
+ AlbumArtist = 13,
+ Artist = 2,
+ Author = 3,
+ Bitrate = 20,
+ CDTrackNumber = 0,
+ Compilation = 15,
+ Composer = 4,
+ Date = 5,
+ DiscNumber = 14,
+ Duration = 9,
+ Genre = 6,
+ HasAudio = 16,
+ HasVideo = 17,
+ Location = 23,
+ MimeType = 12,
+ NumTracks = 10,
+ Title = 7,
+ VideoHeight = 19,
+ VideoWidth = 18,
+ VideoRotation = 24,
+ Writer = 11,
+ Year = 8
+ };
+
+ AndroidMediaMetadataRetriever();
+ ~AndroidMediaMetadataRetriever();
+
+ QString extractMetadata(MetadataKey key);
+ bool setDataSource(const QUrl &url);
+
+private:
+ void release();
+ QJNIObjectPrivate m_metadataRetriever;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMEDIAMETADATARETRIEVER_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
new file mode 100644
index 000000000..7fc15e788
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp
@@ -0,0 +1,435 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidmediaplayer_p.h"
+
+#include <QString>
+#include <QtCore/private/qjni_p.h>
+#include <QtCore/private/qjnihelpers_p.h>
+#include "androidsurfacetexture.h"
+#include <QList>
+#include <QReadWriteLock>
+
+static const char QtAndroidMediaPlayerClassName[] = "org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer";
+typedef QList<AndroidMediaPlayer *> MediaPlayerList;
+Q_GLOBAL_STATIC(MediaPlayerList, mediaPlayers)
+Q_GLOBAL_STATIC(QReadWriteLock, rwLock)
+
+QT_BEGIN_NAMESPACE
+
+AndroidMediaPlayer::AndroidMediaPlayer()
+ : QObject()
+{
+ QWriteLocker locker(rwLock);
+ auto context = QtAndroidPrivate::activity() ? QtAndroidPrivate::activity() : QtAndroidPrivate::service();
+ const jlong id = reinterpret_cast<jlong>(this);
+ mMediaPlayer = QJNIObjectPrivate(QtAndroidMediaPlayerClassName,
+ "(Landroid/content/Context;J)V",
+ context,
+ id);
+ mediaPlayers->append(this);
+}
+
+AndroidMediaPlayer::~AndroidMediaPlayer()
+{
+ QWriteLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(this);
+ Q_ASSERT(i != -1);
+ mediaPlayers->remove(i);
+}
+
+void AndroidMediaPlayer::release()
+{
+ mMediaPlayer.callMethod<void>("release");
+}
+
+void AndroidMediaPlayer::reset()
+{
+ mMediaPlayer.callMethod<void>("reset");
+}
+
+int AndroidMediaPlayer::getCurrentPosition()
+{
+ return mMediaPlayer.callMethod<jint>("getCurrentPosition");
+}
+
+int AndroidMediaPlayer::getDuration()
+{
+ return mMediaPlayer.callMethod<jint>("getDuration");
+}
+
+bool AndroidMediaPlayer::isPlaying()
+{
+ return mMediaPlayer.callMethod<jboolean>("isPlaying");
+}
+
+int AndroidMediaPlayer::volume()
+{
+ return mMediaPlayer.callMethod<jint>("getVolume");
+}
+
+bool AndroidMediaPlayer::isMuted()
+{
+ return mMediaPlayer.callMethod<jboolean>("isMuted");
+}
+
+qreal AndroidMediaPlayer::playbackRate()
+{
+ qreal rate(1.0);
+
+ if (QtAndroidPrivate::androidSdkVersion() < 23)
+ return rate;
+
+ QJNIObjectPrivate player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", "()Landroid/media/MediaPlayer;");
+ if (player.isValid()) {
+ QJNIObjectPrivate playbackParams = player.callObjectMethod("getPlaybackParams", "()Landroid/media/PlaybackParams;");
+ if (playbackParams.isValid()) {
+ const qreal speed = playbackParams.callMethod<jfloat>("getSpeed", "()F");
+ QJNIEnvironmentPrivate env;
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif // QT_DEBUG
+ env->ExceptionClear();
+ } else {
+ rate = speed;
+ }
+ }
+ }
+
+ return rate;
+}
+
+jobject AndroidMediaPlayer::display()
+{
+ return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object();
+}
+
+void AndroidMediaPlayer::play()
+{
+ mMediaPlayer.callMethod<void>("start");
+}
+
+void AndroidMediaPlayer::pause()
+{
+ mMediaPlayer.callMethod<void>("pause");
+}
+
+void AndroidMediaPlayer::stop()
+{
+ mMediaPlayer.callMethod<void>("stop");
+}
+
+void AndroidMediaPlayer::seekTo(qint32 msec)
+{
+ mMediaPlayer.callMethod<void>("seekTo", "(I)V", jint(msec));
+}
+
+void AndroidMediaPlayer::setMuted(bool mute)
+{
+ mMediaPlayer.callMethod<void>("mute", "(Z)V", jboolean(mute));
+}
+
+void AndroidMediaPlayer::setDataSource(const QNetworkRequest &request)
+{
+ QJNIObjectPrivate string = QJNIObjectPrivate::fromString(request.url().toString(QUrl::FullyEncoded));
+
+ mMediaPlayer.callMethod<void>("initHeaders", "()V");
+ for (auto &header : request.rawHeaderList()) {
+ auto value = request.rawHeader(header);
+ mMediaPlayer.callMethod<void>("setHeader", "(Ljava/lang/String;Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(header).object(), QJNIObjectPrivate::fromString(value).object());
+ }
+
+ mMediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", string.object());
+}
+
+void AndroidMediaPlayer::prepareAsync()
+{
+ mMediaPlayer.callMethod<void>("prepareAsync");
+}
+
+void AndroidMediaPlayer::setVolume(int volume)
+{
+ mMediaPlayer.callMethod<void>("setVolume", "(I)V", jint(volume));
+}
+
+bool AndroidMediaPlayer::setPlaybackRate(qreal rate)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 23) {
+ qWarning("Setting the playback rate on a media player requires Android 6.0 (API level 23) or later");
+ return false;
+ }
+
+ QJNIEnvironmentPrivate env;
+
+ QJNIObjectPrivate player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", "()Landroid/media/MediaPlayer;");
+ if (player.isValid()) {
+ QJNIObjectPrivate playbackParams = player.callObjectMethod("getPlaybackParams", "()Landroid/media/PlaybackParams;");
+ if (playbackParams.isValid()) {
+ playbackParams.callObjectMethod("setSpeed", "(F)Landroid/media/PlaybackParams;", jfloat(rate));
+ // pitch can only be > 0
+ if (!qFuzzyIsNull(rate))
+ playbackParams.callObjectMethod("setPitch", "(F)Landroid/media/PlaybackParams;", jfloat(qAbs(rate)));
+ player.callMethod<void>("setPlaybackParams", "(Landroid/media/PlaybackParams;)V", playbackParams.object());
+ if (Q_UNLIKELY(env->ExceptionCheck())) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif // QT_DEBUG
+ env->ExceptionClear();
+ qWarning() << "Invalid playback rate" << rate;
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture)
+{
+ mMediaPlayer.callMethod<void>("setDisplay",
+ "(Landroid/view/SurfaceHolder;)V",
+ surfaceTexture ? surfaceTexture->surfaceHolder() : 0);
+}
+
+void AndroidMediaPlayer::setAudioRole(QAudio::Role role)
+{
+ QString str;
+ switch (role) {
+ case QAudio::MusicRole:
+ str = QLatin1String("CONTENT_TYPE_MUSIC");
+ break;
+ case QAudio::VideoRole:
+ str = QLatin1String("CONTENT_TYPE_MOVIE");
+ break;
+ case QAudio::VoiceCommunicationRole:
+ str = QLatin1String("USAGE_VOICE_COMMUNICATION");
+ break;
+ case QAudio::AlarmRole:
+ str = QLatin1String("USAGE_ALARM");
+ break;
+ case QAudio::NotificationRole:
+ str = QLatin1String("USAGE_NOTIFICATION");
+ break;
+ case QAudio::RingtoneRole:
+ str = QLatin1String("USAGE_NOTIFICATION_RINGTONE");
+ break;
+ case QAudio::AccessibilityRole:
+ str = QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY");
+ break;
+ case QAudio::SonificationRole:
+ str = QLatin1String("CONTENT_TYPE_SONIFICATION");
+ break;
+ case QAudio::GameRole:
+ str = QLatin1String("USAGE_GAME");
+ break;
+ default:
+ break;
+ }
+
+ setCustomAudioRole(str);
+}
+
+void AndroidMediaPlayer::setCustomAudioRole(const QString &role)
+{
+ QStringList roles = role.split(",", Qt::SkipEmptyParts);
+
+ int type = 0; // CONTENT_TYPE_UNKNOWN
+ int usage = 0; // USAGE_UNKNOWN
+ for (int i = 0; i < qMin(2, roles.size()); ++i) {
+ auto r = roles[i];
+ if (r == QLatin1String("CONTENT_TYPE_MOVIE"))
+ type = 3;
+ else if (r == QLatin1String("CONTENT_TYPE_MUSIC"))
+ type = 2;
+ else if (r == QLatin1String("CONTENT_TYPE_SONIFICATION"))
+ type = 4;
+ else if (r == QLatin1String("CONTENT_TYPE_SPEECH"))
+ type = 1;
+ else if (r == QLatin1String("USAGE_ALARM"))
+ usage = 4;
+ else if (r == QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY"))
+ usage = 11;
+ else if (r == QLatin1String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"))
+ usage = 12;
+ else if (r == QLatin1String("USAGE_ASSISTANCE_SONIFICATION"))
+ usage = 13;
+ else if (r == QLatin1String("USAGE_ASSISTANT"))
+ usage = 16;
+ else if (r == QLatin1String("USAGE_GAME"))
+ usage = 14;
+ else if (r == QLatin1String("USAGE_MEDIA"))
+ usage = 1;
+ else if (r == QLatin1String("USAGE_NOTIFICATION"))
+ usage = 5;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED"))
+ usage = 9;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT"))
+ usage = 8;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST"))
+ usage = 7;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_EVENT"))
+ usage = 10;
+ else if (r == QLatin1String("USAGE_NOTIFICATION_RINGTONE"))
+ usage = 6;
+ else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION"))
+ usage = 2;
+ else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION_SIGNALLING"))
+ usage = 3;
+ }
+
+ mMediaPlayer.callMethod<void>("setAudioAttributes", "(II)V", jint(type), jint(usage));
+}
+
+static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->error(what, extra);
+}
+
+static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent);
+}
+
+static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->progressChanged(progress);
+}
+
+static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->durationChanged(duration);
+}
+
+static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->info(what, extra);
+}
+
+static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->stateChanged(state);
+}
+
+static void onVideoSizeChangedNative(JNIEnv *env,
+ jobject thiz,
+ jint width,
+ jint height,
+ jlong id)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ QReadLocker locker(rwLock);
+ const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height);
+}
+
+bool AndroidMediaPlayer::initJNI(JNIEnv *env)
+{
+ jclass clazz = QJNIEnvironmentPrivate::findClass(QtAndroidMediaPlayerClassName,
+ env);
+
+ static const JNINativeMethod methods[] = {
+ {"onErrorNative", "(IIJ)V", reinterpret_cast<void *>(onErrorNative)},
+ {"onBufferingUpdateNative", "(IJ)V", reinterpret_cast<void *>(onBufferingUpdateNative)},
+ {"onProgressUpdateNative", "(IJ)V", reinterpret_cast<void *>(onProgressUpdateNative)},
+ {"onDurationChangedNative", "(IJ)V", reinterpret_cast<void *>(onDurationChangedNative)},
+ {"onInfoNative", "(IIJ)V", reinterpret_cast<void *>(onInfoNative)},
+ {"onVideoSizeChangedNative", "(IIJ)V", reinterpret_cast<void *>(onVideoSizeChangedNative)},
+ {"onStateChangedNative", "(IJ)V", reinterpret_cast<void *>(onStateChangedNative)}
+ };
+
+ if (clazz && env->RegisterNatives(clazz,
+ methods,
+ sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
new file mode 100644
index 000000000..bf7a7002b
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDMEDIAPLAYER_H
+#define ANDROIDMEDIAPLAYER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QObject>
+#include <QNetworkRequest>
+#include <QtCore/private/qjni_p.h>
+#include <QAudio>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidSurfaceTexture;
+
+class AndroidMediaPlayer : public QObject
+{
+ Q_OBJECT
+public:
+ AndroidMediaPlayer();
+ ~AndroidMediaPlayer();
+
+ enum MediaError
+ {
+ // What
+ MEDIA_ERROR_UNKNOWN = 1,
+ MEDIA_ERROR_SERVER_DIED = 100,
+ MEDIA_ERROR_INVALID_STATE = -38, // Undocumented
+ // Extra
+ MEDIA_ERROR_IO = -1004,
+ MEDIA_ERROR_MALFORMED = -1007,
+ MEDIA_ERROR_UNSUPPORTED = -1010,
+ MEDIA_ERROR_TIMED_OUT = -110,
+ MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
+ MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN = -2147483648 // Undocumented
+ };
+
+ enum MediaInfo
+ {
+ MEDIA_INFO_UNKNOWN = 1,
+ MEDIA_INFO_VIDEO_TRACK_LAGGING = 700,
+ MEDIA_INFO_VIDEO_RENDERING_START = 3,
+ MEDIA_INFO_BUFFERING_START = 701,
+ MEDIA_INFO_BUFFERING_END = 702,
+ MEDIA_INFO_BAD_INTERLEAVING = 800,
+ MEDIA_INFO_NOT_SEEKABLE = 801,
+ MEDIA_INFO_METADATA_UPDATE = 802
+ };
+
+ enum MediaPlayerState
+ {
+ Uninitialized = 0x1, /* End */
+ Idle = 0x2,
+ Preparing = 0x4,
+ Prepared = 0x8,
+ Initialized = 0x10,
+ Started = 0x20,
+ Stopped = 0x40,
+ Paused = 0x80,
+ PlaybackCompleted = 0x100,
+ Error = 0x200
+ };
+
+ void release();
+ void reset();
+
+ int getCurrentPosition();
+ int getDuration();
+ bool isPlaying();
+ int volume();
+ bool isMuted();
+ qreal playbackRate();
+ jobject display();
+
+ void play();
+ void pause();
+ void stop();
+ void seekTo(qint32 msec);
+ void setMuted(bool mute);
+ void setDataSource(const QNetworkRequest &request);
+ void prepareAsync();
+ void setVolume(int volume);
+ bool setPlaybackRate(qreal rate);
+ void setDisplay(AndroidSurfaceTexture *surfaceTexture);
+ void setAudioRole(QAudio::Role role);
+ void setCustomAudioRole(const QString &role);
+
+ static bool initJNI(JNIEnv *env);
+
+Q_SIGNALS:
+ void error(qint32 what, qint32 extra);
+ void bufferingChanged(qint32 percent);
+ void durationChanged(qint64 duration);
+ void progressChanged(qint64 progress);
+ void stateChanged(qint32 state);
+ void info(qint32 what, qint32 extra);
+ void videoSizeChanged(qint32 width, qint32 height);
+
+private:
+ QJNIObjectPrivate mMediaPlayer;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMEDIAPLAYER_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp
new file mode 100644
index 000000000..97bbd3b6a
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp
@@ -0,0 +1,405 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidmediarecorder_p.h"
+
+#include "androidcamera_p.h"
+#include "androidsurfacetexture_p.h"
+#include "androidsurfaceview_p.h"
+#include "qandroidglobal_p.h"
+#include "qandroidmultimediautils_p.h"
+#include <QtCore/private/qjni_p.h>
+#include <qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QMap<QString, QJNIObjectPrivate> CamcorderProfiles;
+Q_GLOBAL_STATIC(CamcorderProfiles, g_camcorderProfiles)
+
+static QString profileKey()
+{
+ return QStringLiteral("%1-%2");
+}
+
+bool AndroidCamcorderProfile::hasProfile(jint cameraId, Quality quality)
+{
+ if (g_camcorderProfiles->contains(profileKey().arg(cameraId).arg(quality)))
+ return true;
+
+ return QJNIObjectPrivate::callStaticMethod<jboolean>("android/media/CamcorderProfile",
+ "hasProfile",
+ "(II)Z",
+ cameraId,
+ quality);
+}
+
+AndroidCamcorderProfile AndroidCamcorderProfile::get(jint cameraId, Quality quality)
+{
+ const QString key = profileKey().arg(cameraId).arg(quality);
+ QMap<QString, QJNIObjectPrivate>::const_iterator it = g_camcorderProfiles->constFind(key);
+
+ if (it != g_camcorderProfiles->constEnd())
+ return AndroidCamcorderProfile(*it);
+
+ QJNIObjectPrivate camProfile = QJNIObjectPrivate::callStaticObjectMethod("android/media/CamcorderProfile",
+ "get",
+ "(II)Landroid/media/CamcorderProfile;",
+ cameraId,
+ quality);
+
+ return AndroidCamcorderProfile((*g_camcorderProfiles)[key] = camProfile);
+}
+
+int AndroidCamcorderProfile::getValue(AndroidCamcorderProfile::Field field) const
+{
+ switch (field) {
+ case audioBitRate:
+ return m_camcorderProfile.getField<jint>("audioBitRate");
+ case audioChannels:
+ return m_camcorderProfile.getField<jint>("audioChannels");
+ case audioCodec:
+ return m_camcorderProfile.getField<jint>("audioCodec");
+ case audioSampleRate:
+ return m_camcorderProfile.getField<jint>("audioSampleRate");
+ case duration:
+ return m_camcorderProfile.getField<jint>("duration");
+ case fileFormat:
+ return m_camcorderProfile.getField<jint>("fileFormat");
+ case quality:
+ return m_camcorderProfile.getField<jint>("quality");
+ case videoBitRate:
+ return m_camcorderProfile.getField<jint>("videoBitRate");
+ case videoCodec:
+ return m_camcorderProfile.getField<jint>("videoCodec");
+ case videoFrameHeight:
+ return m_camcorderProfile.getField<jint>("videoFrameHeight");
+ case videoFrameRate:
+ return m_camcorderProfile.getField<jint>("videoFrameRate");
+ case videoFrameWidth:
+ return m_camcorderProfile.getField<jint>("videoFrameWidth");
+ }
+
+ return 0;
+}
+
+AndroidCamcorderProfile::AndroidCamcorderProfile(const QJNIObjectPrivate &camcorderProfile)
+{
+ m_camcorderProfile = camcorderProfile;
+}
+
+static const char QtMediaRecorderListenerClassName[] = "org/qtproject/qt/android/multimedia/QtMediaRecorderListener";
+typedef QMap<jlong, AndroidMediaRecorder*> MediaRecorderMap;
+Q_GLOBAL_STATIC(MediaRecorderMap, mediaRecorders)
+
+static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra)
+{
+ AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
+ if (obj)
+ emit obj->error(what, extra);
+}
+
+static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra)
+{
+ AndroidMediaRecorder *obj = mediaRecorders->value(id, 0);
+ if (obj)
+ emit obj->info(what, extra);
+}
+
+AndroidMediaRecorder::AndroidMediaRecorder()
+ : QObject()
+ , m_id(reinterpret_cast<jlong>(this))
+{
+ m_mediaRecorder = QJNIObjectPrivate("android/media/MediaRecorder");
+ if (m_mediaRecorder.isValid()) {
+ QJNIObjectPrivate listener(QtMediaRecorderListenerClassName, "(J)V", m_id);
+ m_mediaRecorder.callMethod<void>("setOnErrorListener",
+ "(Landroid/media/MediaRecorder$OnErrorListener;)V",
+ listener.object());
+ m_mediaRecorder.callMethod<void>("setOnInfoListener",
+ "(Landroid/media/MediaRecorder$OnInfoListener;)V",
+ listener.object());
+ mediaRecorders->insert(m_id, this);
+ }
+}
+
+AndroidMediaRecorder::~AndroidMediaRecorder()
+{
+ mediaRecorders->remove(m_id);
+}
+
+void AndroidMediaRecorder::release()
+{
+ m_mediaRecorder.callMethod<void>("release");
+}
+
+bool AndroidMediaRecorder::prepare()
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("prepare");
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ return false;
+ }
+ return true;
+}
+
+void AndroidMediaRecorder::reset()
+{
+ m_mediaRecorder.callMethod<void>("reset");
+}
+
+bool AndroidMediaRecorder::start()
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("start");
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ return false;
+ }
+ return true;
+}
+
+void AndroidMediaRecorder::stop()
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("stop");
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setAudioChannels(int numChannels)
+{
+ m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels);
+}
+
+void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setAudioEncodingBitRate(int bitRate)
+{
+ m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate);
+}
+
+void AndroidMediaRecorder::setAudioSamplingRate(int samplingRate)
+{
+ m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate);
+}
+
+void AndroidMediaRecorder::setAudioSource(AudioSource source)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setAudioSource", "(I)V", int(source));
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setCamera(AndroidCamera *camera)
+{
+ QJNIObjectPrivate cam = camera->getCameraObject();
+ m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object());
+}
+
+void AndroidMediaRecorder::setVideoEncoder(VideoEncoder encoder)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder));
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setVideoEncodingBitRate(int bitRate)
+{
+ m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate);
+}
+
+void AndroidMediaRecorder::setVideoFrameRate(int rate)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setVideoSize(const QSize &size)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setVideoSource(VideoSource source)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setVideoSource", "(I)V", int(source));
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setOrientationHint(int degrees)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setOutputFormat(OutputFormat format)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setOutputFormat", "(I)V", int(format));
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setOutputFile(const QString &path)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setOutputFile",
+ "(Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(path).object());
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture)
+{
+ QJNIEnvironmentPrivate env;
+ m_mediaRecorder.callMethod<void>("setPreviewDisplay",
+ "(Landroid/view/Surface;)V",
+ texture->surface());
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder)
+{
+ QJNIEnvironmentPrivate env;
+ QJNIObjectPrivate surfaceHolder(holder->surfaceHolder());
+ QJNIObjectPrivate surface = surfaceHolder.callObjectMethod("getSurface",
+ "()Landroid/view/Surface;");
+ if (!surface.isValid())
+ return;
+
+ m_mediaRecorder.callMethod<void>("setPreviewDisplay",
+ "(Landroid/view/Surface;)V",
+ surface.object());
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif
+ env->ExceptionClear();
+ }
+}
+
+bool AndroidMediaRecorder::initJNI(JNIEnv *env)
+{
+ jclass clazz = QJNIEnvironmentPrivate::findClass(QtMediaRecorderListenerClassName,
+ env);
+
+ static const JNINativeMethod methods[] = {
+ {"notifyError", "(JII)V", (void *)notifyError},
+ {"notifyInfo", "(JII)V", (void *)notifyInfo}
+ };
+
+ if (clazz && env->RegisterNatives(clazz,
+ methods,
+ sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h
new file mode 100644
index 000000000..9cba14f75
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDMEDIARECORDER_H
+#define ANDROIDMEDIARECORDER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <QtCore/private/qjni_p.h>
+#include <qsize.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidCamera;
+class AndroidSurfaceTexture;
+class AndroidSurfaceHolder;
+
+class AndroidCamcorderProfile
+{
+public:
+ enum Quality { // Needs to match CamcorderProfile
+ QUALITY_LOW,
+ QUALITY_HIGH,
+ QUALITY_QCIF,
+ QUALITY_CIF,
+ QUALITY_480P,
+ QUALITY_720P,
+ QUALITY_1080P,
+ QUALITY_QVGA
+ };
+
+ enum Field {
+ audioBitRate,
+ audioChannels,
+ audioCodec,
+ audioSampleRate,
+ duration,
+ fileFormat,
+ quality,
+ videoBitRate,
+ videoCodec,
+ videoFrameHeight,
+ videoFrameRate,
+ videoFrameWidth
+ };
+
+ static bool hasProfile(jint cameraId, Quality quality);
+ static AndroidCamcorderProfile get(jint cameraId, Quality quality);
+ int getValue(Field field) const;
+
+private:
+ AndroidCamcorderProfile(const QJNIObjectPrivate &camcorderProfile);
+ QJNIObjectPrivate m_camcorderProfile;
+};
+
+class AndroidMediaRecorder : public QObject
+{
+ Q_OBJECT
+public:
+ enum AudioEncoder {
+ DefaultAudioEncoder = 0,
+ AMR_NB_Encoder = 1,
+ AMR_WB_Encoder = 2,
+ AAC = 3
+ };
+
+ enum AudioSource {
+ DefaultAudioSource = 0,
+ Mic = 1,
+ VoiceUplink = 2,
+ VoiceDownlink = 3,
+ VoiceCall = 4,
+ Camcorder = 5,
+ VoiceRecognition = 6
+ };
+
+ enum VideoEncoder {
+ DefaultVideoEncoder = 0,
+ H263 = 1,
+ H264 = 2,
+ MPEG_4_SP = 3
+ };
+
+ enum VideoSource {
+ DefaultVideoSource = 0,
+ Camera = 1
+ };
+
+ enum OutputFormat {
+ DefaultOutputFormat = 0,
+ THREE_GPP = 1,
+ MPEG_4 = 2,
+ AMR_NB_Format = 3,
+ AMR_WB_Format = 4
+ };
+
+ AndroidMediaRecorder();
+ ~AndroidMediaRecorder();
+
+ void release();
+ bool prepare();
+ void reset();
+
+ bool start();
+ void stop();
+
+ void setAudioChannels(int numChannels);
+ void setAudioEncoder(AudioEncoder encoder);
+ void setAudioEncodingBitRate(int bitRate);
+ void setAudioSamplingRate(int samplingRate);
+ void setAudioSource(AudioSource source);
+
+ void setCamera(AndroidCamera *camera);
+ void setVideoEncoder(VideoEncoder encoder);
+ void setVideoEncodingBitRate(int bitRate);
+ void setVideoFrameRate(int rate);
+ void setVideoSize(const QSize &size);
+ void setVideoSource(VideoSource source);
+
+ void setOrientationHint(int degrees);
+
+ void setOutputFormat(OutputFormat format);
+ void setOutputFile(const QString &path);
+
+ void setSurfaceTexture(AndroidSurfaceTexture *texture);
+ void setSurfaceHolder(AndroidSurfaceHolder *holder);
+
+ static bool initJNI(JNIEnv *env);
+
+Q_SIGNALS:
+ void error(int what, int extra);
+ void info(int what, int extra);
+
+private:
+ jlong m_id;
+ QJNIObjectPrivate m_mediaRecorder;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMEDIARECORDER_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp
new file mode 100644
index 000000000..387cb1721
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidmultimediautils_p.h"
+
+#include <QtCore/private/qjni_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+void AndroidMultimediaUtils::enableOrientationListener(bool enable)
+{
+ QJNIObjectPrivate::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "enableOrientationListener",
+ "(Z)V",
+ enable);
+}
+
+int AndroidMultimediaUtils::getDeviceOrientation()
+{
+ return QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getDeviceOrientation");
+}
+
+QString AndroidMultimediaUtils::getDefaultMediaDirectory(MediaType type)
+{
+ QJNIObjectPrivate path = QJNIObjectPrivate::callStaticObjectMethod("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "getDefaultMediaDirectory",
+ "(I)Ljava/lang/String;",
+ jint(type));
+ return path.toString();
+}
+
+void AndroidMultimediaUtils::registerMediaFile(const QString &file)
+{
+ QJNIObjectPrivate::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils",
+ "registerMediaFile",
+ "(Ljava/lang/String;)V",
+ QJNIObjectPrivate::fromString(file).object());
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h
new file mode 100644
index 000000000..cb80c5c1e
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDMULTIMEDIAUTILS_H
+#define ANDROIDMULTIMEDIAUTILS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <QtCore/private/qjni_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidMultimediaUtils
+{
+public:
+ enum MediaType {
+ Music = 0,
+ Movies = 1,
+ DCIM = 2,
+ Sounds = 3
+ };
+
+ static void enableOrientationListener(bool enable);
+ static int getDeviceOrientation();
+ static QString getDefaultMediaDirectory(MediaType type);
+ static void registerMediaFile(const QString &file);
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDMULTIMEDIAUTILS_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp
new file mode 100644
index 000000000..8f9be7c3b
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidsurfacetexture_p.h"
+#include <QtCore/private/qjni_p.h>
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qmutex.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char QtSurfaceTextureListenerClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceTextureListener";
+typedef QList<jlong> SurfaceTextures;
+Q_GLOBAL_STATIC(SurfaceTextures, g_surfaceTextures);
+Q_GLOBAL_STATIC(QMutex, g_textureMutex);
+
+// native method for QtSurfaceTexture.java
+static void notifyFrameAvailable(JNIEnv* , jobject, jlong id)
+{
+ const QMutexLocker lock(g_textureMutex());
+ const int idx = g_surfaceTextures->indexOf(id);
+ if (idx == -1)
+ return;
+
+ AndroidSurfaceTexture *obj = reinterpret_cast<AndroidSurfaceTexture *>(g_surfaceTextures->at(idx));
+ if (obj)
+ Q_EMIT obj->frameAvailable();
+}
+
+AndroidSurfaceTexture::AndroidSurfaceTexture(quint32 texName)
+ : QObject()
+{
+ Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *));
+ // API level 11 or higher is required
+ if (QtAndroidPrivate::androidSdkVersion() < 11) {
+ qWarning("Camera preview and video playback require Android 3.0 (API level 11) or later.");
+ return;
+ }
+
+ QJNIEnvironmentPrivate env;
+ m_surfaceTexture = QJNIObjectPrivate("android/graphics/SurfaceTexture", "(I)V", jint(texName));
+ if (env->ExceptionCheck()) {
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif // QT_DEBUG
+ env->ExceptionClear();
+ }
+
+ if (!m_surfaceTexture.isValid())
+ return;
+
+ const QMutexLocker lock(g_textureMutex());
+ g_surfaceTextures->append(jlong(this));
+ QJNIObjectPrivate listener(QtSurfaceTextureListenerClassName, "(J)V", jlong(this));
+ setOnFrameAvailableListener(listener);
+}
+
+AndroidSurfaceTexture::~AndroidSurfaceTexture()
+{
+ if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surface.isValid())
+ m_surface.callMethod<void>("release");
+
+ if (m_surfaceTexture.isValid()) {
+ release();
+ const QMutexLocker lock(g_textureMutex());
+ const int idx = g_surfaceTextures->indexOf(jlong(this));
+ if (idx != -1)
+ g_surfaceTextures->remove(idx);
+ }
+}
+
+QMatrix4x4 AndroidSurfaceTexture::getTransformMatrix()
+{
+ QMatrix4x4 matrix;
+ if (!m_surfaceTexture.isValid())
+ return matrix;
+
+ QJNIEnvironmentPrivate env;
+
+ jfloatArray array = env->NewFloatArray(16);
+ m_surfaceTexture.callMethod<void>("getTransformMatrix", "([F)V", array);
+ env->GetFloatArrayRegion(array, 0, 16, matrix.data());
+ env->DeleteLocalRef(array);
+
+ return matrix;
+}
+
+void AndroidSurfaceTexture::release()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 14)
+ return;
+
+ m_surfaceTexture.callMethod<void>("release");
+}
+
+void AndroidSurfaceTexture::updateTexImage()
+{
+ if (!m_surfaceTexture.isValid())
+ return;
+
+ m_surfaceTexture.callMethod<void>("updateTexImage");
+}
+
+jobject AndroidSurfaceTexture::surfaceTexture()
+{
+ return m_surfaceTexture.object();
+}
+
+jobject AndroidSurfaceTexture::surface()
+{
+ if (!m_surface.isValid()) {
+ m_surface = QJNIObjectPrivate("android/view/Surface",
+ "(Landroid/graphics/SurfaceTexture;)V",
+ m_surfaceTexture.object());
+ }
+
+ return m_surface.object();
+}
+
+jobject AndroidSurfaceTexture::surfaceHolder()
+{
+ if (!m_surfaceHolder.isValid()) {
+ m_surfaceHolder = QJNIObjectPrivate("org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder",
+ "(Landroid/view/Surface;)V",
+ surface());
+ }
+
+ return m_surfaceHolder.object();
+}
+
+void AndroidSurfaceTexture::attachToGLContext(quint32 texName)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
+ return;
+
+ m_surfaceTexture.callMethod<void>("attachToGLContext", "(I)V", texName);
+}
+
+void AndroidSurfaceTexture::detachFromGLContext()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
+ return;
+
+ m_surfaceTexture.callMethod<void>("detachFromGLContext");
+}
+
+bool AndroidSurfaceTexture::initJNI(JNIEnv *env)
+{
+ // SurfaceTexture is available since API 11.
+ if (QtAndroidPrivate::androidSdkVersion() < 11)
+ return false;
+
+ jclass clazz = QJNIEnvironmentPrivate::findClass(QtSurfaceTextureListenerClassName,
+ env);
+
+ static const JNINativeMethod methods[] = {
+ {"notifyFrameAvailable", "(J)V", (void *)notifyFrameAvailable}
+ };
+
+ if (clazz && env->RegisterNatives(clazz,
+ methods,
+ sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
+ return false;
+ }
+
+ return true;
+}
+
+void AndroidSurfaceTexture::setOnFrameAvailableListener(const QJNIObjectPrivate &listener)
+{
+ m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener",
+ "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V",
+ listener.object());
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h
new file mode 100644
index 000000000..5404bb7a7
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDSURFACETEXTURE_H
+#define ANDROIDSURFACETEXTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qobject.h>
+#include <QtCore/private/qjni_p.h>
+
+#include <QMatrix4x4>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidSurfaceTexture : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AndroidSurfaceTexture(quint32 texName);
+ ~AndroidSurfaceTexture();
+
+ jobject surfaceTexture();
+ jobject surface();
+ jobject surfaceHolder();
+ inline bool isValid() const { return m_surfaceTexture.isValid(); }
+
+ QMatrix4x4 getTransformMatrix();
+ void release(); // API level 14
+ void updateTexImage();
+
+ void attachToGLContext(quint32 texName); // API level 16
+ void detachFromGLContext(); // API level 16
+
+ static bool initJNI(JNIEnv *env);
+
+Q_SIGNALS:
+ void frameAvailable();
+
+private:
+ void setOnFrameAvailableListener(const QJNIObjectPrivate &listener);
+
+ QJNIObjectPrivate m_surfaceTexture;
+ QJNIObjectPrivate m_surface;
+ QJNIObjectPrivate m_surfaceHolder;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDSURFACETEXTURE_H
diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp
new file mode 100644
index 000000000..cbe17a6f9
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "androidsurfaceview_p.h"
+
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtGui/qwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback";
+typedef QList<AndroidSurfaceHolder *> SurfaceHolders;
+Q_GLOBAL_STATIC(SurfaceHolders, surfaceHolders)
+Q_GLOBAL_STATIC(QMutex, shLock)
+
+AndroidSurfaceHolder::AndroidSurfaceHolder(QJNIObjectPrivate object)
+ : m_surfaceHolder(object)
+ , m_surfaceCreated(false)
+{
+ if (!m_surfaceHolder.isValid())
+ return;
+
+ {
+ QMutexLocker locker(shLock());
+ surfaceHolders->append(this);
+ }
+
+ QJNIObjectPrivate callback(QtSurfaceHolderCallbackClassName, "(J)V", reinterpret_cast<jlong>(this));
+ m_surfaceHolder.callMethod<void>("addCallback",
+ "(Landroid/view/SurfaceHolder$Callback;)V",
+ callback.object());
+}
+
+AndroidSurfaceHolder::~AndroidSurfaceHolder()
+{
+ QMutexLocker locker(shLock());
+ const int i = surfaceHolders->indexOf(this);
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ surfaceHolders->remove(i);
+}
+
+jobject AndroidSurfaceHolder::surfaceHolder() const
+{
+ return m_surfaceHolder.object();
+}
+
+bool AndroidSurfaceHolder::isSurfaceCreated() const
+{
+ QMutexLocker locker(shLock());
+ return m_surfaceCreated;
+}
+
+void AndroidSurfaceHolder::handleSurfaceCreated(JNIEnv*, jobject, jlong id)
+{
+ QMutexLocker locker(shLock());
+ const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ (*surfaceHolders)[i]->m_surfaceCreated = true;
+ Q_EMIT (*surfaceHolders)[i]->surfaceCreated();
+}
+
+void AndroidSurfaceHolder::handleSurfaceDestroyed(JNIEnv*, jobject, jlong id)
+{
+ QMutexLocker locker(shLock());
+ const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id));
+ if (Q_UNLIKELY(i == -1))
+ return;
+
+ (*surfaceHolders)[i]->m_surfaceCreated = false;
+}
+
+bool AndroidSurfaceHolder::initJNI(JNIEnv *env)
+{
+ jclass clazz = QJNIEnvironmentPrivate::findClass(QtSurfaceHolderCallbackClassName,
+ env);
+
+ static const JNINativeMethod methods[] = {
+ {"notifySurfaceCreated", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceCreated},
+ {"notifySurfaceDestroyed", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceDestroyed}
+ };
+
+ if (clazz && env->RegisterNatives(clazz,
+ methods,
+ sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
+ return false;
+ }
+
+ return true;
+}
+
+AndroidSurfaceView::AndroidSurfaceView()
+ : m_window(0)
+ , m_surfaceHolder(0)
+ , m_pendingVisible(-1)
+{
+ QtAndroidPrivate::runOnAndroidThreadSync([this] {
+ m_surfaceView = QJNIObjectPrivate("android/view/SurfaceView",
+ "(Landroid/content/Context;)V",
+ QtAndroidPrivate::activity());
+ }, QJNIEnvironmentPrivate());
+
+ Q_ASSERT(m_surfaceView.isValid());
+
+ QJNIObjectPrivate holder = m_surfaceView.callObjectMethod("getHolder",
+ "()Landroid/view/SurfaceHolder;");
+ if (!holder.isValid()) {
+ m_surfaceView = QJNIObjectPrivate();
+ } else {
+ m_surfaceHolder = new AndroidSurfaceHolder(holder);
+ connect(m_surfaceHolder, &AndroidSurfaceHolder::surfaceCreated,
+ this, &AndroidSurfaceView::surfaceCreated);
+ { // Lock now to avoid a race with handleSurfaceCreated()
+ QMutexLocker locker(shLock());
+ m_window = QWindow::fromWinId(WId(m_surfaceView.object()));
+
+ if (m_pendingVisible != -1)
+ m_window->setVisible(m_pendingVisible);
+ if (m_pendingGeometry.isValid())
+ m_window->setGeometry(m_pendingGeometry);
+ }
+ }
+}
+
+AndroidSurfaceView::~AndroidSurfaceView()
+{
+ delete m_surfaceHolder;
+ delete m_window;
+}
+
+AndroidSurfaceHolder *AndroidSurfaceView::holder() const
+{
+ return m_surfaceHolder;
+}
+
+void AndroidSurfaceView::setVisible(bool v)
+{
+ if (m_window)
+ m_window->setVisible(v);
+ else
+ m_pendingVisible = int(v);
+}
+
+void AndroidSurfaceView::setGeometry(int x, int y, int width, int height)
+{
+ if (m_window)
+ m_window->setGeometry(x, y, width, height);
+ else
+ m_pendingGeometry = QRect(x, y, width, height);
+}
+
+QT_END_NAMESPACE
diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h
new file mode 100644
index 000000000..7d89df09b
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ANDROIDSURFACEVIEW_H
+#define ANDROIDSURFACEVIEW_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qjni_p.h>
+#include <qrect.h>
+#include <QtCore/qrunnable.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+
+class AndroidSurfaceHolder : public QObject
+{
+ Q_OBJECT
+public:
+ ~AndroidSurfaceHolder();
+
+ jobject surfaceHolder() const;
+ bool isSurfaceCreated() const;
+
+ static bool initJNI(JNIEnv *env);
+
+Q_SIGNALS:
+ void surfaceCreated();
+
+private:
+ AndroidSurfaceHolder(QJNIObjectPrivate object);
+
+ static void handleSurfaceCreated(JNIEnv*, jobject, jlong id);
+ static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id);
+
+ QJNIObjectPrivate m_surfaceHolder;
+ bool m_surfaceCreated;
+
+ friend class AndroidSurfaceView;
+};
+
+class AndroidSurfaceView : public QObject
+{
+ Q_OBJECT
+public:
+ AndroidSurfaceView();
+ ~AndroidSurfaceView();
+
+ AndroidSurfaceHolder *holder() const;
+
+ void setVisible(bool v);
+ void setGeometry(int x, int y, int width, int height);
+
+Q_SIGNALS:
+ void surfaceCreated();
+
+private:
+ QJNIObjectPrivate m_surfaceView;
+ QWindow *m_window;
+ AndroidSurfaceHolder *m_surfaceHolder;
+ int m_pendingVisible;
+ QRect m_pendingGeometry;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDSURFACEVIEW_H
diff --git a/src/multimedia/platform/android/wrappers/jni/jni.pri b/src/multimedia/platform/android/wrappers/jni/jni.pri
new file mode 100644
index 000000000..14bc78573
--- /dev/null
+++ b/src/multimedia/platform/android/wrappers/jni/jni.pri
@@ -0,0 +1,21 @@
+QT += core-private
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/androidmediaplayer_p.h \
+ $$PWD/androidsurfacetexture_p.h \
+ $$PWD/androidmediametadataretriever_p.h \
+ $$PWD/androidcamera_p.h \
+ $$PWD/androidmultimediautils_p.h \
+ $$PWD/androidmediarecorder_p.h \
+ $$PWD/androidsurfaceview_p.h
+
+SOURCES += \
+ $$PWD/androidmediaplayer.cpp \
+ $$PWD/androidsurfacetexture.cpp \
+ $$PWD/androidmediametadataretriever.cpp \
+ $$PWD/androidcamera.cpp \
+ $$PWD/androidmultimediautils.cpp \
+ $$PWD/androidmediarecorder.cpp \
+ $$PWD/androidsurfaceview.cpp