/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "jcamera.h" #include #include #include #include "qandroidmultimediautils.h" #include #include QT_BEGIN_NAMESPACE static jclass g_qtCameraClass = 0; static QMap g_objectMap; static QMutex g_objectMapMutex; static QRect areaToRect(jobject areaObj) { QJNIObjectPrivate area(areaObj); QJNIObjectPrivate rect = area.getObjectField("rect", "Landroid/graphics/Rect;"); return QRect(rect.getField("left"), rect.getField("top"), rect.callMethod("width"), rect.callMethod("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 QtCamera.java static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success) { g_objectMapMutex.lock(); JCamera *obj = g_objectMap.value(id, 0); g_objectMapMutex.unlock(); if (obj) Q_EMIT obj->autoFocusComplete(success); } static void notifyPictureExposed(JNIEnv* , jobject, int id) { g_objectMapMutex.lock(); JCamera *obj = g_objectMap.value(id, 0); g_objectMapMutex.unlock(); if (obj) Q_EMIT obj->pictureExposed(); } static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) { g_objectMapMutex.lock(); JCamera *obj = g_objectMap.value(id, 0); g_objectMapMutex.unlock(); if (obj) { QByteArray bytes; int arrayLength = env->GetArrayLength(data); bytes.resize(arrayLength); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); Q_EMIT obj->pictureCaptured(bytes); } } static void notifyFrameFetched(JNIEnv *env, jobject, int id, jbyteArray data) { g_objectMapMutex.lock(); JCamera *obj = g_objectMap.value(id, 0); g_objectMapMutex.unlock(); if (obj) { QByteArray bytes; int arrayLength = env->GetArrayLength(data); bytes.resize(arrayLength); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); Q_EMIT obj->frameFetched(bytes); } } class JCameraInstantiator : public QObject { Q_OBJECT public: JCameraInstantiator() : QObject(0) {} QJNIObjectPrivate result() {return lastCamera;} public slots: void openCamera(int cameraId) { QJNIEnvironmentPrivate env; lastCamera = QJNIObjectPrivate::callStaticObjectMethod(g_qtCameraClass, "open", "(I)Lorg/qtproject/qt5/android/multimedia/QtCamera;", cameraId); } private: QJNIObjectPrivate lastCamera; }; class JCameraWorker : public QObject, public QJNIObjectPrivate { Q_OBJECT friend class JCamera; JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread *workerThread); Q_INVOKABLE void release(); Q_INVOKABLE JCamera::CameraFacing getFacing(); Q_INVOKABLE int getNativeOrientation(); Q_INVOKABLE void setDisplayOrientation(int degrees); Q_INVOKABLE QSize getPreferredPreviewSizeForVideo(); Q_INVOKABLE QList getSupportedPreviewSizes(); Q_INVOKABLE JCamera::ImageFormat getPreviewFormat(); Q_INVOKABLE void setPreviewFormat(JCamera::ImageFormat fmt); Q_INVOKABLE QSize previewSize() const { return m_previewSize; } Q_INVOKABLE void updatePreviewSize(); Q_INVOKABLE void setPreviewTexture(void *surfaceTexture); Q_INVOKABLE bool isZoomSupported(); Q_INVOKABLE int getMaxZoom(); Q_INVOKABLE QList 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 getFocusAreas(); Q_INVOKABLE void setFocusAreas(const QList &areas); Q_INVOKABLE void autoFocus(); 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 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 fetchEachFrame(bool fetch); Q_INVOKABLE void fetchLastPreviewFrame(); Q_INVOKABLE void applyParameters(); Q_INVOKABLE QStringList callParametersStringListMethod(const QByteArray &methodName); Q_INVOKABLE void callVoidMethod(const QByteArray &methodName); int m_cameraId; QJNIObjectPrivate m_info; QJNIObjectPrivate m_parameters; QSize m_previewSize; int m_rotation; bool m_hasAPI14; JCamera *q; QThread *m_workerThread; QMutex m_parametersMutex; Q_SIGNALS: void previewSizeChanged(); void previewStarted(); void previewStopped(); void autoFocusStarted(); void whiteBalanceChanged(); void previewFetched(const QByteArray &preview); }; JCamera::JCamera(int cameraId, jobject cam, QThread *workerThread) : QObject() { qRegisterMetaType >(); qRegisterMetaType >(); qRegisterMetaType >(); d = new JCameraWorker(this, cameraId, cam, workerThread); connect(d, &JCameraWorker::previewSizeChanged, this, &JCamera::previewSizeChanged); connect(d, &JCameraWorker::previewStarted, this, &JCamera::previewStarted); connect(d, &JCameraWorker::previewStopped, this, &JCamera::previewStopped); connect(d, &JCameraWorker::autoFocusStarted, this, &JCamera::autoFocusStarted); connect(d, &JCameraWorker::whiteBalanceChanged, this, &JCamera::whiteBalanceChanged); connect(d, &JCameraWorker::previewFetched, this, &JCamera::previewFetched); } JCamera::~JCamera() { if (d->isValid()) { g_objectMapMutex.lock(); g_objectMap.remove(d->m_cameraId); g_objectMapMutex.unlock(); } QThread *workerThread = d->m_workerThread; d->deleteLater(); workerThread->quit(); } JCamera *JCamera::open(int cameraId) { QThread *cameraThread = new QThread; connect(cameraThread, &QThread::finished, cameraThread, &QThread::deleteLater); cameraThread->start(); JCameraInstantiator *instantiator = new JCameraInstantiator; instantiator->moveToThread(cameraThread); QMetaObject::invokeMethod(instantiator, "openCamera", Qt::BlockingQueuedConnection, Q_ARG(int, cameraId)); QJNIObjectPrivate camera = instantiator->result(); delete instantiator; if (!camera.isValid()) { cameraThread->terminate(); delete cameraThread; return 0; } else { return new JCamera(cameraId, camera.object(), cameraThread); } } int JCamera::cameraId() const { return d->m_cameraId; } void JCamera::lock() { QMetaObject::invokeMethod(d, "callVoidMethod", Q_ARG(QByteArray, "lock")); } void JCamera::unlock() { QMetaObject::invokeMethod(d, "callVoidMethod", Q_ARG(QByteArray, "unlock")); } void JCamera::reconnect() { QMetaObject::invokeMethod(d, "callVoidMethod", Q_ARG(QByteArray, "reconnect")); } void JCamera::release() { QMetaObject::invokeMethod(d, "release"); } JCamera::CameraFacing JCamera::getFacing() { return d->getFacing(); } int JCamera::getNativeOrientation() { return d->getNativeOrientation(); } void JCamera::setDisplayOrientation(int degrees) { QMetaObject::invokeMethod(d, "setDisplayOrientation", Q_ARG(int, degrees)); } QSize JCamera::getPreferredPreviewSizeForVideo() { return d->getPreferredPreviewSizeForVideo(); } QList JCamera::getSupportedPreviewSizes() { return d->getSupportedPreviewSizes(); } JCamera::ImageFormat JCamera::getPreviewFormat() { return d->getPreviewFormat(); } void JCamera::setPreviewFormat(ImageFormat fmt) { QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(JCamera::ImageFormat, fmt)); } QSize JCamera::previewSize() const { return d->m_previewSize; } void JCamera::setPreviewSize(const QSize &size) { d->m_parametersMutex.lock(); bool areParametersValid = d->m_parameters.isValid(); d->m_parametersMutex.unlock(); if (!areParametersValid || !size.isValid()) return; d->m_previewSize = size; QMetaObject::invokeMethod(d, "updatePreviewSize"); } void JCamera::setPreviewTexture(jobject surfaceTexture) { QMetaObject::invokeMethod(d, "setPreviewTexture", Q_ARG(void *, surfaceTexture)); } bool JCamera::isZoomSupported() { return d->isZoomSupported(); } int JCamera::getMaxZoom() { return d->getMaxZoom(); } QList JCamera::getZoomRatios() { return d->getZoomRatios(); } int JCamera::getZoom() { return d->getZoom(); } void JCamera::setZoom(int value) { QMetaObject::invokeMethod(d, "setZoom", Q_ARG(int, value)); } QStringList JCamera::getSupportedFlashModes() { return d->callParametersStringListMethod("getSupportedFlashModes"); } QString JCamera::getFlashMode() { return d->getFlashMode(); } void JCamera::setFlashMode(const QString &value) { QMetaObject::invokeMethod(d, "setFlashMode", Q_ARG(QString, value)); } QStringList JCamera::getSupportedFocusModes() { return d->callParametersStringListMethod("getSupportedFocusModes"); } QString JCamera::getFocusMode() { return d->getFocusMode(); } void JCamera::setFocusMode(const QString &value) { QMetaObject::invokeMethod(d, "setFocusMode", Q_ARG(QString, value)); } int JCamera::getMaxNumFocusAreas() { return d->getMaxNumFocusAreas(); } QList JCamera::getFocusAreas() { return d->getFocusAreas(); } void JCamera::setFocusAreas(const QList &areas) { QMetaObject::invokeMethod(d, "setFocusAreas", Q_ARG(QList, areas)); } void JCamera::autoFocus() { QMetaObject::invokeMethod(d, "autoFocus"); } void JCamera::cancelAutoFocus() { QMetaObject::invokeMethod(d, "callVoidMethod", Q_ARG(QByteArray, "cancelAutoFocus")); } bool JCamera::isAutoExposureLockSupported() { return d->isAutoExposureLockSupported(); } bool JCamera::getAutoExposureLock() { return d->getAutoExposureLock(); } void JCamera::setAutoExposureLock(bool toggle) { QMetaObject::invokeMethod(d, "setAutoExposureLock", Q_ARG(bool, toggle)); } bool JCamera::isAutoWhiteBalanceLockSupported() { return d->isAutoWhiteBalanceLockSupported(); } bool JCamera::getAutoWhiteBalanceLock() { return d->getAutoWhiteBalanceLock(); } void JCamera::setAutoWhiteBalanceLock(bool toggle) { QMetaObject::invokeMethod(d, "setAutoWhiteBalanceLock", Q_ARG(bool, toggle)); } int JCamera::getExposureCompensation() { return d->getExposureCompensation(); } void JCamera::setExposureCompensation(int value) { QMetaObject::invokeMethod(d, "setExposureCompensation", Q_ARG(int, value)); } float JCamera::getExposureCompensationStep() { return d->getExposureCompensationStep(); } int JCamera::getMinExposureCompensation() { return d->getMinExposureCompensation(); } int JCamera::getMaxExposureCompensation() { return d->getMaxExposureCompensation(); } QStringList JCamera::getSupportedSceneModes() { return d->callParametersStringListMethod("getSupportedSceneModes"); } QString JCamera::getSceneMode() { return d->getSceneMode(); } void JCamera::setSceneMode(const QString &value) { QMetaObject::invokeMethod(d, "setSceneMode", Q_ARG(QString, value)); } QStringList JCamera::getSupportedWhiteBalance() { return d->callParametersStringListMethod("getSupportedWhiteBalance"); } QString JCamera::getWhiteBalance() { return d->getWhiteBalance(); } void JCamera::setWhiteBalance(const QString &value) { QMetaObject::invokeMethod(d, "setWhiteBalance", Q_ARG(QString, value)); } void JCamera::setRotation(int rotation) { //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 JCamera::getRotation() const { return d->m_rotation; } QList JCamera::getSupportedPictureSizes() { return d->getSupportedPictureSizes(); } void JCamera::setPictureSize(const QSize &size) { QMetaObject::invokeMethod(d, "setPictureSize", Q_ARG(QSize, size)); } void JCamera::setJpegQuality(int quality) { QMetaObject::invokeMethod(d, "setJpegQuality", Q_ARG(int, quality)); } void JCamera::takePicture() { QMetaObject::invokeMethod(d, "callVoidMethod", Q_ARG(QByteArray, "takePicture")); } void JCamera::fetchEachFrame(bool fetch) { QMetaObject::invokeMethod(d, "fetchEachFrame", Q_ARG(bool, fetch)); } void JCamera::fetchLastPreviewFrame() { QMetaObject::invokeMethod(d, "fetchLastPreviewFrame"); } QJNIObjectPrivate JCamera::getCameraObject() { return d->getObjectField("m_camera", "Landroid/hardware/Camera;"); } void JCamera::startPreview() { QMetaObject::invokeMethod(d, "startPreview"); } void JCamera::stopPreview() { QMetaObject::invokeMethod(d, "stopPreview"); } //JCameraWorker JCameraWorker::JCameraWorker(JCamera *camera, int cameraId, jobject cam, QThread *workerThread) : QObject(0) , QJNIObjectPrivate(cam) , m_cameraId(cameraId) , m_rotation(0) , m_hasAPI14(false) , m_parametersMutex(QMutex::Recursive) { q = camera; m_workerThread = workerThread; moveToThread(m_workerThread); if (isValid()) { g_objectMapMutex.lock(); g_objectMap.insert(cameraId, q); g_objectMapMutex.unlock(); m_info = QJNIObjectPrivate("android/hardware/Camera$CameraInfo"); callStaticMethod("android/hardware/Camera", "getCameraInfo", "(ILandroid/hardware/Camera$CameraInfo;)V", cameraId, m_info.object()); QJNIObjectPrivate params = callObjectMethod("getParameters", "()Landroid/hardware/Camera$Parameters;"); m_parameters = QJNIObjectPrivate(params); // Check if API 14 is available QJNIEnvironmentPrivate env; jclass clazz = env->FindClass("android/hardware/Camera"); if (env->ExceptionCheck()) { clazz = 0; env->ExceptionClear(); } if (clazz) { // startFaceDetection() was added in API 14 jmethodID id = env->GetMethodID(clazz, "startFaceDetection", "()V"); if (env->ExceptionCheck()) { id = 0; env->ExceptionClear(); } m_hasAPI14 = bool(id); } } } void JCameraWorker::release() { m_previewSize = QSize(); m_parametersMutex.lock(); m_parameters = QJNIObjectPrivate(); m_parametersMutex.unlock(); callMethod("release"); } JCamera::CameraFacing JCameraWorker::getFacing() { return JCamera::CameraFacing(m_info.getField("facing")); } int JCameraWorker::getNativeOrientation() { return m_info.getField("orientation"); } void JCameraWorker::setDisplayOrientation(int degrees) { callMethod("setDisplayOrientation", "(I)V", degrees); } QSize JCameraWorker::getPreferredPreviewSizeForVideo() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return QSize(); QJNIObjectPrivate size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo", "()Landroid/hardware/Camera$Size;"); return QSize(size.getField("width"), size.getField("height")); } QList JCameraWorker::getSupportedPreviewSizes() { QList list; QMutexLocker parametersLocker(&m_parametersMutex); if (m_parameters.isValid()) { QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPreviewSizes", "()Ljava/util/List;"); int count = sizeList.callMethod("size"); for (int i = 0; i < count; ++i) { QJNIObjectPrivate size = sizeList.callObjectMethod("get", "(I)Ljava/lang/Object;", i); list.append(QSize(size.getField("width"), size.getField("height"))); } qSort(list.begin(), list.end(), qt_sizeLessThan); } return list; } JCamera::ImageFormat JCameraWorker::getPreviewFormat() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return JCamera::Unknown; return JCamera::ImageFormat(m_parameters.callMethod("getPreviewFormat")); } void JCameraWorker::setPreviewFormat(JCamera::ImageFormat fmt) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setPreviewFormat", "(I)V", jint(fmt)); applyParameters(); } void JCameraWorker::updatePreviewSize() { QMutexLocker parametersLocker(&m_parametersMutex); if (m_previewSize.isValid()) { m_parameters.callMethod("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height()); applyParameters(); } emit previewSizeChanged(); } void JCameraWorker::setPreviewTexture(void *surfaceTexture) { callMethod("setPreviewTexture", "(Landroid/graphics/SurfaceTexture;)V", static_cast(surfaceTexture)); } bool JCameraWorker::isZoomSupported() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return false; return m_parameters.callMethod("isZoomSupported"); } int JCameraWorker::getMaxZoom() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getMaxZoom"); } QList JCameraWorker::getZoomRatios() { QMutexLocker parametersLocker(&m_parametersMutex); QList ratios; if (m_parameters.isValid()) { QJNIObjectPrivate ratioList = m_parameters.callObjectMethod("getZoomRatios", "()Ljava/util/List;"); int count = ratioList.callMethod("size"); for (int i = 0; i < count; ++i) { QJNIObjectPrivate zoomRatio = ratioList.callObjectMethod("get", "(I)Ljava/lang/Object;", i); ratios.append(zoomRatio.callMethod("intValue")); } } return ratios; } int JCameraWorker::getZoom() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getZoom"); } void JCameraWorker::setZoom(int value) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setZoom", "(I)V", value); applyParameters(); } QString JCameraWorker::getFlashMode() { QMutexLocker parametersLocker(&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 JCameraWorker::setFlashMode(const QString &value) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setFlashMode", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(value).object()); applyParameters(); } QString JCameraWorker::getFocusMode() { QMutexLocker parametersLocker(&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 JCameraWorker::setFocusMode(const QString &value) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setFocusMode", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(value).object()); applyParameters(); } int JCameraWorker::getMaxNumFocusAreas() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return 0; return m_parameters.callMethod("getMaxNumFocusAreas"); } QList JCameraWorker::getFocusAreas() { QMutexLocker parametersLocker(&m_parametersMutex); QList areas; if (m_hasAPI14 && m_parameters.isValid()) { QJNIObjectPrivate list = m_parameters.callObjectMethod("getFocusAreas", "()Ljava/util/List;"); if (list.isValid()) { int count = list.callMethod("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 JCameraWorker::setFocusAreas(const QList &areas) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !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("add", "(Ljava/lang/Object;)Z", rectToArea(areas.at(i)).object()); if (env->ExceptionCheck()) env->ExceptionClear(); } list = arrayList; } m_parameters.callMethod("setFocusAreas", "(Ljava/util/List;)V", list.object()); applyParameters(); } void JCameraWorker::autoFocus() { callMethod("autoFocus"); emit autoFocusStarted(); } bool JCameraWorker::isAutoExposureLockSupported() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("isAutoExposureLockSupported"); } bool JCameraWorker::getAutoExposureLock() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("getAutoExposureLock"); } void JCameraWorker::setAutoExposureLock(bool toggle) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return; m_parameters.callMethod("setAutoExposureLock", "(Z)V", toggle); applyParameters(); } bool JCameraWorker::isAutoWhiteBalanceLockSupported() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("isAutoWhiteBalanceLockSupported"); } bool JCameraWorker::getAutoWhiteBalanceLock() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("getAutoWhiteBalanceLock"); } void JCameraWorker::setAutoWhiteBalanceLock(bool toggle) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_hasAPI14 || !m_parameters.isValid()) return; m_parameters.callMethod("setAutoWhiteBalanceLock", "(Z)V", toggle); applyParameters(); } int JCameraWorker::getExposureCompensation() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getExposureCompensation"); } void JCameraWorker::setExposureCompensation(int value) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setExposureCompensation", "(I)V", value); applyParameters(); } float JCameraWorker::getExposureCompensationStep() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getExposureCompensationStep"); } int JCameraWorker::getMinExposureCompensation() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getMinExposureCompensation"); } int JCameraWorker::getMaxExposureCompensation() { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getMaxExposureCompensation"); } QString JCameraWorker::getSceneMode() { QMutexLocker parametersLocker(&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 JCameraWorker::setSceneMode(const QString &value) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setSceneMode", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(value).object()); applyParameters(); } QString JCameraWorker::getWhiteBalance() { QMutexLocker parametersLocker(&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 JCameraWorker::setWhiteBalance(const QString &value) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setWhiteBalance", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(value).object()); applyParameters(); emit whiteBalanceChanged(); } void JCameraWorker::updateRotation() { QMutexLocker parametersLocker(&m_parametersMutex); m_parameters.callMethod("setRotation", "(I)V", m_rotation); applyParameters(); } QList JCameraWorker::getSupportedPictureSizes() { QMutexLocker parametersLocker(&m_parametersMutex); QList list; if (m_parameters.isValid()) { QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPictureSizes", "()Ljava/util/List;"); int count = sizeList.callMethod("size"); for (int i = 0; i < count; ++i) { QJNIObjectPrivate size = sizeList.callObjectMethod("get", "(I)Ljava/lang/Object;", i); list.append(QSize(size.getField("width"), size.getField("height"))); } qSort(list.begin(), list.end(), qt_sizeLessThan); } return list; } void JCameraWorker::setPictureSize(const QSize &size) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setPictureSize", "(II)V", size.width(), size.height()); applyParameters(); } void JCameraWorker::setJpegQuality(int quality) { QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) return; m_parameters.callMethod("setJpegQuality", "(I)V", quality); applyParameters(); } void JCameraWorker::startPreview() { callVoidMethod("startPreview"); emit previewStarted(); } void JCameraWorker::stopPreview() { callVoidMethod("stopPreview"); emit previewStopped(); } void JCameraWorker::fetchEachFrame(bool fetch) { callMethod("fetchEachFrame", "(Z)V", fetch); } void JCameraWorker::fetchLastPreviewFrame() { QJNIEnvironmentPrivate env; QJNIObjectPrivate dataObj = callObjectMethod("lockAndFetchPreviewBuffer", "()[B"); if (!dataObj.object()) { callMethod("unlockPreviewBuffer"); return; } jbyteArray data = static_cast(dataObj.object()); QByteArray bytes; int arrayLength = env->GetArrayLength(data); bytes.resize(arrayLength); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); callMethod("unlockPreviewBuffer"); emit previewFetched(bytes); } void JCameraWorker::applyParameters() { callMethod("setParameters", "(Landroid/hardware/Camera$Parameters;)V", m_parameters.object()); } QStringList JCameraWorker::callParametersStringListMethod(const QByteArray &methodName) { QMutexLocker parametersLocker(&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("size"); for (int i = 0; i < count; ++i) { QJNIObjectPrivate string = list.callObjectMethod("get", "(I)Ljava/lang/Object;", i); stringList.append(string.toString()); } } } return stringList; } void JCameraWorker::callVoidMethod(const QByteArray &methodName) { callMethod(methodName.constData()); } static JNINativeMethod methods[] = { {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured}, {"notifyFrameFetched", "(I[B)V", (void *)notifyFrameFetched} }; bool JCamera::initJNI(JNIEnv *env) { jclass clazz = env->FindClass("org/qtproject/qt5/android/multimedia/QtCamera"); if (env->ExceptionCheck()) env->ExceptionClear(); if (clazz) { g_qtCameraClass = static_cast(env->NewGlobalRef(clazz)); if (env->RegisterNatives(g_qtCameraClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { return false; } } return true; } QT_END_NAMESPACE #include "jcamera.moc"