Android: minor refactor of the camera frame callback.
Change-Id: I6b281c9b2d02cf223e66e04e31fdd0268aa277fc Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
@@ -38,63 +38,87 @@ import android.graphics.ImageFormat;
|
|||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import java.lang.Math;
|
import java.lang.Math;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
public class QtCameraListener implements Camera.ShutterCallback,
|
public class QtCameraListener implements Camera.ShutterCallback,
|
||||||
Camera.PictureCallback,
|
Camera.PictureCallback,
|
||||||
Camera.AutoFocusCallback,
|
Camera.AutoFocusCallback,
|
||||||
Camera.PreviewCallback
|
Camera.PreviewCallback
|
||||||
{
|
{
|
||||||
private int m_cameraId = -1;
|
|
||||||
private byte[][] m_cameraPreviewBuffer = null;
|
|
||||||
private volatile int m_actualPreviewBuffer = 0;
|
|
||||||
private final ReentrantLock m_buffersLock = new ReentrantLock();
|
|
||||||
private boolean m_fetchEachFrame = false;
|
|
||||||
|
|
||||||
private static final String TAG = "Qt Camera";
|
private static final String TAG = "Qt Camera";
|
||||||
|
|
||||||
|
private static final int BUFFER_POOL_SIZE = 2;
|
||||||
|
|
||||||
|
private int m_cameraId = -1;
|
||||||
|
|
||||||
|
private boolean m_notifyNewFrames = false;
|
||||||
|
private byte[][] m_previewBuffers = null;
|
||||||
|
private byte[] m_lastPreviewBuffer = null;
|
||||||
|
private Camera.Size m_previewSize = null;
|
||||||
|
|
||||||
private QtCameraListener(int id)
|
private QtCameraListener(int id)
|
||||||
{
|
{
|
||||||
m_cameraId = id;
|
m_cameraId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void preparePreviewBuffer(Camera camera)
|
public void notifyNewFrames(boolean notify)
|
||||||
{
|
{
|
||||||
Camera.Size previewSize = camera.getParameters().getPreviewSize();
|
m_notifyNewFrames = notify;
|
||||||
double bytesPerPixel = ImageFormat.getBitsPerPixel(camera.getParameters().getPreviewFormat()) / 8.0;
|
|
||||||
int bufferSizeNeeded = (int)Math.ceil(bytesPerPixel*previewSize.width*previewSize.height);
|
|
||||||
m_buffersLock.lock();
|
|
||||||
if (m_cameraPreviewBuffer == null || m_cameraPreviewBuffer[0].length < bufferSizeNeeded)
|
|
||||||
m_cameraPreviewBuffer = new byte[2][bufferSizeNeeded];
|
|
||||||
m_buffersLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fetchEachFrame(boolean fetch)
|
public byte[] lastPreviewBuffer()
|
||||||
{
|
{
|
||||||
m_fetchEachFrame = fetch;
|
return m_lastPreviewBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] lockAndFetchPreviewBuffer()
|
public int previewWidth()
|
||||||
{
|
{
|
||||||
//This method should always be followed by unlockPreviewBuffer()
|
if (m_previewSize == null)
|
||||||
//This method is not just a getter. It also marks last preview as already seen one.
|
return -1;
|
||||||
//We should reset actualBuffer flag here to make sure we will not use old preview with future captures
|
|
||||||
byte[] result = null;
|
return m_previewSize.width;
|
||||||
m_buffersLock.lock();
|
|
||||||
result = m_cameraPreviewBuffer[(m_actualPreviewBuffer == 1) ? 0 : 1];
|
|
||||||
m_actualPreviewBuffer = 0;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlockPreviewBuffer()
|
public int previewHeight()
|
||||||
{
|
{
|
||||||
if (m_buffersLock.isHeldByCurrentThread())
|
if (m_previewSize == null)
|
||||||
m_buffersLock.unlock();
|
return -1;
|
||||||
|
|
||||||
|
return m_previewSize.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] callbackBuffer()
|
public void setupPreviewCallback(Camera camera)
|
||||||
{
|
{
|
||||||
return m_cameraPreviewBuffer[(m_actualPreviewBuffer == 1) ? 1 : 0];
|
// Clear previous callback (also clears added buffers)
|
||||||
|
m_lastPreviewBuffer = null;
|
||||||
|
camera.setPreviewCallbackWithBuffer(null);
|
||||||
|
|
||||||
|
final Camera.Parameters params = camera.getParameters();
|
||||||
|
m_previewSize = params.getPreviewSize();
|
||||||
|
double bytesPerPixel = ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8.0;
|
||||||
|
int bufferSizeNeeded = (int) Math.ceil(bytesPerPixel * m_previewSize.width * m_previewSize.height);
|
||||||
|
|
||||||
|
// We could keep the same buffers when they are already bigger than the required size
|
||||||
|
// but the Android doc says the size must match, so in doubt just replace them.
|
||||||
|
if (m_previewBuffers == null || m_previewBuffers[0].length != bufferSizeNeeded)
|
||||||
|
m_previewBuffers = new byte[BUFFER_POOL_SIZE][bufferSizeNeeded];
|
||||||
|
|
||||||
|
// Add callback and queue all buffers
|
||||||
|
camera.setPreviewCallbackWithBuffer(this);
|
||||||
|
for (byte[] buffer : m_previewBuffers)
|
||||||
|
camera.addCallbackBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPreviewFrame(byte[] data, Camera camera)
|
||||||
|
{
|
||||||
|
// Re-enqueue the last buffer
|
||||||
|
if (m_lastPreviewBuffer != null)
|
||||||
|
camera.addCallbackBuffer(m_lastPreviewBuffer);
|
||||||
|
|
||||||
|
m_lastPreviewBuffer = data;
|
||||||
|
|
||||||
|
if (data != null && m_notifyNewFrames)
|
||||||
|
notifyNewPreviewFrame(m_cameraId, data, m_previewSize.width, m_previewSize.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,24 +133,6 @@ public class QtCameraListener implements Camera.ShutterCallback,
|
|||||||
notifyPictureCaptured(m_cameraId, data);
|
notifyPictureCaptured(m_cameraId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPreviewFrame(byte[] data, Camera camera)
|
|
||||||
{
|
|
||||||
m_buffersLock.lock();
|
|
||||||
|
|
||||||
if (data != null && m_fetchEachFrame)
|
|
||||||
notifyFrameFetched(m_cameraId, data);
|
|
||||||
|
|
||||||
if (data == m_cameraPreviewBuffer[0])
|
|
||||||
m_actualPreviewBuffer = 1;
|
|
||||||
else if (data == m_cameraPreviewBuffer[1])
|
|
||||||
m_actualPreviewBuffer = 2;
|
|
||||||
else
|
|
||||||
m_actualPreviewBuffer = 0;
|
|
||||||
camera.addCallbackBuffer(m_cameraPreviewBuffer[(m_actualPreviewBuffer == 1) ? 1 : 0]);
|
|
||||||
m_buffersLock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAutoFocus(boolean success, Camera camera)
|
public void onAutoFocus(boolean success, Camera camera)
|
||||||
{
|
{
|
||||||
@@ -136,5 +142,5 @@ public class QtCameraListener implements Camera.ShutterCallback,
|
|||||||
private static native void notifyAutoFocusComplete(int id, boolean success);
|
private static native void notifyAutoFocusComplete(int id, boolean success);
|
||||||
private static native void notifyPictureExposed(int id);
|
private static native void notifyPictureExposed(int id);
|
||||||
private static native void notifyPictureCaptured(int id, byte[] data);
|
private static native void notifyPictureCaptured(int id, byte[] data);
|
||||||
private static native void notifyFrameFetched(int id, byte[] data);
|
private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,9 +206,10 @@ bool QAndroidCameraSession::open()
|
|||||||
|
|
||||||
if (m_camera) {
|
if (m_camera) {
|
||||||
connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed()));
|
connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed()));
|
||||||
connect(m_camera, SIGNAL(previewFetched(QByteArray)), this, SLOT(onCameraPreviewFetched(QByteArray)));
|
connect(m_camera, SIGNAL(lastPreviewFrameFetched(QByteArray,int,int)),
|
||||||
connect(m_camera, SIGNAL(frameFetched(QByteArray)),
|
this, SLOT(onLastPreviewFrameFetched(QByteArray,int,int)));
|
||||||
this, SLOT(onCameraFrameFetched(QByteArray)),
|
connect(m_camera, SIGNAL(newPreviewFrame(QByteArray,int,int)),
|
||||||
|
this, SLOT(onNewPreviewFrame(QByteArray,int,int)),
|
||||||
Qt::DirectConnection);
|
Qt::DirectConnection);
|
||||||
connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray)));
|
connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray)));
|
||||||
connect(m_camera, SIGNAL(previewStarted()), this, SLOT(onCameraPreviewStarted()));
|
connect(m_camera, SIGNAL(previewStarted()), this, SLOT(onCameraPreviewStarted()));
|
||||||
@@ -221,7 +222,7 @@ bool QAndroidCameraSession::open()
|
|||||||
if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
|
if (m_camera->getPreviewFormat() != AndroidCamera::NV21)
|
||||||
m_camera->setPreviewFormat(AndroidCamera::NV21);
|
m_camera->setPreviewFormat(AndroidCamera::NV21);
|
||||||
|
|
||||||
m_camera->fetchEachFrame(m_videoProbes.count());
|
m_camera->notifyNewFrames(m_videoProbes.count());
|
||||||
|
|
||||||
emit opened();
|
emit opened();
|
||||||
} else {
|
} else {
|
||||||
@@ -410,7 +411,7 @@ void QAndroidCameraSession::addProbe(QAndroidMediaVideoProbeControl *probe)
|
|||||||
if (probe)
|
if (probe)
|
||||||
m_videoProbes << probe;
|
m_videoProbes << probe;
|
||||||
if (m_camera)
|
if (m_camera)
|
||||||
m_camera->fetchEachFrame(m_videoProbes.count());
|
m_camera->notifyNewFrames(m_videoProbes.count());
|
||||||
m_videoProbesMutex.unlock();
|
m_videoProbesMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,7 +420,7 @@ void QAndroidCameraSession::removeProbe(QAndroidMediaVideoProbeControl *probe)
|
|||||||
m_videoProbesMutex.lock();
|
m_videoProbesMutex.lock();
|
||||||
m_videoProbes.remove(probe);
|
m_videoProbes.remove(probe);
|
||||||
if (m_camera)
|
if (m_camera)
|
||||||
m_camera->fetchEachFrame(m_videoProbes.count());
|
m_camera->notifyNewFrames(m_videoProbes.count());
|
||||||
m_videoProbesMutex.unlock();
|
m_videoProbesMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,25 +563,54 @@ void QAndroidCameraSession::onCameraPictureExposed()
|
|||||||
m_camera->fetchLastPreviewFrame();
|
m_camera->fetchLastPreviewFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidCameraSession::onCameraPreviewFetched(const QByteArray &preview)
|
void QAndroidCameraSession::onLastPreviewFrameFetched(const QByteArray &preview, int width, int height)
|
||||||
{
|
{
|
||||||
if (preview.size()) {
|
if (preview.size()) {
|
||||||
QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
|
QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
|
||||||
m_currentImageCaptureId,
|
m_currentImageCaptureId,
|
||||||
preview,
|
preview,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
m_camera->getRotation());
|
m_camera->getRotation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidCameraSession::onCameraFrameFetched(const QByteArray &frame)
|
void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation)
|
||||||
|
{
|
||||||
|
emit imageCaptured(id, prepareImageFromPreviewData(data, width, height, rotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QAndroidCameraSession::prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation)
|
||||||
|
{
|
||||||
|
QImage result(width, height, QImage::Format_ARGB32);
|
||||||
|
qt_convert_NV21_to_ARGB32((const uchar *)data.constData(),
|
||||||
|
(quint32 *)result.bits(),
|
||||||
|
width,
|
||||||
|
height);
|
||||||
|
|
||||||
|
QTransform transform;
|
||||||
|
|
||||||
|
// Preview display of front-facing cameras is flipped horizontally, but the frame data
|
||||||
|
// we get here is not. Flip it ourselves if the camera is front-facing to match what the user
|
||||||
|
// sees on the viewfinder.
|
||||||
|
if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
|
||||||
|
transform.scale(-1, 1);
|
||||||
|
|
||||||
|
transform.rotate(rotation);
|
||||||
|
|
||||||
|
result = result.transformed(transform);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAndroidCameraSession::onNewPreviewFrame(const QByteArray &frame, int width, int height)
|
||||||
{
|
{
|
||||||
m_videoProbesMutex.lock();
|
m_videoProbesMutex.lock();
|
||||||
if (frame.size() && m_videoProbes.count()) {
|
if (frame.size() && m_videoProbes.count()) {
|
||||||
const QSize frameSize = m_camera->previewSize();
|
|
||||||
// Bytes per line should be only for the first plane. For NV21, the Y plane has 8 bits
|
// Bytes per line should be only for the first plane. For NV21, the Y plane has 8 bits
|
||||||
// per sample, so bpl == width
|
// per sample, so bpl == width
|
||||||
QVideoFrame videoFrame(new DataVideoBuffer(frame, frameSize.width()),
|
QVideoFrame videoFrame(new DataVideoBuffer(frame, width),
|
||||||
frameSize,
|
QSize(width, height),
|
||||||
QVideoFrame::Format_NV21);
|
QVideoFrame::Format_NV21);
|
||||||
foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes)
|
foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes)
|
||||||
probe->newFrameProbed(videoFrame);
|
probe->newFrameProbed(videoFrame);
|
||||||
@@ -666,35 +696,6 @@ void QAndroidCameraSession::processCapturedImage(int id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int rotation)
|
|
||||||
{
|
|
||||||
emit imageCaptured(id, prepareImageFromPreviewData(data, rotation));
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage QAndroidCameraSession::prepareImageFromPreviewData(const QByteArray &data, int rotation)
|
|
||||||
{
|
|
||||||
QSize frameSize = m_camera->previewSize();
|
|
||||||
QImage result(frameSize, QImage::Format_ARGB32);
|
|
||||||
qt_convert_NV21_to_ARGB32((const uchar *)data.constData(),
|
|
||||||
(quint32 *)result.bits(),
|
|
||||||
frameSize.width(),
|
|
||||||
frameSize.height());
|
|
||||||
|
|
||||||
QTransform transform;
|
|
||||||
|
|
||||||
// Preview display of front-facing cameras is flipped horizontally, but the frame data
|
|
||||||
// we get here is not. Flip it ourselves if the camera is front-facing to match what the user
|
|
||||||
// sees on the viewfinder.
|
|
||||||
if (m_camera->getFacing() == AndroidCamera::CameraFacingFront)
|
|
||||||
transform.scale(-1, 1);
|
|
||||||
|
|
||||||
transform.rotate(rotation);
|
|
||||||
|
|
||||||
result = result.transformed(transform);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAndroidCameraSession::onVideoOutputReady(bool ready)
|
void QAndroidCameraSession::onVideoOutputReady(bool ready)
|
||||||
{
|
{
|
||||||
if (ready && m_state == QCamera::ActiveState)
|
if (ready && m_state == QCamera::ActiveState)
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ private Q_SLOTS:
|
|||||||
void onApplicationStateChanged(Qt::ApplicationState state);
|
void onApplicationStateChanged(Qt::ApplicationState state);
|
||||||
|
|
||||||
void onCameraPictureExposed();
|
void onCameraPictureExposed();
|
||||||
void onCameraPreviewFetched(const QByteArray &preview);
|
|
||||||
void onCameraFrameFetched(const QByteArray &frame);
|
|
||||||
void onCameraPictureCaptured(const QByteArray &data);
|
void onCameraPictureCaptured(const QByteArray &data);
|
||||||
|
void onLastPreviewFrameFetched(const QByteArray &preview, int width, int height);
|
||||||
|
void onNewPreviewFrame(const QByteArray &frame, int width, int height);
|
||||||
void onCameraPreviewStarted();
|
void onCameraPreviewStarted();
|
||||||
void onCameraPreviewStopped();
|
void onCameraPreviewStopped();
|
||||||
|
|
||||||
@@ -129,8 +129,8 @@ private:
|
|||||||
void stopPreview();
|
void stopPreview();
|
||||||
|
|
||||||
void applyImageSettings();
|
void applyImageSettings();
|
||||||
void processPreviewImage(int id, const QByteArray &data, int rotation);
|
void processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation);
|
||||||
QImage prepareImageFromPreviewData(const QByteArray &data, int rotation);
|
QImage prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation);
|
||||||
void processCapturedImage(int id,
|
void processCapturedImage(int id,
|
||||||
const QByteArray &data,
|
const QByteArray &data,
|
||||||
const QSize &resolution,
|
const QSize &resolution,
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void notifyFrameFetched(JNIEnv *env, jobject, int id, jbyteArray data)
|
static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, int width, int height)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&g_cameraMapMutex);
|
QMutexLocker locker(&g_cameraMapMutex);
|
||||||
AndroidCamera *obj = g_cameraMap->value(id, 0);
|
AndroidCamera *obj = g_cameraMap->value(id, 0);
|
||||||
@@ -123,7 +123,7 @@ static void notifyFrameFetched(JNIEnv *env, jobject, int id, jbyteArray data)
|
|||||||
QByteArray bytes(arrayLength, Qt::Uninitialized);
|
QByteArray bytes(arrayLength, Qt::Uninitialized);
|
||||||
env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
|
env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data());
|
||||||
|
|
||||||
Q_EMIT obj->frameFetched(bytes);
|
Q_EMIT obj->newPreviewFrame(bytes, width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ public:
|
|||||||
Q_INVOKABLE void takePicture();
|
Q_INVOKABLE void takePicture();
|
||||||
|
|
||||||
Q_INVOKABLE void setupPreviewFrameCallback();
|
Q_INVOKABLE void setupPreviewFrameCallback();
|
||||||
Q_INVOKABLE void fetchEachFrame(bool fetch);
|
Q_INVOKABLE void notifyNewFrames(bool notify);
|
||||||
Q_INVOKABLE void fetchLastPreviewFrame();
|
Q_INVOKABLE void fetchLastPreviewFrame();
|
||||||
|
|
||||||
Q_INVOKABLE void applyParameters();
|
Q_INVOKABLE void applyParameters();
|
||||||
@@ -230,7 +230,7 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
void whiteBalanceChanged();
|
void whiteBalanceChanged();
|
||||||
|
|
||||||
void previewFetched(const QByteArray &preview);
|
void lastPreviewFrameFetched(const QByteArray &preview, int width, int height);
|
||||||
};
|
};
|
||||||
|
|
||||||
AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
|
AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
|
||||||
@@ -248,7 +248,7 @@ AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker)
|
|||||||
connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped);
|
connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped);
|
||||||
connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted);
|
connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted);
|
||||||
connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged);
|
connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged);
|
||||||
connect(d, &AndroidCameraPrivate::previewFetched, this, &AndroidCamera::previewFetched);
|
connect(d, &AndroidCameraPrivate::lastPreviewFrameFetched, this, &AndroidCamera::lastPreviewFrameFetched);
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidCamera::~AndroidCamera()
|
AndroidCamera::~AndroidCamera()
|
||||||
@@ -640,10 +640,10 @@ void AndroidCamera::setupPreviewFrameCallback()
|
|||||||
QMetaObject::invokeMethod(d, "setupPreviewFrameCallback");
|
QMetaObject::invokeMethod(d, "setupPreviewFrameCallback");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidCamera::fetchEachFrame(bool fetch)
|
void AndroidCamera::notifyNewFrames(bool notify)
|
||||||
{
|
{
|
||||||
Q_D(AndroidCamera);
|
Q_D(AndroidCamera);
|
||||||
QMetaObject::invokeMethod(d, "fetchEachFrame", Q_ARG(bool, fetch));
|
QMetaObject::invokeMethod(d, "notifyNewFrames", Q_ARG(bool, notify));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidCamera::fetchLastPreviewFrame()
|
void AndroidCamera::fetchLastPreviewFrame()
|
||||||
@@ -1337,41 +1337,32 @@ void AndroidCameraPrivate::takePicture()
|
|||||||
|
|
||||||
void AndroidCameraPrivate::setupPreviewFrameCallback()
|
void AndroidCameraPrivate::setupPreviewFrameCallback()
|
||||||
{
|
{
|
||||||
//We need to clear preview buffers queue here, but there is no method to do it
|
m_cameraListener.callMethod<void>("setupPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
|
||||||
//Though just resetting preview callback do the trick
|
|
||||||
m_camera.callMethod<void>("setPreviewCallbackWithBuffer",
|
|
||||||
"(Landroid/hardware/Camera$PreviewCallback;)V",
|
|
||||||
jobject(0));
|
|
||||||
m_cameraListener.callMethod<void>("preparePreviewBuffer", "(Landroid/hardware/Camera;)V", m_camera.object());
|
|
||||||
QJNIObjectPrivate buffer = m_cameraListener.callObjectMethod<jbyteArray>("callbackBuffer");
|
|
||||||
m_camera.callMethod<void>("addCallbackBuffer", "([B)V", buffer.object());
|
|
||||||
m_camera.callMethod<void>("setPreviewCallbackWithBuffer",
|
|
||||||
"(Landroid/hardware/Camera$PreviewCallback;)V",
|
|
||||||
m_cameraListener.object());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidCameraPrivate::fetchEachFrame(bool fetch)
|
void AndroidCameraPrivate::notifyNewFrames(bool notify)
|
||||||
{
|
{
|
||||||
m_cameraListener.callMethod<void>("fetchEachFrame", "(Z)V", fetch);
|
m_cameraListener.callMethod<void>("notifyNewFrames", "(Z)V", notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidCameraPrivate::fetchLastPreviewFrame()
|
void AndroidCameraPrivate::fetchLastPreviewFrame()
|
||||||
{
|
{
|
||||||
QJNIEnvironmentPrivate env;
|
QJNIEnvironmentPrivate env;
|
||||||
QJNIObjectPrivate data = m_cameraListener.callObjectMethod("lockAndFetchPreviewBuffer", "()[B");
|
QJNIObjectPrivate data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B");
|
||||||
if (!data.isValid()) {
|
|
||||||
m_cameraListener.callMethod<void>("unlockPreviewBuffer");
|
if (!data.isValid())
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
|
const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
|
||||||
QByteArray bytes(arrayLength, Qt::Uninitialized);
|
QByteArray bytes(arrayLength, Qt::Uninitialized);
|
||||||
env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
|
env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
|
||||||
0,
|
0,
|
||||||
arrayLength,
|
arrayLength,
|
||||||
reinterpret_cast<jbyte *>(bytes.data()));
|
reinterpret_cast<jbyte *>(bytes.data()));
|
||||||
m_cameraListener.callMethod<void>("unlockPreviewBuffer");
|
|
||||||
|
|
||||||
emit previewFetched(bytes);
|
emit lastPreviewFrameFetched(bytes,
|
||||||
|
m_cameraListener.callMethod<jint>("previewWidth"),
|
||||||
|
m_cameraListener.callMethod<jint>("previewHeight"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidCameraPrivate::applyParameters()
|
void AndroidCameraPrivate::applyParameters()
|
||||||
@@ -1416,7 +1407,7 @@ bool AndroidCamera::initJNI(JNIEnv *env)
|
|||||||
{"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
|
{"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
|
||||||
{"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
|
{"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
|
||||||
{"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
|
{"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
|
||||||
{"notifyFrameFetched", "(I[B)V", (void *)notifyFrameFetched}
|
{"notifyNewPreviewFrame", "(I[BII)V", (void *)notifyNewPreviewFrame}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (clazz && env->RegisterNatives(clazz,
|
if (clazz && env->RegisterNatives(clazz,
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ public:
|
|||||||
void takePicture();
|
void takePicture();
|
||||||
|
|
||||||
void setupPreviewFrameCallback();
|
void setupPreviewFrameCallback();
|
||||||
void fetchEachFrame(bool fetch);
|
void notifyNewFrames(bool notify);
|
||||||
void fetchLastPreviewFrame();
|
void fetchLastPreviewFrame();
|
||||||
QJNIObjectPrivate getCameraObject();
|
QJNIObjectPrivate getCameraObject();
|
||||||
|
|
||||||
@@ -177,8 +177,8 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
void pictureExposed();
|
void pictureExposed();
|
||||||
void pictureCaptured(const QByteArray &data);
|
void pictureCaptured(const QByteArray &data);
|
||||||
void previewFetched(const QByteArray &preview);
|
void lastPreviewFrameFetched(const QByteArray &preview, int width, int height);
|
||||||
void frameFetched(const QByteArray &frame);
|
void newPreviewFrame(const QByteArray &frame, int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AndroidCamera(AndroidCameraPrivate *d, QThread *worker);
|
AndroidCamera(AndroidCameraPrivate *d, QThread *worker);
|
||||||
|
|||||||
Reference in New Issue
Block a user