Android: fix QMediaRecorder crashing the camera server on some devices.
Some devices require MediaRecorder.setPreviewDisplay() to always be called, even though the Android doc says otherwise. Task-number: QTBUG-37837 Change-Id: I1e9b56f06e7c41bdf684f93b5ec7635f8ae9f379 Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
@@ -68,6 +68,7 @@ public:
|
||||
void setCaptureMode(QCamera::CaptureModes mode);
|
||||
bool isCaptureModeSupported(QCamera::CaptureModes mode) const;
|
||||
|
||||
QAndroidVideoOutput *videoOutput() const { return m_videoOutput; }
|
||||
void setVideoOutput(QAndroidVideoOutput *output);
|
||||
void adjustViewfinderSize(const QSize &captureSize, bool restartPreview = true);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "qandroidcamerasession.h"
|
||||
#include "androidmultimediautils.h"
|
||||
#include "qandroidmultimediautils.h"
|
||||
#include "qandroidvideooutput.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -217,6 +218,20 @@ void QAndroidCaptureSession::start()
|
||||
m_usedOutputLocation = QUrl::fromLocalFile(filePath);
|
||||
m_mediaRecorder->setOutputFile(filePath);
|
||||
|
||||
// Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay()
|
||||
// is not necessary when the Camera already has a Surface, it doesn't actually work on some
|
||||
// devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare()
|
||||
// and start() if MediaRecorder.setPreviewDispaly() is not called.
|
||||
if (m_cameraSession) {
|
||||
// When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same
|
||||
// one that is set on the Camera or it will crash, hence the reset().
|
||||
m_cameraSession->videoOutput()->reset();
|
||||
if (m_cameraSession->videoOutput()->surfaceTexture())
|
||||
m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture());
|
||||
else if (m_cameraSession->videoOutput()->surfaceHolder())
|
||||
m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder());
|
||||
}
|
||||
|
||||
if (!m_mediaRecorder->prepare()) {
|
||||
emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder."));
|
||||
restartViewfinder();
|
||||
@@ -412,13 +427,23 @@ void QAndroidCaptureSession::applySettings()
|
||||
|
||||
void QAndroidCaptureSession::updateViewfinder()
|
||||
{
|
||||
m_cameraSession->camera()->stopPreview();
|
||||
m_cameraSession->camera()->stopPreviewSynchronous();
|
||||
m_cameraSession->adjustViewfinderSize(m_videoSettings.resolution(), false);
|
||||
}
|
||||
|
||||
void QAndroidCaptureSession::restartViewfinder()
|
||||
{
|
||||
m_cameraSession->camera()->reconnect();
|
||||
|
||||
// This is not necessary on most devices, but it crashes on some if we don't stop the
|
||||
// preview and reset the preview display on the camera when recording is over.
|
||||
m_cameraSession->camera()->stopPreviewSynchronous();
|
||||
m_cameraSession->videoOutput()->reset();
|
||||
if (m_cameraSession->videoOutput()->surfaceTexture())
|
||||
m_cameraSession->camera()->setPreviewTexture(m_cameraSession->videoOutput()->surfaceTexture());
|
||||
else if (m_cameraSession->videoOutput()->surfaceHolder())
|
||||
m_cameraSession->camera()->setPreviewDisplay(m_cameraSession->videoOutput()->surfaceHolder());
|
||||
|
||||
m_cameraSession->camera()->startPreview();
|
||||
m_cameraSession->setReadyForCapture(true);
|
||||
}
|
||||
|
||||
@@ -744,6 +744,12 @@ void AndroidCamera::stopPreview()
|
||||
QMetaObject::invokeMethod(d, "stopPreview");
|
||||
}
|
||||
|
||||
void AndroidCamera::stopPreviewSynchronous()
|
||||
{
|
||||
Q_D(AndroidCamera);
|
||||
QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
|
||||
AndroidCameraPrivate::AndroidCameraPrivate()
|
||||
: QObject(),
|
||||
m_parametersMutex(QMutex::Recursive)
|
||||
|
||||
@@ -155,6 +155,7 @@ public:
|
||||
|
||||
void startPreview();
|
||||
void stopPreview();
|
||||
void stopPreviewSynchronous();
|
||||
|
||||
void takePicture();
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "androidmediarecorder.h"
|
||||
|
||||
#include "androidcamera.h"
|
||||
#include "androidsurfacetexture.h"
|
||||
#include "androidsurfaceview.h"
|
||||
#include <QtCore/private/qjni_p.h>
|
||||
#include <qmap.h>
|
||||
|
||||
@@ -339,6 +341,41 @@ void AndroidMediaRecorder::setOutputFile(const QString &path)
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture)
|
||||
{
|
||||
QJNIEnvironmentPrivate env;
|
||||
m_mediaRecorder.callMethod<void>("setPreviewDisplay",
|
||||
"(Landroid/view/Surface;)V",
|
||||
texture->surface());
|
||||
if (env->ExceptionCheck()) {
|
||||
#ifdef QT_DEBUG
|
||||
env->ExceptionDescribe();
|
||||
#endif
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder)
|
||||
{
|
||||
QJNIEnvironmentPrivate env;
|
||||
QJNIObjectPrivate surfaceHolder(holder->surfaceHolder());
|
||||
QJNIObjectPrivate surface = surfaceHolder.callObjectMethod("getSurface",
|
||||
"()Landroid/view/Surface;");
|
||||
if (!surface.isValid())
|
||||
return;
|
||||
|
||||
m_mediaRecorder.callMethod<void>("setPreviewDisplay",
|
||||
"(Landroid/view/Surface;)V",
|
||||
surface.object());
|
||||
if (env->ExceptionCheck()) {
|
||||
#ifdef QT_DEBUG
|
||||
env->ExceptionDescribe();
|
||||
#endif
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AndroidMediaRecorder::initJNI(JNIEnv *env)
|
||||
{
|
||||
jclass clazz = QJNIEnvironmentPrivate::findClass(QtMediaRecorderListenerClassName,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class AndroidCamera;
|
||||
class AndroidSurfaceTexture;
|
||||
class AndroidSurfaceHolder;
|
||||
|
||||
class AndroidCamcorderProfile
|
||||
{
|
||||
@@ -149,6 +151,9 @@ public:
|
||||
void setOutputFormat(OutputFormat format);
|
||||
void setOutputFile(const QString &path);
|
||||
|
||||
void setSurfaceTexture(AndroidSurfaceTexture *texture);
|
||||
void setSurfaceHolder(AndroidSurfaceHolder *holder);
|
||||
|
||||
static bool initJNI(JNIEnv *env);
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
@@ -78,8 +78,8 @@ AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName)
|
||||
|
||||
AndroidSurfaceTexture::~AndroidSurfaceTexture()
|
||||
{
|
||||
if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surfaceView.isValid())
|
||||
m_surfaceView.callMethod<void>("release");
|
||||
if (QtAndroidPrivate::androidSdkVersion() > 13 && m_surface.isValid())
|
||||
m_surface.callMethod<void>("release");
|
||||
|
||||
if (m_surfaceTexture.isValid()) {
|
||||
release();
|
||||
@@ -124,21 +124,23 @@ jobject AndroidSurfaceTexture::surfaceTexture()
|
||||
return m_surfaceTexture.object();
|
||||
}
|
||||
|
||||
jobject AndroidSurfaceTexture::surfaceView()
|
||||
jobject AndroidSurfaceTexture::surface()
|
||||
{
|
||||
return m_surfaceView.object();
|
||||
if (!m_surface.isValid()) {
|
||||
m_surface = QJNIObjectPrivate("android/view/Surface",
|
||||
"(Landroid/graphics/SurfaceTexture;)V",
|
||||
m_surfaceTexture.object());
|
||||
}
|
||||
|
||||
return m_surface.object();
|
||||
}
|
||||
|
||||
jobject AndroidSurfaceTexture::surfaceHolder()
|
||||
{
|
||||
if (!m_surfaceHolder.isValid()) {
|
||||
m_surfaceView = QJNIObjectPrivate("android/view/Surface",
|
||||
"(Landroid/graphics/SurfaceTexture;)V",
|
||||
m_surfaceTexture.object());
|
||||
|
||||
m_surfaceHolder = QJNIObjectPrivate("org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder",
|
||||
"(Landroid/view/Surface;)V",
|
||||
m_surfaceView.object());
|
||||
surface());
|
||||
}
|
||||
|
||||
return m_surfaceHolder.object();
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
|
||||
int textureID() const { return m_texID; }
|
||||
jobject surfaceTexture();
|
||||
jobject surfaceView();
|
||||
jobject surface();
|
||||
jobject surfaceHolder();
|
||||
inline bool isValid() const { return m_surfaceTexture.isValid(); }
|
||||
|
||||
@@ -66,7 +66,7 @@ Q_SIGNALS:
|
||||
private:
|
||||
int m_texID;
|
||||
QJNIObjectPrivate m_surfaceTexture;
|
||||
QJNIObjectPrivate m_surfaceView;
|
||||
QJNIObjectPrivate m_surface;
|
||||
QJNIObjectPrivate m_surfaceHolder;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user