3package org.qtproject.qt.android.multimedia.qffmpeg;
5import android.annotation.SuppressLint;
6import android.annotation.TargetApi;
7import android.content.Context;
8import android.graphics.Rect;
9import android.hardware.camera2.CameraAccessException;
10import android.hardware.camera2.CameraCaptureSession;
11import android.hardware.camera2.CameraDevice;
12import android.hardware.camera2.CameraMetadata;
13import android.hardware.camera2.CameraManager;
14import android.hardware.camera2.CaptureFailure;
15import android.hardware.camera2.CaptureResult;
16import android.hardware.camera2.CaptureRequest;
17import android.hardware.camera2.TotalCaptureResult;
18import android.media.Image;
19import android.media.ImageReader;
20import android.os.Handler;
21import android.os.HandlerThread;
22import android.util.Log;
23import android.util.Range;
24import android.view.Surface;
25import java.lang.Thread;
26import java.util.ArrayList;
29import org.qtproject.qt.android.UsedFromNativeCode;
42 CameraDevice mCameraDevice =
null;
43 QtVideoDeviceManager mVideoDeviceManager =
null;
45 HandlerThread mBackgroundThread;
49 ImageReader mPreviewImageReader =
null;
51 ImageReader mStillPhotoImageReader =
null;
52 CameraManager mCameraManager;
53 CameraCaptureSession mCaptureSession;
54 CaptureRequest.Builder mPreviewRequestBuilder;
55 CaptureRequest mPreviewRequest;
59 private static int MaxNumberFrames = 12;
66 boolean mIsStarted =
false;
68 boolean mIsTakingStillPhoto =
false;
70 private CameraSettings mCameraSettings =
new CameraSettings();
72 final SyncedMembers mSyncedMembers =
new SyncedMembers();
76 public void resetControlProperties() {
77 synchronized (mSyncedMembers) {
78 mSyncedMembers.mCameraSettings =
new CameraSettings();
83 private CameraSettings atomicCameraSettingsCopy() {
84 synchronized (mSyncedMembers) {
85 return new CameraSettings(mSyncedMembers.mCameraSettings);
89 QtExifDataHandler mExifDataHandler =
null;
95 CameraDeviceStateCallback mStateCallback =
new CameraDeviceStateCallback(
this);
100 CameraCaptureSessionStateCallback mCaptureStateCallback =
new CameraCaptureSessionStateCallback(
this);
108 class PreviewCaptureSessionCallback
extends CameraCaptureSession.CaptureCallback {
110 public void onCaptureFailed(
111 CameraCaptureSession session,
113 CaptureFailure failure)
115 super.onCaptureFailed(session,
request, failure);
123 PreviewCaptureSessionCallback mPreviewCaptureCallback =
new PreviewCaptureSessionCallback();
126 mCameraManager = (CameraManager)
context.getSystemService(
Context.CAMERA_SERVICE);
127 mVideoDeviceManager =
new QtVideoDeviceManager(
context);
128 startBackgroundThread();
131 void startBackgroundThread() {
132 mBackgroundThread =
new HandlerThread(
"CameraBackground");
133 mBackgroundThread.start();
134 mBackgroundHandler =
new Handler(mBackgroundThread.getLooper());
138 void stopBackgroundThread() {
139 mBackgroundThread.quitSafely();
141 mBackgroundThread.join();
142 mBackgroundThread =
null;
143 mBackgroundHandler =
null;
144 }
catch (Exception e) {
149 @SuppressLint(
"MissingPermission")
152 mCameraId = cameraId;
153 mCameraManager.openCamera(cameraId,mStateCallback,mBackgroundHandler);
155 }
catch (Exception e){
165 ImageReader.OnImageAvailableListener mOnStillPhotoAvailableListener =
new ImageReader.OnImageAvailableListener() {
167 public void onImageAvailable(ImageReader reader) {
168 QtCamera2.this.onStillPhotoAvailable(mCameraId,
reader.acquireLatestImage());
175 ImageReader.OnImageAvailableListener mOnPreviewImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
177 public void onImageAvailable(ImageReader reader) {
181 QtCamera2.this.onPreviewFrameAvailable(mCameraId,
img);
182 }
catch (IllegalStateException e) {
185 Log.e(
LOG_TAG,
"Image processing taking too long. Let's wait 0,5s more " + e);
188 QtCamera2.this.onPreviewFrameAvailable(mCameraId,
reader.acquireLatestImage());
189 }
catch (IllegalStateException | InterruptedException e2) {
190 Log.e(
LOG_TAG,
"Will not wait anymore. Restart camera session. " + e2);
192 String cameraId = mCameraId;
195 mPreviewImageReader.getWidth(),
196 mPreviewImageReader.getHeight(),
197 mPreviewImageReader.getImageFormat());
208 setFrameRate(minFps, maxFps);
213 if (mPreviewImageReader !=
null)
214 removeSurface(mPreviewImageReader.getSurface());
216 if (mStillPhotoImageReader !=
null)
217 removeSurface(mStillPhotoImageReader.getSurface());
219 mPreviewImageReader = ImageReader.newInstance(
width,
height,
format, MaxNumberFrames);
220 mPreviewImageReader.setOnImageAvailableListener(mOnPreviewImageAvailableListener, mBackgroundHandler);
221 addSurface(mPreviewImageReader.getSurface());
223 mStillPhotoImageReader = ImageReader.newInstance(
width,
height,
format, MaxNumberFrames);
224 mStillPhotoImageReader.setOnImageAvailableListener(mOnStillPhotoAvailableListener, mBackgroundHandler);
225 addSurface(mStillPhotoImageReader.getSurface());
228 private void setFrameRate(
int minFrameRate,
int maxFrameRate) {
229 synchronized (mSyncedMembers) {
230 if (minFrameRate <= 0 || maxFrameRate <= 0)
231 mSyncedMembers.mCameraSettings.mFpsRange =
null;
233 mSyncedMembers.mCameraSettings.mFpsRange =
new Range<>(minFrameRate, maxFrameRate);
237 boolean addSurface(Surface surface) {
238 if (mTargetSurfaces.contains(surface))
241 return mTargetSurfaces.add(surface);
244 boolean removeSurface(Surface surface) {
245 return mTargetSurfaces.remove(surface);
249 void clearSurfaces() {
250 mTargetSurfaces.clear();
254 boolean createSession() {
255 if (mCameraDevice ==
null)
261 mCameraDevice.createCaptureSession(mTargetSurfaces, mCaptureStateCallback, mBackgroundHandler);
263 }
catch (Exception exception) {
264 Log.w(
LOG_TAG,
"Failed to create a capture session:" + exception);
271 if (mCameraDevice ==
null)
274 if (mCaptureSession ==
null)
278 synchronized (mSyncedMembers) {
279 setRepeatingRequestToPreview();
280 mSyncedMembers.mIsStarted =
true;
283 }
catch (CameraAccessException exception) {
284 Log.w(
LOG_TAG,
"Failed to start preview:" + exception);
290 void stopAndClose() {
291 synchronized (mSyncedMembers) {
293 if (
null != mCaptureSession) {
294 mCaptureSession.close();
295 mCaptureSession =
null;
297 if (
null != mCameraDevice) {
298 mCameraDevice.close();
299 mCameraDevice =
null;
302 mTargetSurfaces.clear();
303 }
catch (Exception exception) {
304 Log.w(
LOG_TAG,
"Failed to stop and close:" + exception);
306 mSyncedMembers.mIsStarted =
false;
307 mSyncedMembers.mIsTakingStillPhoto =
false;
313 void finalizeStillPhoto(CameraSettings cameraSettings)
throws CameraAccessException
315 final CaptureRequest.Builder requestBuilder =
316 mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
317 requestBuilder.addTarget(mStillPhotoImageReader.getSurface());
319 CaptureRequest.CONTROL_CAPTURE_INTENT,
320 CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
322 applyStillPhotoSettingsToCaptureRequestBuilder(
326 mCaptureSession.capture(
327 requestBuilder.build(),
328 new CameraStillPhotoFinalizerCallback(
this),
349 void beginStillPhotoCapture() {
350 synchronized (mSyncedMembers) {
351 if (mSyncedMembers.mIsTakingStillPhoto) {
356 "beginStillPhotoCapture() was called on camera backend while there " +
357 "is already a still photo in progress. This is not supported. Likely Qt " +
363 final CameraSettings cameraSettings = atomicCameraSettingsCopy();
365 submitNewStillPhotoCapture(cameraSettings);
366 }
catch (CameraAccessException e) {
367 Log.w(
LOG_TAG,
"Cannot get access to the camera: " + e);
375 void submitNewStillPhotoCapture(CameraSettings cameraSettings)
throws CameraAccessException
377 CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(
378 CameraDevice.TEMPLATE_STILL_CAPTURE);
381 requestBuilder.addTarget(mPreviewImageReader.getSurface());
383 applyStillPhotoSettingsToCaptureRequestBuilder(
391 final boolean triggerAutoFocus = requestBuilder.get(CaptureRequest.CONTROL_AF_MODE)
392 == CaptureResult.CONTROL_AF_MODE_AUTO;
394 final Integer aeMode = requestBuilder.get(CaptureRequest.CONTROL_AE_MODE);
395 boolean triggerAutoExposure = aeMode !=
null
396 && aeMode != CaptureResult.CONTROL_AE_MODE_OFF;
398 final CameraStillPhotoPrecaptureCallback precaptureCallback =
399 new CameraStillPhotoPrecaptureCallback(
403 triggerAutoExposure);
405 mCaptureSession.setRepeatingRequest(
406 requestBuilder.build(),
413 if (triggerAutoFocus) {
415 CaptureRequest.CONTROL_AF_TRIGGER,
416 CaptureRequest.CONTROL_AF_TRIGGER_START);
418 if (triggerAutoExposure) {
420 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
421 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
425 mCaptureSession.capture(
426 requestBuilder.build(),
430 synchronized (mSyncedMembers) {
431 mSyncedMembers.mIsTakingStillPhoto =
true;
438 if (mExifDataHandler !=
null)
439 mExifDataHandler.save(
path);
444 private Rect getScalerCropRegion(
float zoomFactor)
446 Rect activePixels = mVideoDeviceManager.getActiveArraySize(mCameraId);
447 float zoomRatio = 1.0f;
448 if (zoomFactor != 0.0
f)
449 zoomRatio = 1.0f / zoomFactor;
451 int croppedWidth = activePixels.width() - (int)(activePixels.width() * zoomRatio);
452 int croppedHeight = activePixels.height() - (int)(activePixels.height() * zoomRatio);
453 return new Rect(croppedWidth/2, croppedHeight/2, activePixels.width() - croppedWidth/2,
454 activePixels.height() - croppedHeight/2);
457 private void applyZoomSettingsToRequestBuilder(CaptureRequest.Builder requBuilder,
float zoomFactor)
459 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R) {
460 requBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion(zoomFactor));
462 requBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomFactor);
467 void zoomTo(
float factor)
469 synchronized (mSyncedMembers) {
470 mSyncedMembers.mCameraSettings.mZoomFactor = factor;
472 if (!mSyncedMembers.mIsStarted) {
480 applyZoomSettingsToRequestBuilder(mPreviewRequestBuilder, factor);
481 mPreviewRequest = mPreviewRequestBuilder.build();
483 if (mSyncedMembers.mIsTakingStillPhoto) {
489 mCaptureSession.setRepeatingRequest(
491 mPreviewCaptureCallback,
493 }
catch (Exception exception) {
494 Log.w(
LOG_TAG,
"Failed to set zoom:" + exception);
503 void setFocusMode(
int newFocusMode)
512 if (newFocusMode == 0)
513 newAfMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
514 else if (newFocusMode == 5)
515 newAfMode = CaptureRequest.CONTROL_AF_MODE_OFF;
519 "received a QCamera::FocusMode from native code that is not recognized. " +
520 "Likely Qt developer bug. Ignoring.");
537 synchronized (mSyncedMembers) {
538 mSyncedMembers.mCameraSettings.mAFMode = newAfMode;
542 if (!mSyncedMembers.mIsStarted)
545 applyFocusSettingsToCaptureRequestBuilder(
546 mPreviewRequestBuilder,
547 mSyncedMembers.mCameraSettings,
549 mPreviewRequest = mPreviewRequestBuilder.build();
551 if (mSyncedMembers.mIsTakingStillPhoto) {
557 mCaptureSession.setRepeatingRequest(
559 mPreviewCaptureCallback,
561 }
catch (Exception exception) {
562 Log.w(
LOG_TAG,
"Failed to set focus mode:" + exception);
568 void setFlashMode(
String flashMode)
570 synchronized (mSyncedMembers) {
571 int flashModeValue = mVideoDeviceManager.stringToControlAEMode(flashMode);
572 if (flashModeValue < 0) {
576 mSyncedMembers.mCameraSettings.mStillPhotoFlashMode = flashModeValue;
586 public void setFocusDistance(
float distanceInput)
588 if (distanceInput < 0.f || distanceInput > 1.
f) {
591 "received out-of-bounds value when setting camera focus-distance. " +
592 "Likely Qt developer bug. Ignoring.");
599 synchronized (mSyncedMembers) {
600 mSyncedMembers.mCameraSettings.mFocusDistance = distanceInput;
604 if (!mSyncedMembers.mIsStarted)
609 if (mSyncedMembers.mCameraSettings.mAFMode == CaptureRequest.CONTROL_AF_MODE_OFF) {
610 applyFocusSettingsToCaptureRequestBuilder(
611 mPreviewRequestBuilder,
612 mSyncedMembers.mCameraSettings,
615 mPreviewRequest = mPreviewRequestBuilder.build();
617 if (mSyncedMembers.mIsTakingStillPhoto) {
624 mCaptureSession.setRepeatingRequest(
626 mPreviewCaptureCallback,
628 }
catch (Exception exception) {
629 Log.w(
LOG_TAG,
"Failed to set focus distance:" + exception);
635 private int getTorchModeValue(
boolean mode)
637 return mode ? CameraMetadata.FLASH_MODE_TORCH : CameraMetadata.FLASH_MODE_OFF;
641 void setTorchMode(
boolean torchMode)
643 synchronized (mSyncedMembers) {
644 mSyncedMembers.mCameraSettings.mTorchMode = getTorchModeValue(torchMode);
646 if (mSyncedMembers.mIsStarted) {
647 mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mSyncedMembers.mCameraSettings.mTorchMode);
648 mPreviewRequest = mPreviewRequestBuilder.build();
650 if (mSyncedMembers.mIsTakingStillPhoto) {
657 mCaptureSession.setRepeatingRequest(
659 mPreviewCaptureCallback,
661 }
catch (Exception exception) {
662 Log.w(
LOG_TAG,
"Failed to set flash mode:" + exception);
671 void setRepeatingRequestToPreview() throws CameraAccessException {
672 synchronized (mSyncedMembers) {
673 mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
674 mPreviewRequestBuilder.addTarget(mPreviewImageReader.getSurface());
676 applyPreviewSettingsToCaptureRequestBuilder(
677 mPreviewRequestBuilder,
678 mSyncedMembers.mCameraSettings);
680 mPreviewRequest = mPreviewRequestBuilder.build();
681 mCaptureSession.setRepeatingRequest(
683 mPreviewCaptureCallback,
688 private void applyStillPhotoSettingsToCaptureRequestBuilder(
689 CaptureRequest.Builder requestBuilder,
690 CameraSettings cameraSettings)
693 CaptureRequest.CONTROL_CAPTURE_INTENT,
694 CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
698 CaptureRequest.CONTROL_MODE,
699 CaptureRequest.CONTROL_MODE_AUTO);
703 CaptureRequest.CONTROL_AE_MODE,
704 CaptureRequest.CONTROL_AE_MODE_ON);
706 applyZoomSettingsToRequestBuilder(requestBuilder, cameraSettings.mZoomFactor);
708 applyFocusSettingsToCaptureRequestBuilder(
716 if (cameraSettings.mStillPhotoFlashMode == CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
718 CaptureRequest.CONTROL_AE_MODE,
719 CaptureRequest.CONTROL_AE_MODE_ON);
725 CaptureRequest.FLASH_MODE,
726 CaptureRequest.FLASH_MODE_TORCH);
727 }
else if (cameraSettings.mStillPhotoFlashMode == CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH) {
729 CaptureRequest.CONTROL_AE_MODE,
730 CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
734 private void applyPreviewSettingsToCaptureRequestBuilder(
735 CaptureRequest.Builder requestBuilder,
736 CameraSettings cameraSettings)
738 requestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CameraMetadata.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
740 applyFocusSettingsToCaptureRequestBuilder(
746 requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
747 requestBuilder.set(CaptureRequest.FLASH_MODE, cameraSettings.mTorchMode);
749 applyZoomSettingsToRequestBuilder(requestBuilder, cameraSettings.mZoomFactor);
750 if (cameraSettings.mFpsRange !=
null) {
752 CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
753 cameraSettings.mFpsRange);
758 mPreviewRequestBuilder.set(
759 CaptureRequest.CONTROL_AF_TRIGGER,
760 CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
761 mPreviewRequestBuilder.set(
762 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
763 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
768 void applyFocusSettingsToCaptureRequestBuilder(
769 CaptureRequest.Builder requestBuilder,
770 CameraSettings cameraSettings,
773 int desiredAfMode = cameraSettings.mAFMode;
778 if (stillPhoto && desiredAfMode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
779 desiredAfMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
782 if (!isAfModeAvailable(desiredAfMode)) {
787 if (isAfModeAvailable(CaptureRequest.CONTROL_AF_MODE_OFF)) {
789 CaptureRequest.CONTROL_AF_MODE,
790 CaptureRequest.CONTROL_AF_MODE_OFF);
793 requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE,
null);
797 requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, desiredAfMode);
800 if (desiredAfMode == CaptureRequest.CONTROL_AF_MODE_OFF) {
801 final float lensFocusDistance = calcLensFocusDistanceFromQCameraFocusDistance(
802 cameraSettings.mFocusDistance);
803 if (lensFocusDistance < 0) {
806 "Tried to apply FocusModeManual on a camera that doesn't support "
807 +
"setting lens distance. Likely Qt developer bug. Ignoring.");
809 requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, lensFocusDistance);
812 requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE,
null);
819 private float calcLensFocusDistanceFromQCameraFocusDistance(
float qCameraFocusDistance) {
820 float lensMinimumFocusDistance =
821 mVideoDeviceManager.getLensInfoMinimumFocusDistance(mCameraId);
822 if (lensMinimumFocusDistance <= 0)
829 return (1.
f - qCameraFocusDistance) * lensMinimumFocusDistance;
834 private boolean isAfModeAvailable(
int afMode) {
835 if (mVideoDeviceManager ==
null || mCameraId ==
null || mCameraId.isEmpty())
837 return mVideoDeviceManager.isAfModeAvailable(mCameraId, afMode);
842 static boolean afStateIsReadyForCapture(
Integer afState) {
843 return afState ==
null
844 || afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED
845 || afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED;
848 static boolean aeStateIsReadyForCapture(
Integer aeState) {
849 return aeState ==
null
850 || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED
851 || aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED;
static void onPreviewFrameAvailable(JNIEnv *env, jobject obj, jstring cameraId, QtJniTypes::Image image)
static void onCaptureSessionFailed(JNIEnv *env, jobject obj, jstring cameraId, jint reason, jlong framenumber)
static void onSessionClosed(JNIEnv *env, jobject obj, jstring cameraId)
static void onStillPhotoCaptureFailed(JNIEnv *env, jobject obj, jstring cameraId)
static void onCameraError(JNIEnv *env, jobject obj, jstring cameraId, jint error)
static void onCaptureSessionConfigureFailed(JNIEnv *env, jobject obj, jstring cameraId)
static void onCameraOpened(JNIEnv *env, jobject obj, jstring cameraId)
static void onSessionActive(JNIEnv *env, jobject obj, jstring cameraId)
static void onCameraDisconnect(JNIEnv *env, jobject obj, jstring cameraId)
static void onCaptureSessionConfigured(JNIEnv *env, jobject obj, jstring cameraId)
static void onStillPhotoAvailable(JNIEnv *env, jobject obj, jstring cameraId, QtJniTypes::Image image)
static const QString context()
QImageReader reader("image.png")
[1]
DBusConnection const char DBusError * error
GLsizei const GLchar *const * path
file open(QIODevice::ReadOnly)
QNetworkRequest request(url)
[0]