summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNils Petter Skålerud <[email protected]>2025-06-18 13:47:07 +0200
committerNils Petter Skålerud <[email protected]>2025-07-12 09:16:21 +0200
commitd518af6d4a1865d6c9986a9d2e306a9f77cec853 (patch)
tree93d8134b9077d8e0400642c0c572e33d027f61ba
parent08cc6cc2f590fa366752e817e5502ea971a50f20 (diff)
Android: Allow QImageCapture to recover from camera still photo failingHEADdev
Currently, if a camera still photo capture fails, the QImageCapture object is left waiting for an image indefinitely. Additionally, it currently does not support queuing new image requests. This means that whenever a still photo fails, it becomes impossible to take new images using the QImageCapture object. This patch does the following: - Introduces internal methods to QFFmpegImageCapture to allow removing a pending image from the queue - Introduces a new QAndroidCamera event to report a failed still photo image capture - Connects the two This allows a QImageCapture to stay in working state even when still photo capture fails. Pick-to: 6.10 6.9 6.8 Change-Id: I19b9b2d9e24a376f1372ec061b3a6213d1c58adc Reviewed-by: Artem Dyomin <[email protected]>
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java16
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidcamera.cpp47
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidcamera_p.h6
-rw-r--r--src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp14
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp16
-rw-r--r--src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h2
6 files changed, 82 insertions, 19 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java
index eb913032f..0454ffe30 100644
--- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java
+++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java
@@ -30,6 +30,12 @@ import org.qtproject.qt.android.UsedFromNativeCode;
@TargetApi(23)
class QtCamera2 {
+ // Should be called if an on-going still photo capture has failed to finish.
+ // This lets us submit an appropriate error and notify QImageCapture that there will
+ // be no photo emitted.
+ // TODO: In the future we should send a more descriptive error message to QImageCapture, and
+ // and pass it as a parameter here.
+ native void onStillPhotoCaptureFailed(String cameraId);
CameraDevice mCameraDevice = null;
QtVideoDeviceManager mVideoDeviceManager = null;
@@ -446,7 +452,7 @@ class QtCamera2 {
private void onCaptureFailureEvent() {
mShouldProcessIncomingEvents = false;
- // TODO: Signal to QImageCapture that the capture failed.
+ onStillPhotoCaptureFailed(mCameraId);
synchronized (mSyncedMembers) {
mSyncedMembers.mIsTakingStillPhoto = false;
@@ -560,7 +566,7 @@ class QtCamera2 {
CaptureRequest request,
CaptureFailure failure)
{
- // TODO: Signal to QImageCapture that the capture failed
+ onStillPhotoCaptureFailed(mCameraId);
synchronized (mSyncedMembers) {
mSyncedMembers.mIsTakingStillPhoto = false;
}
@@ -672,8 +678,10 @@ class QtCamera2 {
submitNewStillPhotoCapture(cameraSettings);
} catch (CameraAccessException e) {
Log.w("QtCamera2", "Cannot get access to the camera: " + e);
- // TODO: If we fail to start the still photo capture, then we should report back to the
- // QImageCapture to signal an error on the capture ID, and try to return to previewing.
+ e.printStackTrace();
+ onStillPhotoCaptureFailed(mCameraId);
+ // TODO: Try to go back to previewing if applicable. If that fails too, shut down
+ // camera session and report QCamera as inactive.
}
}
diff --git a/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp b/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp
index 8940f95fd..edc301f98 100644
--- a/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp
+++ b/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp
@@ -749,6 +749,16 @@ void QAndroidCamera::onCaptureSessionFailed(int reason, long frameNumber)
.arg(reason));
}
+// Called by Java background thread if the on-going still-photo capture fails.
+void QAndroidCamera::onStillPhotoCaptureFailed()
+{
+ // TODO: Emit a more descriptive error signal. At the time of writing, there is no
+ // suitable QImageCapture::Error enumerator for this scenario.
+ emit onImageCaptureFailed(
+ QImageCapture::Error::ResourceError,
+ QStringLiteral("Unknown error"));
+}
+
} // namespace QFFmpeg
// JNI logic
@@ -868,23 +878,34 @@ static void onCaptureSessionFailed(JNIEnv *env, jobject obj, jstring cameraId, j
}
Q_DECLARE_JNI_NATIVE_METHOD(onCaptureSessionFailed)
+static void onStillPhotoCaptureFailed(JNIEnv *env, jobject obj, jstring cameraId)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ GET_CAMERA(cameraId);
+
+ camera->onStillPhotoCaptureFailed();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(onStillPhotoCaptureFailed)
+
bool QFFmpeg::QAndroidCamera::registerNativeMethods()
{
static const bool registered = []() {
return QJniEnvironment().registerNativeMethods(
- QtJniTypes::Traits<QtJniTypes::QtCamera2>::className(),
- {
- Q_JNI_NATIVE_METHOD(onCameraOpened),
- Q_JNI_NATIVE_METHOD(onCameraDisconnect),
- Q_JNI_NATIVE_METHOD(onCameraError),
- Q_JNI_NATIVE_METHOD(onCaptureSessionConfigured),
- Q_JNI_NATIVE_METHOD(onCaptureSessionConfigureFailed),
- Q_JNI_NATIVE_METHOD(onCaptureSessionFailed),
- Q_JNI_NATIVE_METHOD(onPreviewFrameAvailable),
- Q_JNI_NATIVE_METHOD(onStillPhotoAvailable),
- Q_JNI_NATIVE_METHOD(onSessionActive),
- Q_JNI_NATIVE_METHOD(onSessionClosed),
- });
+ QtJniTypes::Traits<QtJniTypes::QtCamera2>::className(),
+ {
+ Q_JNI_NATIVE_METHOD(onCameraOpened),
+ Q_JNI_NATIVE_METHOD(onCameraDisconnect),
+ Q_JNI_NATIVE_METHOD(onCameraError),
+ Q_JNI_NATIVE_METHOD(onCaptureSessionConfigured),
+ Q_JNI_NATIVE_METHOD(onCaptureSessionConfigureFailed),
+ Q_JNI_NATIVE_METHOD(onCaptureSessionFailed),
+ Q_JNI_NATIVE_METHOD(onPreviewFrameAvailable),
+ Q_JNI_NATIVE_METHOD(onStillPhotoAvailable),
+ Q_JNI_NATIVE_METHOD(onSessionActive),
+ Q_JNI_NATIVE_METHOD(onSessionClosed),
+ Q_JNI_NATIVE_METHOD(onStillPhotoCaptureFailed),
+ });
}();
return registered;
}
diff --git a/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h b/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h
index 43c10f2f2..4bd16979a 100644
--- a/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h
+++ b/src/plugins/multimedia/ffmpeg/qandroidcamera_p.h
@@ -20,6 +20,8 @@
#include <QtCore/qjniobject.h>
#include <QtCore/qobject.h>
+#include <QtMultimedia/qimagecapture.h>
+
#include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
QT_BEGIN_NAMESPACE
@@ -81,9 +83,11 @@ public slots:
void onCaptureSessionFailed(int reason, long frameNumber);
void onSessionActive();
void onSessionClosed();
+ void onStillPhotoCaptureFailed();
-Q_SIGNALS:
+signals:
void onStillPhotoCaptured(const QVideoFrame&);
+ void onImageCaptureFailed(QImageCapture::Error error, const QString &errMsg);
private:
bool isActivating() const { return m_state != State::Closed; }
diff --git a/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp b/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp
index 179412808..f9ad2e869 100644
--- a/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp
+++ b/src/plugins/multimedia/ffmpeg/qandroidimagecapture.cpp
@@ -39,7 +39,19 @@ void QAndroidImageCapture::setupVideoSourceConnections()
&QAndroidCamera::onStillPhotoCaptured,
this,
&QAndroidImageCapture::newVideoFrame);
- } else
+
+ // The backend might call onImageCaptureFailed before the call to
+ // QAndroidImageCapture::doCapture() is finished and returns the request id to the user.
+ // Therefore we want to use queued connection for this to make sure any errors are raised
+ // after that function ends.
+ connect(
+ androidCamera,
+ &QAndroidCamera::onImageCaptureFailed,
+ this,
+ &QFFmpegImageCapture::cancelPendingImage,
+ Qt::QueuedConnection);
+ }
+ else
QFFmpegImageCapture::setupVideoSourceConnections();
}
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
index 5ddb0f022..8e869ddba 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture.cpp
@@ -135,6 +135,22 @@ void QFFmpegImageCapture::setCaptureSession(QPlatformMediaCaptureSession *sessio
onVideoSourceChanged();
}
+void QFFmpegImageCapture::cancelPendingImage(QImageCapture::Error error, const QString &errorMsg)
+{
+ if (m_pendingImages.empty()) {
+ qCDebug(qLcImageCapture) <<
+ "QImageCapture backend received an event to cancel a pending capture, "
+ "but no captures are pending. Most likely an internal Qt bug.";
+ return;
+ }
+
+ PendingImage cancelledImage = m_pendingImages.dequeue();
+
+ emit QPlatformImageCapture::error(cancelledImage.id, error, errorMsg);
+
+ updateReadyForCapture();
+}
+
void QFFmpegImageCapture::updateReadyForCapture()
{
const bool ready = m_session && m_pendingImages.size() < MaxPendingImagesCount && m_videoSource
diff --git a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
index c5197156a..5c6d4fc5f 100644
--- a/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
+++ b/src/plugins/multimedia/ffmpeg/qffmpegimagecapture_p.h
@@ -40,6 +40,8 @@ public:
void setCaptureSession(QPlatformMediaCaptureSession *session);
+ void cancelPendingImage(QImageCapture::Error error, const QString &errorMsg);
+
protected:
virtual int doCapture(const QString &fileName);
virtual void setupVideoSourceConnections();