diff options
5 files changed, 91 insertions, 43 deletions
diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCamera.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCamera.java index 3d891196f..4aa07b713 100644 --- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCamera.java +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCamera.java @@ -42,8 +42,11 @@ package org.qtproject.qt5.android.multimedia; import android.hardware.Camera; +import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.util.Log; +import java.lang.Math; +import java.util.concurrent.locks.ReentrantLock; public class QtCamera implements Camera.ShutterCallback, Camera.PictureCallback, @@ -52,6 +55,11 @@ public class QtCamera implements Camera.ShutterCallback, { private int m_cameraId = -1; private Camera m_camera = null; + private byte[] m_cameraPreviewFirstBuffer = null; + private byte[] m_cameraPreviewSecondBuffer = null; + private int m_actualPreviewBuffer = 0; + private final ReentrantLock m_buffersLock = new ReentrantLock(); + private boolean m_isReleased = false; private static final String TAG = "Qt Camera"; @@ -97,6 +105,7 @@ public class QtCamera implements Camera.ShutterCallback, public void release() { + m_isReleased = true; m_camera.release(); } @@ -134,6 +143,22 @@ public class QtCamera implements Camera.ShutterCallback, public void startPreview() { + Camera.Size previewSize = m_camera.getParameters().getPreviewSize(); + double bytesPerPixel = ImageFormat.getBitsPerPixel(m_camera.getParameters().getPreviewFormat()) / 8.0; + int bufferSizeNeeded = (int)Math.ceil(bytesPerPixel*previewSize.width*previewSize.height); + + //We need to clear preview buffers queue here, but there is no method to do it + //Though just resetting preview callback do the trick + m_camera.setPreviewCallback(null); + m_buffersLock.lock(); + if (m_cameraPreviewFirstBuffer == null || m_cameraPreviewFirstBuffer.length < bufferSizeNeeded) + m_cameraPreviewFirstBuffer = new byte[bufferSizeNeeded]; + if (m_cameraPreviewSecondBuffer == null || m_cameraPreviewSecondBuffer.length < bufferSizeNeeded) + m_cameraPreviewSecondBuffer = new byte[bufferSizeNeeded]; + addCallbackBuffer(); + m_buffersLock.unlock(); + m_camera.setPreviewCallbackWithBuffer(this); + m_camera.startPreview(); } @@ -152,11 +177,6 @@ public class QtCamera implements Camera.ShutterCallback, m_camera.cancelAutoFocus(); } - public void requestPreviewFrame() - { - m_camera.setOneShotPreviewCallback(this); - } - public void takePicture() { try { @@ -166,6 +186,37 @@ public class QtCamera implements Camera.ShutterCallback, } } + public byte[] lockAndFetchPreviewBuffer() + { + //This method should always be followed by unlockPreviewBuffer() + //This method is not just a getter. It also marks last preview as already seen one. + //We should reset actualBuffer flag here to make sure we will not use old preview with future captures + byte[] result = null; + m_buffersLock.lock(); + if (m_actualPreviewBuffer == 1) + result = m_cameraPreviewFirstBuffer; + else if (m_actualPreviewBuffer == 2) + result = m_cameraPreviewSecondBuffer; + m_actualPreviewBuffer = 0; + return result; + } + + public void unlockPreviewBuffer() + { + if (m_buffersLock.isHeldByCurrentThread()) + m_buffersLock.unlock(); + } + + private void addCallbackBuffer() + { + if (m_isReleased) + return; + + m_camera.addCallbackBuffer((m_actualPreviewBuffer == 1) + ? m_cameraPreviewSecondBuffer + : m_cameraPreviewFirstBuffer); + } + @Override public void onShutter() { @@ -181,7 +232,15 @@ public class QtCamera implements Camera.ShutterCallback, @Override public void onPreviewFrame(byte[] data, Camera camera) { - notifyPreviewFrame(m_cameraId, data); + m_buffersLock.lock(); + if (data == m_cameraPreviewFirstBuffer) + m_actualPreviewBuffer = 1; + else if (data == m_cameraPreviewSecondBuffer) + m_actualPreviewBuffer = 2; + else + m_actualPreviewBuffer = 0; + addCallbackBuffer(); + m_buffersLock.unlock(); } @Override @@ -193,5 +252,4 @@ public class QtCamera implements Camera.ShutterCallback, private static native void notifyAutoFocusComplete(int id, boolean success); private static native void notifyPictureExposed(int id); private static native void notifyPictureCaptured(int id, byte[] data); - private static native void notifyPreviewFrame(int id, byte[] data); } diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp index 5ff19d1c1..55065cb46 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp @@ -183,7 +183,6 @@ bool QAndroidCameraSession::open() if (m_camera) { connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed())); connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray))); - connect(m_camera, SIGNAL(previewFrameAvailable(QByteArray)), this, SLOT(onCameraPreviewFrameAvailable(QByteArray))); m_nativeOrientation = m_camera->getNativeOrientation(); @@ -484,7 +483,6 @@ int QAndroidCameraSession::capture(const QString &fileName) // adjust picture rotation depending on the device orientation m_camera->setRotation(currentCameraRotation()); - m_camera->requestPreviewFrame(); m_camera->takePicture(); } else { //: Drive mode is the camera's shutter mode, for example single shot, continuos exposure, etc. @@ -509,6 +507,13 @@ void QAndroidCameraSession::onCameraPictureExposed() return; emit imageExposed(m_currentImageCaptureId); + QByteArray lastFrame = m_camera->fetchLastPreviewFrame(); + if (lastFrame.size()) { + QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, + m_currentImageCaptureId, + lastFrame, + m_camera->getRotation()); + } } void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data) @@ -571,17 +576,6 @@ void QAndroidCameraSession::processCapturedImage(int id, } } -void QAndroidCameraSession::onCameraPreviewFrameAvailable(const QByteArray &data) -{ - if (m_captureCanceled || m_readyForCapture) - return; - - QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, - m_currentImageCaptureId, - data, - m_camera->getRotation()); -} - void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int rotation) { QSize frameSize = m_camera->previewSize(); diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.h b/src/plugins/android/src/mediacapture/qandroidcamerasession.h index 17ea4171f..61d8c1a17 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.h @@ -114,7 +114,6 @@ private Q_SLOTS: void onCameraPictureExposed(); void onCameraPictureCaptured(const QByteArray &data); - void onCameraPreviewFrameAvailable(const QByteArray &data); private: bool open(); diff --git a/src/plugins/android/src/wrappers/jcamera.cpp b/src/plugins/android/src/wrappers/jcamera.cpp index e69cb554d..5712ae356 100644 --- a/src/plugins/android/src/wrappers/jcamera.cpp +++ b/src/plugins/android/src/wrappers/jcamera.cpp @@ -102,18 +102,6 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) } } -static void notifyPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data) -{ - JCamera *obj = g_objectMap.value(id, 0); - if (obj) { - QByteArray bytes; - int arrayLength = env->GetArrayLength(data); - bytes.resize(arrayLength); - env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); - Q_EMIT obj->previewFrameAvailable(bytes); - } -} - JCamera::JCamera(int cameraId, jobject cam) : QObject() , QJNIObjectPrivate(cam) @@ -667,14 +655,26 @@ void JCamera::setJpegQuality(int quality) applyParameters(); } -void JCamera::requestPreviewFrame() +void JCamera::takePicture() { - callMethod<void>("requestPreviewFrame"); + callMethod<void>("takePicture"); } -void JCamera::takePicture() +QByteArray JCamera::fetchLastPreviewFrame() { - callMethod<void>("takePicture"); + QJNIEnvironmentPrivate env; + QJNIObjectPrivate dataObj = callObjectMethod("lockAndFetchPreviewBuffer", "()[B"); + if (!dataObj.object()) { + callMethod<void>("unlockPreviewBuffer"); + return QByteArray(); + } + jbyteArray data = static_cast<jbyteArray>(dataObj.object()); + QByteArray bytes; + int arrayLength = env->GetArrayLength(data); + bytes.resize(arrayLength); + env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); + callMethod<void>("unlockPreviewBuffer"); + return bytes; } void JCamera::startPreview() @@ -720,8 +720,7 @@ QStringList JCamera::callStringListMethod(const char *methodName) static JNINativeMethod methods[] = { {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, - {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured}, - {"notifyPreviewFrame", "(I[B)V", (void *)notifyPreviewFrame} + {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured} }; bool JCamera::initJNI(JNIEnv *env) diff --git a/src/plugins/android/src/wrappers/jcamera.h b/src/plugins/android/src/wrappers/jcamera.h index 7c47ec7a2..6bba98402 100644 --- a/src/plugins/android/src/wrappers/jcamera.h +++ b/src/plugins/android/src/wrappers/jcamera.h @@ -147,10 +147,10 @@ public: void startPreview(); void stopPreview(); - void requestPreviewFrame(); - void takePicture(); + QByteArray fetchLastPreviewFrame(); + static bool initJNI(JNIEnv *env); Q_SIGNALS: @@ -161,8 +161,6 @@ Q_SIGNALS: void whiteBalanceChanged(); - void previewFrameAvailable(const QByteArray &data); - void pictureExposed(); void pictureCaptured(const QByteArray &data); |