diff options
author | Nils Petter Skålerud <[email protected]> | 2025-06-18 13:47:07 +0200 |
---|---|---|
committer | Nils Petter Skålerud <[email protected]> | 2025-07-12 09:16:21 +0200 |
commit | d518af6d4a1865d6c9986a9d2e306a9f77cec853 (patch) | |
tree | 93d8134b9077d8e0400642c0c572e33d027f61ba | |
parent | 08cc6cc2f590fa366752e817e5502ea971a50f20 (diff) |
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]>
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(); |