summaryrefslogtreecommitdiffstats
path: root/src/plugins/android
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2016-08-13 13:53:51 +0200
committerLiang Qi <liang.qi@qt.io>2016-08-13 13:53:59 +0200
commit820205e604a5f281238c23464638fdff72b969d1 (patch)
treec6f6723449d1b11e8d540752e50e3c2e11613f33 /src/plugins/android
parent03d55888942feac26054978ce4e7c6edd4611eda (diff)
parentc30eeb5b7486caa31cdb0f9279de2f78fed89c54 (diff)
Merge remote-tracking branch 'origin/5.6' into 5.7
Diffstat (limited to 'src/plugins/android')
-rw-r--r--src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java30
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.cpp82
-rw-r--r--src/plugins/android/src/common/qandroidvideooutput.h16
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcamerasession.cpp6
-rw-r--r--src/plugins/android/src/wrappers/jni/androidcamera.cpp26
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp4
-rw-r--r--src/plugins/android/src/wrappers/jni/androidsurfacetexture.h4
7 files changed, 105 insertions, 63 deletions
diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java
index 57700cd75..49cb0a947 100644
--- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java
+++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java
@@ -57,6 +57,7 @@ public class QtCameraListener implements Camera.ShutterCallback,
private int m_cameraId = -1;
private boolean m_notifyNewFrames = false;
+ private boolean m_notifyWhenFrameAvailable = false;
private byte[][] m_previewBuffers = null;
private byte[] m_lastPreviewBuffer = null;
private Camera.Size m_previewSize = null;
@@ -73,6 +74,11 @@ public class QtCameraListener implements Camera.ShutterCallback,
m_notifyNewFrames = notify;
}
+ public void notifyWhenFrameAvailable(boolean notify)
+ {
+ m_notifyWhenFrameAvailable = notify;
+ }
+
public byte[] lastPreviewBuffer()
{
return m_lastPreviewBuffer;
@@ -104,11 +110,16 @@ public class QtCameraListener implements Camera.ShutterCallback,
return m_previewBytesPerLine;
}
+ public void clearPreviewCallback(Camera camera)
+ {
+ camera.setPreviewCallbackWithBuffer(null);
+ }
+
public void setupPreviewCallback(Camera camera)
{
// Clear previous callback (also clears added buffers)
+ clearPreviewCallback(camera);
m_lastPreviewBuffer = null;
- camera.setPreviewCallbackWithBuffer(null);
final Camera.Parameters params = camera.getParameters();
m_previewSize = params.getPreviewSize();
@@ -164,11 +175,17 @@ public class QtCameraListener implements Camera.ShutterCallback,
m_lastPreviewBuffer = data;
- if (data != null && m_notifyNewFrames) {
- notifyNewPreviewFrame(m_cameraId, data,
- m_previewSize.width, m_previewSize.height,
- m_previewFormat,
- m_previewBytesPerLine);
+ if (data != null) {
+ if (m_notifyWhenFrameAvailable) {
+ m_notifyWhenFrameAvailable = false;
+ notifyFrameAvailable(m_cameraId);
+ }
+ if (m_notifyNewFrames) {
+ notifyNewPreviewFrame(m_cameraId, data,
+ m_previewSize.width, m_previewSize.height,
+ m_previewFormat,
+ m_previewBytesPerLine);
+ }
}
}
@@ -195,4 +212,5 @@ public class QtCameraListener implements Camera.ShutterCallback,
private static native void notifyPictureCaptured(int id, byte[] data);
private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height,
int pixelFormat, int bytesPerLine);
+ private static native void notifyFrameAvailable(int id);
}
diff --git a/src/plugins/android/src/common/qandroidvideooutput.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp
index a5cd3580b..1d0df27f2 100644
--- a/src/plugins/android/src/common/qandroidvideooutput.cpp
+++ b/src/plugins/android/src/common/qandroidvideooutput.cpp
@@ -66,6 +66,22 @@ static const GLfloat g_texture_data[] = {
0.f, 1.f
};
+void OpenGLResourcesDeleter::deleteTextureHelper(quint32 id)
+{
+ if (id != 0)
+ glDeleteTextures(1, &id);
+}
+
+void OpenGLResourcesDeleter::deleteFboHelper(void *fbo)
+{
+ delete reinterpret_cast<QOpenGLFramebufferObject *>(fbo);
+}
+
+void OpenGLResourcesDeleter::deleteShaderProgramHelper(void *prog)
+{
+ delete reinterpret_cast<QOpenGLShaderProgram *>(prog);
+}
+
class AndroidTextureVideoBuffer : public QAbstractVideoBuffer
{
@@ -130,40 +146,6 @@ private:
QImage m_image;
};
-
-class OpenGLResourcesDeleter : public QObject
-{
-public:
- OpenGLResourcesDeleter()
- : m_textureID(0)
- , m_fbo(0)
- , m_program(0)
- { }
-
- ~OpenGLResourcesDeleter()
- {
- glDeleteTextures(1, &m_textureID);
- delete m_fbo;
- delete m_program;
- }
-
- void setTexture(quint32 id) {
- if (m_textureID)
- glDeleteTextures(1, &m_textureID);
-
- m_textureID = id;
- }
-
- void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; }
- void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; }
-
-private:
- quint32 m_textureID;
- QOpenGLFramebufferObject *m_fbo;
- QOpenGLShaderProgram *m_program;
-};
-
-
QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
: QAndroidVideoOutput(parent)
, m_surface(0)
@@ -171,7 +153,6 @@ QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent)
, m_externalTex(0)
, m_fbo(0)
, m_program(0)
- , m_glDeleter(0)
, m_surfaceTextureCanAttachToContext(QtAndroidPrivate::androidSdkVersion() >= 16)
{
@@ -181,8 +162,12 @@ QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput()
{
clearSurfaceTexture();
- if (m_glDeleter)
+ if (!m_glDeleter.isNull()) { // Make sure all of these are deleted on the render thread.
+ m_glDeleter->deleteFbo(m_fbo);
+ m_glDeleter->deleteShaderProgram(m_program);
+ m_glDeleter->deleteTexture(m_externalTex);
m_glDeleter->deleteLater();
+ }
}
QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const
@@ -229,8 +214,7 @@ bool QAndroidTextureVideoOutput::initSurfaceTexture()
// for the GL render thread to call us back to do it.
if (QOpenGLContext::currentContext()) {
glGenTextures(1, &m_externalTex);
- m_glDeleter = new OpenGLResourcesDeleter;
- m_glDeleter->setTexture(m_externalTex);
+ m_glDeleter.reset(new OpenGLResourcesDeleter);
} else if (!m_externalTex) {
return false;
}
@@ -245,10 +229,9 @@ bool QAndroidTextureVideoOutput::initSurfaceTexture()
} else {
delete m_surfaceTexture;
m_surfaceTexture = 0;
- if (m_glDeleter)
- m_glDeleter->deleteLater();
+ if (!m_glDeleter.isNull())
+ m_glDeleter->deleteTexture(m_externalTex);
m_externalTex = 0;
- m_glDeleter = 0;
}
return m_surfaceTexture != 0;
@@ -263,8 +246,14 @@ void QAndroidTextureVideoOutput::clearSurfaceTexture()
}
// Also reset the attached OpenGL texture
- if (m_surfaceTextureCanAttachToContext)
+ // Note: The Android SurfaceTexture class does not release the texture on deletion,
+ // only if detachFromGLContext() called (API level >= 16), so we'll do it manually,
+ // on the render thread.
+ if (m_surfaceTextureCanAttachToContext) {
+ if (!m_glDeleter.isNull())
+ m_glDeleter->deleteTexture(m_externalTex);
m_externalTex = 0;
+ }
}
AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture()
@@ -394,19 +383,17 @@ void QAndroidTextureVideoOutput::createGLResources()
Q_ASSERT(QOpenGLContext::currentContext() != NULL);
if (!m_glDeleter)
- m_glDeleter = new OpenGLResourcesDeleter;
+ m_glDeleter.reset(new OpenGLResourcesDeleter);
if (m_surfaceTextureCanAttachToContext && !m_externalTex) {
m_surfaceTexture->detachFromGLContext();
glGenTextures(1, &m_externalTex);
m_surfaceTexture->attachToGLContext(m_externalTex);
- m_glDeleter->setTexture(m_externalTex);
}
if (!m_fbo || m_fbo->size() != m_nativeSize) {
delete m_fbo;
m_fbo = new QOpenGLFramebufferObject(m_nativeSize);
- m_glDeleter->setFbo(m_fbo);
}
if (!m_program) {
@@ -437,8 +424,6 @@ void QAndroidTextureVideoOutput::createGLResources()
m_program->bindAttributeLocation("vertexCoordsArray", 0);
m_program->bindAttributeLocation("textureCoordArray", 1);
m_program->link();
-
- m_glDeleter->setShaderProgram(m_program);
}
}
@@ -448,8 +433,7 @@ void QAndroidTextureVideoOutput::customEvent(QEvent *e)
// This is running in the render thread (OpenGL enabled)
if (!m_surfaceTextureCanAttachToContext && !m_externalTex) {
glGenTextures(1, &m_externalTex);
- m_glDeleter = new OpenGLResourcesDeleter; // will cleanup GL resources in the correct thread
- m_glDeleter->setTexture(m_externalTex);
+ m_glDeleter.reset(new OpenGLResourcesDeleter); // We'll use this to cleanup GL resources in the correct thread
emit readyChanged(true);
}
}
diff --git a/src/plugins/android/src/common/qandroidvideooutput.h b/src/plugins/android/src/common/qandroidvideooutput.h
index 67bac7052..a12db75a2 100644
--- a/src/plugins/android/src/common/qandroidvideooutput.h
+++ b/src/plugins/android/src/common/qandroidvideooutput.h
@@ -50,7 +50,6 @@ class AndroidSurfaceTexture;
class AndroidSurfaceHolder;
class QOpenGLFramebufferObject;
class QOpenGLShaderProgram;
-class OpenGLResourcesDeleter;
class QAbstractVideoSurface;
class QAndroidVideoOutput : public QObject
@@ -75,6 +74,19 @@ protected:
QAndroidVideoOutput(QObject *parent) : QObject(parent) { }
};
+class OpenGLResourcesDeleter : public QObject
+{
+ Q_OBJECT
+public:
+ void deleteTexture(quint32 id) { QMetaObject::invokeMethod(this, "deleteTextureHelper", Qt::AutoConnection, Q_ARG(quint32, id)); }
+ void deleteFbo(QOpenGLFramebufferObject *fbo) { QMetaObject::invokeMethod(this, "deleteFboHelper", Qt::AutoConnection, Q_ARG(void *, fbo)); }
+ void deleteShaderProgram(QOpenGLShaderProgram *prog) { QMetaObject::invokeMethod(this, "deleteShaderProgramHelper", Qt::AutoConnection, Q_ARG(void *, prog)); }
+
+private:
+ Q_INVOKABLE void deleteTextureHelper(quint32 id);
+ Q_INVOKABLE void deleteFboHelper(void *fbo);
+ Q_INVOKABLE void deleteShaderProgramHelper(void *prog);
+};
class QAndroidTextureVideoOutput : public QAndroidVideoOutput
{
@@ -114,7 +126,7 @@ private:
quint32 m_externalTex;
QOpenGLFramebufferObject *m_fbo;
QOpenGLShaderProgram *m_program;
- OpenGLResourcesDeleter *m_glDeleter;
+ QScopedPointer<OpenGLResourcesDeleter, QScopedPointerDeleteLater> m_glDeleter;
bool m_surfaceTextureCanAttachToContext;
diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
index 0b082b02b..8663f8c5f 100644
--- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
@@ -684,6 +684,9 @@ void QAndroidCameraSession::onCameraTakePictureFailed()
{
emit imageCaptureError(m_currentImageCaptureId, QCameraImageCapture::ResourceError,
tr("Failed to capture image"));
+
+ // Preview needs to be restarted and the preview call back must be setup again
+ m_camera->startPreview();
}
void QAndroidCameraSession::onCameraPictureExposed()
@@ -697,6 +700,9 @@ void QAndroidCameraSession::onCameraPictureExposed()
void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
{
+ if (m_captureCanceled)
+ return;
+
QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
m_currentImageCaptureId,
frame,
diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
index 3295e4d33..76d3ffb44 100644
--- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp
@@ -152,6 +152,16 @@ static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data,
Q_EMIT (*it)->newPreviewFrame(frame);
}
+static void notifyFrameAvailable(JNIEnv *, jobject, int id)
+{
+ QReadLocker locker(rwLock);
+ const auto it = cameras->constFind(id);
+ if (Q_UNLIKELY(it == cameras->cend()))
+ return;
+
+ (*it)->fetchLastPreviewFrame();
+}
+
class AndroidCameraPrivate : public QObject
{
Q_OBJECT
@@ -1540,6 +1550,9 @@ void AndroidCameraPrivate::stopPreview()
{
QJNIEnvironmentPrivate env;
+ // cancel any pending new frame notification
+ m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", false);
+
m_camera.callMethod<void>("stopPreview");
exceptionCheckAndClear(env);
@@ -1550,6 +1563,11 @@ void AndroidCameraPrivate::takePicture()
{
QJNIEnvironmentPrivate env;
+ // We must clear the preview callback before calling takePicture(), otherwise the call will
+ // block and the camera server will be frozen until the next device restart...
+ // That problem only happens on some devices and on the emulator
+ m_cameraListener.callMethod<void>("clearPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object());
+
m_camera.callMethod<void>("takePicture", "(Landroid/hardware/Camera$ShutterCallback;"
"Landroid/hardware/Camera$PictureCallback;"
"Landroid/hardware/Camera$PictureCallback;)V",
@@ -1576,8 +1594,11 @@ void AndroidCameraPrivate::fetchLastPreviewFrame()
QJNIEnvironmentPrivate env;
QJNIObjectPrivate data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B");
- if (!data.isValid())
+ if (!data.isValid()) {
+ // If there's no buffer received yet, retry when the next one arrives
+ m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", true);
return;
+ }
const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
if (arrayLength == 0)
@@ -1643,7 +1664,8 @@ bool AndroidCamera::initJNI(JNIEnv *env)
{"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete},
{"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed},
{"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured},
- {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame}
+ {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame},
+ {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable}
};
if (clazz && env->RegisterNatives(clazz,
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
index df09d0819..764315acd 100644
--- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
+++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp
@@ -62,7 +62,7 @@ static void notifyFrameAvailable(JNIEnv* , jobject, jlong id)
Q_EMIT obj->frameAvailable();
}
-AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName)
+AndroidSurfaceTexture::AndroidSurfaceTexture(quint32 texName)
: QObject()
{
Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *));
@@ -163,7 +163,7 @@ jobject AndroidSurfaceTexture::surfaceHolder()
return m_surfaceHolder.object();
}
-void AndroidSurfaceTexture::attachToGLContext(int texName)
+void AndroidSurfaceTexture::attachToGLContext(quint32 texName)
{
if (QtAndroidPrivate::androidSdkVersion() < 16 || !m_surfaceTexture.isValid())
return;
diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
index 0a271287a..911711774 100644
--- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
+++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h
@@ -51,7 +51,7 @@ class AndroidSurfaceTexture : public QObject
{
Q_OBJECT
public:
- explicit AndroidSurfaceTexture(unsigned int texName);
+ explicit AndroidSurfaceTexture(quint32 texName);
~AndroidSurfaceTexture();
jobject surfaceTexture();
@@ -63,7 +63,7 @@ public:
void release(); // API level 14
void updateTexImage();
- void attachToGLContext(int texName); // API level 16
+ void attachToGLContext(quint32 texName); // API level 16
void detachFromGLContext(); // API level 16
static bool initJNI(JNIEnv *env);