From a0df6c38364c87001258fe9f2375a2bd34ce4cfc Mon Sep 17 00:00:00 2001 From: Denis Kormalev Date: Mon, 16 Dec 2013 12:54:17 +0400 Subject: Android: camera moved to a dedicated thread Actual camera work moved to JCameraWorker which lives in dedicated thread. JCamera now acts as proxy calling JCameraWorker methods with invokeMethod (when it is needed). [ChangeLog][QtMultimedia][Android] camera operations moved to a dedicated thread Task-number: QTBUG-35564 Change-Id: Ie4edcbf0869d56b0fef4ad0c820450cc77657fdd Reviewed-by: Yoann Lopes --- .../src/mediacapture/qandroidcamerasession.cpp | 41 +- .../src/mediacapture/qandroidcamerasession.h | 3 + src/plugins/android/src/wrappers/jcamera.cpp | 772 +++++++++++++++++---- src/plugins/android/src/wrappers/jcamera.h | 32 +- .../android/src/wrappers/jmediarecorder.cpp | 2 +- 5 files changed, 687 insertions(+), 163 deletions(-) diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp index 55065cb46..cb5282394 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp @@ -182,7 +182,10 @@ bool QAndroidCameraSession::open() if (m_camera) { connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed())); + connect(m_camera, SIGNAL(previewFetched(QByteArray)), this, SLOT(onCameraPreviewFetched(QByteArray))); 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())); m_nativeOrientation = m_camera->getNativeOrientation(); @@ -309,11 +312,6 @@ void QAndroidCameraSession::startPreview() m_camera->startPreview(); m_previewStarted = true; - - m_status = QCamera::ActiveStatus; - emit statusChanged(m_status); - - setReadyForCapture(true); } void QAndroidCameraSession::stopPreview() @@ -331,11 +329,6 @@ void QAndroidCameraSession::stopPreview() if (m_videoOutput) m_videoOutput->stop(); m_previewStarted = false; - - m_status = QCamera::LoadedStatus; - emit statusChanged(m_status); - - setReadyForCapture(false); } void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings) @@ -507,11 +500,15 @@ void QAndroidCameraSession::onCameraPictureExposed() return; emit imageExposed(m_currentImageCaptureId); - QByteArray lastFrame = m_camera->fetchLastPreviewFrame(); - if (lastFrame.size()) { + m_camera->fetchLastPreviewFrame(); +} + +void QAndroidCameraSession::onCameraPreviewFetched(const QByteArray &preview) +{ + if (preview.size()) { QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, m_currentImageCaptureId, - lastFrame, + preview, m_camera->getRotation()); } } @@ -532,10 +529,28 @@ void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data) // Preview needs to be restarted after taking a picture m_camera->startPreview(); +} + +void QAndroidCameraSession::onCameraPreviewStarted() +{ + if (m_status == QCamera::StartingStatus) { + m_status = QCamera::ActiveStatus; + emit statusChanged(m_status); + } setReadyForCapture(true); } +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, diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.h b/src/plugins/android/src/mediacapture/qandroidcamerasession.h index 61d8c1a17..a621404a3 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.h @@ -113,7 +113,10 @@ private Q_SLOTS: void onApplicationStateChanged(Qt::ApplicationState state); void onCameraPictureExposed(); + void onCameraPreviewFetched(const QByteArray &preview); void onCameraPictureCaptured(const QByteArray &data); + void onCameraPreviewStarted(); + void onCameraPreviewStopped(); private: bool open(); diff --git a/src/plugins/android/src/wrappers/jcamera.cpp b/src/plugins/android/src/wrappers/jcamera.cpp index 5712ae356..0c7455fae 100644 --- a/src/plugins/android/src/wrappers/jcamera.cpp +++ b/src/plugins/android/src/wrappers/jcamera.cpp @@ -45,11 +45,14 @@ #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) { @@ -78,21 +81,27 @@ static QJNIObjectPrivate rectToArea(const QRect &rect) // 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); @@ -102,15 +111,505 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) } } -JCamera::JCamera(int cameraId, jobject cam) +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 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::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_objectMap.insert(cameraId, this); + g_objectMapMutex.lock(); + g_objectMap.insert(cameraId, q); + g_objectMapMutex.unlock(); m_info = QJNIObjectPrivate("android/hardware/Camera$CameraInfo"); callStaticMethod("android/hardware/Camera", @@ -141,66 +640,34 @@ JCamera::JCamera(int cameraId, jobject cam) } } -JCamera::~JCamera() -{ - if (isValid()) - g_objectMap.remove(m_cameraId); -} - -JCamera *JCamera::open(int cameraId) -{ - QJNIEnvironmentPrivate env; - - QJNIObjectPrivate camera = callStaticObjectMethod(g_qtCameraClass, - "open", - "(I)Lorg/qtproject/qt5/android/multimedia/QtCamera;", - cameraId); - - if (!camera.isValid()) - return 0; - else - return new JCamera(cameraId, camera.object()); -} - -void JCamera::lock() -{ - callMethod("lock"); -} - -void JCamera::unlock() -{ - callMethod("unlock"); -} - -void JCamera::reconnect() -{ - callMethod("reconnect"); -} - -void JCamera::release() +void JCameraWorker::release() { m_previewSize = QSize(); + m_parametersMutex.lock(); m_parameters = QJNIObjectPrivate(); + m_parametersMutex.unlock(); callMethod("release"); } -JCamera::CameraFacing JCamera::getFacing() +JCamera::CameraFacing JCameraWorker::getFacing() { - return CameraFacing(m_info.getField("facing")); + return JCamera::CameraFacing(m_info.getField("facing")); } -int JCamera::getNativeOrientation() +int JCameraWorker::getNativeOrientation() { return m_info.getField("orientation"); } -void JCamera::setDisplayOrientation(int degrees) +void JCameraWorker::setDisplayOrientation(int degrees) { callMethod("setDisplayOrientation", "(I)V", degrees); } -QSize JCamera::getPreferredPreviewSizeForVideo() +QSize JCameraWorker::getPreferredPreviewSizeForVideo() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return QSize(); @@ -210,10 +677,12 @@ QSize JCamera::getPreferredPreviewSizeForVideo() return QSize(size.getField("width"), size.getField("height")); } -QList JCamera::getSupportedPreviewSizes() +QList JCameraWorker::getSupportedPreviewSizes() { QList list; + QMutexLocker parametersLocker(&m_parametersMutex); + if (m_parameters.isValid()) { QJNIObjectPrivate sizeList = m_parameters.callObjectMethod("getSupportedPreviewSizes", "()Ljava/util/List;"); @@ -231,16 +700,20 @@ QList JCamera::getSupportedPreviewSizes() return list; } -JCamera::ImageFormat JCamera::getPreviewFormat() +JCamera::ImageFormat JCameraWorker::getPreviewFormat() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) - return Unknown; + return JCamera::Unknown; return JCamera::ImageFormat(m_parameters.callMethod("getPreviewFormat")); } -void JCamera::setPreviewFormat(ImageFormat fmt) +void JCameraWorker::setPreviewFormat(JCamera::ImageFormat fmt) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -248,44 +721,47 @@ void JCamera::setPreviewFormat(ImageFormat fmt) applyParameters(); } -void JCamera::setPreviewSize(const QSize &size) +void JCameraWorker::updatePreviewSize() { - if (!m_parameters.isValid()) - return; - - m_previewSize = size; + QMutexLocker parametersLocker(&m_parametersMutex); if (m_previewSize.isValid()) { - m_parameters.callMethod("setPreviewSize", "(II)V", size.width(), size.height()); + m_parameters.callMethod("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height()); applyParameters(); } emit previewSizeChanged(); } -void JCamera::setPreviewTexture(jobject surfaceTexture) +void JCameraWorker::setPreviewTexture(void *surfaceTexture) { - callMethod("setPreviewTexture", "(Landroid/graphics/SurfaceTexture;)V", surfaceTexture); + callMethod("setPreviewTexture", "(Landroid/graphics/SurfaceTexture;)V", static_cast(surfaceTexture)); } -bool JCamera::isZoomSupported() +bool JCameraWorker::isZoomSupported() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return false; return m_parameters.callMethod("isZoomSupported"); } -int JCamera::getMaxZoom() +int JCameraWorker::getMaxZoom() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getMaxZoom"); } -QList JCamera::getZoomRatios() +QList JCameraWorker::getZoomRatios() { + QMutexLocker parametersLocker(&m_parametersMutex); + QList ratios; if (m_parameters.isValid()) { @@ -304,16 +780,20 @@ QList JCamera::getZoomRatios() return ratios; } -int JCamera::getZoom() +int JCameraWorker::getZoom() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getZoom"); } -void JCamera::setZoom(int value) +void JCameraWorker::setZoom(int value) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -321,13 +801,10 @@ void JCamera::setZoom(int value) applyParameters(); } -QStringList JCamera::getSupportedFlashModes() +QString JCameraWorker::getFlashMode() { - return callStringListMethod("getSupportedFlashModes"); -} + QMutexLocker parametersLocker(&m_parametersMutex); -QString JCamera::getFlashMode() -{ QString value; if (m_parameters.isValid()) { @@ -340,8 +817,10 @@ QString JCamera::getFlashMode() return value; } -void JCamera::setFlashMode(const QString &value) +void JCameraWorker::setFlashMode(const QString &value) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -351,13 +830,10 @@ void JCamera::setFlashMode(const QString &value) applyParameters(); } -QStringList JCamera::getSupportedFocusModes() +QString JCameraWorker::getFocusMode() { - return callStringListMethod("getSupportedFocusModes"); -} + QMutexLocker parametersLocker(&m_parametersMutex); -QString JCamera::getFocusMode() -{ QString value; if (m_parameters.isValid()) { @@ -370,8 +846,10 @@ QString JCamera::getFocusMode() return value; } -void JCamera::setFocusMode(const QString &value) +void JCameraWorker::setFocusMode(const QString &value) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -381,16 +859,20 @@ void JCamera::setFocusMode(const QString &value) applyParameters(); } -int JCamera::getMaxNumFocusAreas() +int JCameraWorker::getMaxNumFocusAreas() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return 0; return m_parameters.callMethod("getMaxNumFocusAreas"); } -QList JCamera::getFocusAreas() +QList JCameraWorker::getFocusAreas() { + QMutexLocker parametersLocker(&m_parametersMutex); + QList areas; if (m_hasAPI14 && m_parameters.isValid()) { @@ -412,8 +894,10 @@ QList JCamera::getFocusAreas() return areas; } -void JCamera::setFocusAreas(const QList &areas) +void JCameraWorker::setFocusAreas(const QList &areas) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return; @@ -437,35 +921,36 @@ void JCamera::setFocusAreas(const QList &areas) applyParameters(); } -void JCamera::autoFocus() +void JCameraWorker::autoFocus() { callMethod("autoFocus"); emit autoFocusStarted(); } -void JCamera::cancelAutoFocus() +bool JCameraWorker::isAutoExposureLockSupported() { - callMethod("cancelAutoFocus"); -} + QMutexLocker parametersLocker(&m_parametersMutex); -bool JCamera::isAutoExposureLockSupported() -{ if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("isAutoExposureLockSupported"); } -bool JCamera::getAutoExposureLock() +bool JCameraWorker::getAutoExposureLock() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("getAutoExposureLock"); } -void JCamera::setAutoExposureLock(bool toggle) +void JCameraWorker::setAutoExposureLock(bool toggle) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return; @@ -473,24 +958,30 @@ void JCamera::setAutoExposureLock(bool toggle) applyParameters(); } -bool JCamera::isAutoWhiteBalanceLockSupported() +bool JCameraWorker::isAutoWhiteBalanceLockSupported() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("isAutoWhiteBalanceLockSupported"); } -bool JCamera::getAutoWhiteBalanceLock() +bool JCameraWorker::getAutoWhiteBalanceLock() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return false; return m_parameters.callMethod("getAutoWhiteBalanceLock"); } -void JCamera::setAutoWhiteBalanceLock(bool toggle) +void JCameraWorker::setAutoWhiteBalanceLock(bool toggle) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_hasAPI14 || !m_parameters.isValid()) return; @@ -498,16 +989,20 @@ void JCamera::setAutoWhiteBalanceLock(bool toggle) applyParameters(); } -int JCamera::getExposureCompensation() +int JCameraWorker::getExposureCompensation() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getExposureCompensation"); } -void JCamera::setExposureCompensation(int value) +void JCameraWorker::setExposureCompensation(int value) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -515,37 +1010,40 @@ void JCamera::setExposureCompensation(int value) applyParameters(); } -float JCamera::getExposureCompensationStep() +float JCameraWorker::getExposureCompensationStep() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getExposureCompensationStep"); } -int JCamera::getMinExposureCompensation() +int JCameraWorker::getMinExposureCompensation() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getMinExposureCompensation"); } -int JCamera::getMaxExposureCompensation() +int JCameraWorker::getMaxExposureCompensation() { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return 0; return m_parameters.callMethod("getMaxExposureCompensation"); } -QStringList JCamera::getSupportedSceneModes() +QString JCameraWorker::getSceneMode() { - return callStringListMethod("getSupportedSceneModes"); -} + QMutexLocker parametersLocker(&m_parametersMutex); -QString JCamera::getSceneMode() -{ QString value; if (m_parameters.isValid()) { @@ -558,8 +1056,10 @@ QString JCamera::getSceneMode() return value; } -void JCamera::setSceneMode(const QString &value) +void JCameraWorker::setSceneMode(const QString &value) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -569,13 +1069,10 @@ void JCamera::setSceneMode(const QString &value) applyParameters(); } -QStringList JCamera::getSupportedWhiteBalance() +QString JCameraWorker::getWhiteBalance() { - return callStringListMethod("getSupportedWhiteBalance"); -} + QMutexLocker parametersLocker(&m_parametersMutex); -QString JCamera::getWhiteBalance() -{ QString value; if (m_parameters.isValid()) { @@ -588,8 +1085,10 @@ QString JCamera::getWhiteBalance() return value; } -void JCamera::setWhiteBalance(const QString &value) +void JCameraWorker::setWhiteBalance(const QString &value) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -601,23 +1100,18 @@ void JCamera::setWhiteBalance(const QString &value) emit whiteBalanceChanged(); } -void JCamera::setRotation(int rotation) +void JCameraWorker::updateRotation() { - if (!m_parameters.isValid()) - return; + QMutexLocker parametersLocker(&m_parametersMutex); - m_rotation = rotation; - m_parameters.callMethod("setRotation", "(I)V", rotation); + m_parameters.callMethod("setRotation", "(I)V", m_rotation); applyParameters(); } -int JCamera::getRotation() const +QList JCameraWorker::getSupportedPictureSizes() { - return m_rotation; -} + QMutexLocker parametersLocker(&m_parametersMutex); -QList JCamera::getSupportedPictureSizes() -{ QList list; if (m_parameters.isValid()) { @@ -637,8 +1131,10 @@ QList JCamera::getSupportedPictureSizes() return list; } -void JCamera::setPictureSize(const QSize &size) +void JCameraWorker::setPictureSize(const QSize &size) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -646,8 +1142,10 @@ void JCamera::setPictureSize(const QSize &size) applyParameters(); } -void JCamera::setJpegQuality(int quality) +void JCameraWorker::setJpegQuality(int quality) { + QMutexLocker parametersLocker(&m_parametersMutex); + if (!m_parameters.isValid()) return; @@ -655,18 +1153,25 @@ void JCamera::setJpegQuality(int quality) applyParameters(); } -void JCamera::takePicture() +void JCameraWorker::startPreview() +{ + callVoidMethod("startPreview"); + emit previewStarted(); +} + +void JCameraWorker::stopPreview() { - callMethod("takePicture"); + callVoidMethod("stopPreview"); + emit previewStopped(); } -QByteArray JCamera::fetchLastPreviewFrame() +void JCameraWorker::fetchLastPreviewFrame() { QJNIEnvironmentPrivate env; QJNIObjectPrivate dataObj = callObjectMethod("lockAndFetchPreviewBuffer", "()[B"); if (!dataObj.object()) { callMethod("unlockPreviewBuffer"); - return QByteArray(); + return; } jbyteArray data = static_cast(dataObj.object()); QByteArray bytes; @@ -674,32 +1179,25 @@ QByteArray JCamera::fetchLastPreviewFrame() bytes.resize(arrayLength); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); callMethod("unlockPreviewBuffer"); - return bytes; -} -void JCamera::startPreview() -{ - callMethod("startPreview"); -} - -void JCamera::stopPreview() -{ - callMethod("stopPreview"); + emit previewFetched(bytes); } -void JCamera::applyParameters() +void JCameraWorker::applyParameters() { callMethod("setParameters", "(Landroid/hardware/Camera$Parameters;)V", m_parameters.object()); } -QStringList JCamera::callStringListMethod(const char *methodName) +QStringList JCameraWorker::callParametersStringListMethod(const QByteArray &methodName) { + QMutexLocker parametersLocker(&m_parametersMutex); + QStringList stringList; if (m_parameters.isValid()) { - QJNIObjectPrivate list = m_parameters.callObjectMethod(methodName, + QJNIObjectPrivate list = m_parameters.callObjectMethod(methodName.constData(), "()Ljava/util/List;"); if (list.isValid()) { @@ -717,6 +1215,12 @@ QStringList JCamera::callStringListMethod(const char *methodName) return stringList; } +void JCameraWorker::callVoidMethod(const QByteArray &methodName) +{ + callMethod(methodName.constData()); +} + + static JNINativeMethod methods[] = { {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, @@ -742,3 +1246,5 @@ bool JCamera::initJNI(JNIEnv *env) } QT_END_NAMESPACE + +#include "jcamera.moc" diff --git a/src/plugins/android/src/wrappers/jcamera.h b/src/plugins/android/src/wrappers/jcamera.h index 6bba98402..9b2f5b177 100644 --- a/src/plugins/android/src/wrappers/jcamera.h +++ b/src/plugins/android/src/wrappers/jcamera.h @@ -49,9 +49,15 @@ QT_BEGIN_NAMESPACE -class JCamera : public QObject, public QJNIObjectPrivate +class QThread; + +class JCameraWorker; + +class JCamera : public QObject { Q_OBJECT + Q_ENUMS(CameraFacing) + Q_ENUMS(ImageFormat) public: enum CameraFacing { CameraFacingBack = 0, @@ -72,7 +78,7 @@ public: static JCamera *open(int cameraId); - int cameraId() const { return m_cameraId; } + int cameraId() const; void lock(); void unlock(); @@ -90,7 +96,7 @@ public: ImageFormat getPreviewFormat(); void setPreviewFormat(ImageFormat fmt); - QSize previewSize() const { return m_previewSize; } + QSize previewSize() const; void setPreviewSize(const QSize &size); void setPreviewTexture(jobject surfaceTexture); @@ -149,12 +155,15 @@ public: void takePicture(); - QByteArray fetchLastPreviewFrame(); + void fetchLastPreviewFrame(); + QJNIObjectPrivate getCameraObject(); static bool initJNI(JNIEnv *env); Q_SIGNALS: void previewSizeChanged(); + void previewStarted(); + void previewStopped(); void autoFocusStarted(); void autoFocusComplete(bool success); @@ -163,21 +172,12 @@ Q_SIGNALS: void pictureExposed(); void pictureCaptured(const QByteArray &data); + void previewFetched(const QByteArray &preview); private: - JCamera(int cameraId, jobject cam); - void applyParameters(); - - QStringList callStringListMethod(const char *methodName); - - int m_cameraId; - QJNIObjectPrivate m_info; - QJNIObjectPrivate m_parameters; - - QSize m_previewSize; - int m_rotation; + JCamera(int cameraId, jobject cam, QThread *workerThread); - bool m_hasAPI14; + JCameraWorker *d; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jmediarecorder.cpp b/src/plugins/android/src/wrappers/jmediarecorder.cpp index 1a9ee33f7..b2b93f893 100644 --- a/src/plugins/android/src/wrappers/jmediarecorder.cpp +++ b/src/plugins/android/src/wrappers/jmediarecorder.cpp @@ -151,7 +151,7 @@ void JMediaRecorder::setAudioSource(AudioSource source) void JMediaRecorder::setCamera(JCamera *camera) { - QJNIObjectPrivate cam = camera->getObjectField("m_camera", "Landroid/hardware/Camera;"); + QJNIObjectPrivate cam = camera->getCameraObject(); callMethod("setCamera", "(Landroid/hardware/Camera;)V", cam.object()); } -- cgit v1.2.3