diff options
Diffstat (limited to 'src/plugins/android/src/wrappers/jni')
5 files changed, 396 insertions, 13 deletions
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp index 1fe0e666e..083803e38 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp +++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp @@ -33,6 +33,7 @@ #include "androidcamera.h" #include "androidsurfacetexture.h" +#include "androidsurfaceview.h" #include "qandroidmultimediautils.h" #include <qstringlist.h> @@ -41,6 +42,7 @@ #include <QtCore/qthread.h> #include <QtCore/qreadwritelock.h> #include <QtCore/qmutex.h> +#include <QtMultimedia/private/qmemoryvideobuffer_p.h> QT_BEGIN_NAMESPACE @@ -121,7 +123,8 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) Q_EMIT (*it)->pictureCaptured(bytes); } -static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, int width, int height) +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); @@ -129,10 +132,17 @@ static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, return; const int arrayLength = env->GetArrayLength(data); + if (arrayLength == 0) + return; + QByteArray bytes(arrayLength, Qt::Uninitialized); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); - Q_EMIT (*it)->newPreviewFrame(bytes, width, height); + QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), + QSize(width, height), + qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format))); + + Q_EMIT (*it)->newPreviewFrame(frame); } class AndroidCameraPrivate : public QObject @@ -157,10 +167,12 @@ public: 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 void updatePreviewSize(); Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture); + Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder); Q_INVOKABLE bool isZoomSupported(); Q_INVOKABLE int getMaxZoom(); @@ -238,7 +250,7 @@ Q_SIGNALS: void whiteBalanceChanged(); - void lastPreviewFrameFetched(const QByteArray &preview, int width, int height); + void lastPreviewFrameFetched(const QVideoFrame &frame); }; AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker) @@ -250,6 +262,7 @@ AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *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); @@ -368,6 +381,12 @@ void AndroidCamera::setPreviewFormat(ImageFormat fmt) 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); @@ -399,6 +418,18 @@ bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture) 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; +} + bool AndroidCamera::isZoomSupported() { Q_D(AndroidCamera); @@ -834,7 +865,7 @@ AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat() QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) - return AndroidCamera::Unknown; + return AndroidCamera::UnknownImageFormat; return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat")); } @@ -850,6 +881,27 @@ void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt) applyParameters(); } +QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats() +{ + QList<AndroidCamera::ImageFormat> list; + + QMutexLocker parametersLocker(&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; +} + void AndroidCameraPrivate::updatePreviewSize() { QMutexLocker parametersLocker(&m_parametersMutex); @@ -871,6 +923,15 @@ bool AndroidCameraPrivate::setPreviewTexture(void *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); +} + bool AndroidCameraPrivate::isZoomSupported() { QMutexLocker parametersLocker(&m_parametersMutex); @@ -1361,15 +1422,25 @@ void AndroidCameraPrivate::fetchLastPreviewFrame() 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())); - emit lastPreviewFrameFetched(bytes, - m_cameraListener.callMethod<jint>("previewWidth"), - m_cameraListener.callMethod<jint>("previewHeight")); + 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() @@ -1414,7 +1485,7 @@ bool AndroidCamera::initJNI(JNIEnv *env) {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured}, - {"notifyNewPreviewFrame", "(I[BII)V", (void *)notifyNewPreviewFrame} + {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame} }; if (clazz && env->RegisterNatives(clazz, diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.h b/src/plugins/android/src/wrappers/jni/androidcamera.h index 7a8ae8b23..9e4c387c4 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.h +++ b/src/plugins/android/src/wrappers/jni/androidcamera.h @@ -46,6 +46,7 @@ class QThread; class AndroidCameraPrivate; class AndroidSurfaceTexture; +class AndroidSurfaceHolder; struct AndroidCameraInfo { @@ -67,7 +68,7 @@ public: }; enum ImageFormat { // same values as in android.graphics.ImageFormat Java class - Unknown = 0, + UnknownImageFormat = 0, RGB565 = 4, NV16 = 16, NV21 = 17, @@ -95,10 +96,12 @@ public: ImageFormat getPreviewFormat(); void setPreviewFormat(ImageFormat fmt); + QList<ImageFormat> getSupportedPreviewFormats(); QSize previewSize() const; void setPreviewSize(const QSize &size); bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture); + bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder); bool isZoomSupported(); int getMaxZoom(); @@ -177,8 +180,8 @@ Q_SIGNALS: void pictureExposed(); void pictureCaptured(const QByteArray &data); - void lastPreviewFrameFetched(const QByteArray &preview, int width, int height); - void newPreviewFrame(const QByteArray &frame, int width, int height); + void lastPreviewFrameFetched(const QVideoFrame &frame); + void newPreviewFrame(const QVideoFrame &frame); private: AndroidCamera(AndroidCameraPrivate *d, QThread *worker); @@ -188,6 +191,8 @@ private: QScopedPointer<QThread> m_worker; }; +Q_DECLARE_METATYPE(AndroidCamera::ImageFormat) + QT_END_NAMESPACE #endif // ANDROIDCAMERA_H diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp new file mode 100644 index 000000000..67560baf4 --- /dev/null +++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "androidsurfaceview.h" + +#include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qvector.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmutex.h> +#include <QtGui/qwindow.h> + +QT_BEGIN_NAMESPACE + +static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback"; +typedef QVector<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) +{ + setAutoDelete(false); + QtAndroidPrivate::runOnUiThread(this, QJNIEnvironmentPrivate()); +} + +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); +} + +bool AndroidSurfaceView::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + 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); + } + } + + return true; + } + + return QObject::event(e); +} + +// Called on the Android UI thread. +void AndroidSurfaceView::run() +{ + m_surfaceView = QJNIObjectPrivate("android/view/SurfaceView", + "(Landroid/content/Context;)V", + QtAndroidPrivate::activity()); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.h b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h new file mode 100644 index 000000000..661a5959f --- /dev/null +++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ANDROIDSURFACEVIEW_H +#define ANDROIDSURFACEVIEW_H + +#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, public QRunnable +{ + Q_OBJECT +public: + AndroidSurfaceView(); + ~AndroidSurfaceView(); + + AndroidSurfaceHolder *holder() const; + + void setVisible(bool v); + void setGeometry(int x, int y, int width, int height); + + bool event(QEvent *); + +Q_SIGNALS: + void surfaceCreated(); + +protected: + void run() override; + +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/plugins/android/src/wrappers/jni/jni.pri b/src/plugins/android/src/wrappers/jni/jni.pri index e96baff1c..930d7e922 100644 --- a/src/plugins/android/src/wrappers/jni/jni.pri +++ b/src/plugins/android/src/wrappers/jni/jni.pri @@ -8,7 +8,8 @@ HEADERS += \ $$PWD/androidmediametadataretriever.h \ $$PWD/androidcamera.h \ $$PWD/androidmultimediautils.h \ - $$PWD/androidmediarecorder.h + $$PWD/androidmediarecorder.h \ + $$PWD/androidsurfaceview.h SOURCES += \ $$PWD/androidmediaplayer.cpp \ @@ -16,4 +17,5 @@ SOURCES += \ $$PWD/androidmediametadataretriever.cpp \ $$PWD/androidcamera.cpp \ $$PWD/androidmultimediautils.cpp \ - $$PWD/androidmediarecorder.cpp + $$PWD/androidmediarecorder.cpp \ + $$PWD/androidsurfaceview.cpp |