39 CameraDevice mCameraDevice =
null;
41 HandlerThread mBackgroundThread;
42 Handler mBackgroundHandler;
43 ImageReader mImageReader =
null;
44 ImageReader mCapturedPhotoReader =
null;
45 CameraManager mCameraManager;
46 CameraCaptureSession mCaptureSession;
47 CaptureRequest.Builder mPreviewRequestBuilder;
48 CaptureRequest mPreviewRequest;
50 List<Surface> mTargetSurfaces =
new ArrayList<>();
52 private static final int STATE_PREVIEW = 0;
53 private static final int STATE_WAITING_LOCK = 1;
54 private static final int STATE_WAITING_PRECAPTURE = 2;
55 private static final int STATE_WAITING_NON_PRECAPTURE = 3;
56 private static final int STATE_PICTURE_TAKEN = 4;
58 private int mState = STATE_PREVIEW;
60 private boolean mIsStarted =
false;
61 private static int MaxNumberFrames = 12;
62 private int mFlashMode = CaptureRequest.CONTROL_AE_MODE_ON;
63 private int mTorchMode = CameraMetadata.FLASH_MODE_OFF;
64 private int mAFMode = CaptureRequest.CONTROL_AF_MODE_OFF;
65 private float mZoomFactor = 1.0f;
66 private Range<Integer> mFpsRange =
null;
72 CameraDevice.StateCallback mStateCallback =
new CameraDevice.StateCallback() {
74 public void onOpened(CameraDevice cameraDevice) {
75 if (mCameraDevice !=
null)
76 mCameraDevice.close();
77 mCameraDevice = cameraDevice;
81 public void onDisconnected(CameraDevice cameraDevice) {
83 if (mCameraDevice == cameraDevice)
88 public void onError(CameraDevice cameraDevice,
int error) {
90 if (mCameraDevice == cameraDevice)
98 CameraCaptureSession.StateCallback mCaptureStateCallback =
new CameraCaptureSession.StateCallback() {
100 public void onConfigured(CameraCaptureSession cameraCaptureSession) {
101 mCaptureSession = cameraCaptureSession;
106 public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
111 public void onActive(CameraCaptureSession cameraCaptureSession) {
112 super.onActive(cameraCaptureSession);
117 public void onClosed(CameraCaptureSession cameraCaptureSession) {
118 super.onClosed(cameraCaptureSession);
126 CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
127 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest
request, CaptureFailure failure) {
128 super.onCaptureFailed(session,
request, failure);
132 private void process(CaptureResult
result) {
134 case STATE_WAITING_LOCK: {
135 Integer afState =
result.get(CaptureResult.CONTROL_AF_STATE);
136 if (afState ==
null) {
138 }
else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
139 CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
140 Integer aeState =
result.get(CaptureResult.CONTROL_AE_STATE);
141 if (aeState ==
null ||
142 aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
143 mState = STATE_PICTURE_TAKEN;
147 mPreviewRequestBuilder.set(
148 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
149 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
150 mState = STATE_WAITING_PRECAPTURE;
151 mCaptureSession.capture(mPreviewRequestBuilder.build(),
154 }
catch (CameraAccessException e) {
155 Log.w(
"QtCamera2",
"Cannot get access to the camera: " + e);
161 case STATE_WAITING_PRECAPTURE: {
162 Integer aeState =
result.get(CaptureResult.CONTROL_AE_STATE);
163 if (aeState ==
null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
164 mState = STATE_WAITING_NON_PRECAPTURE;
168 case STATE_WAITING_NON_PRECAPTURE: {
169 Integer aeState =
result.get(CaptureResult.CONTROL_AE_STATE);
170 if (aeState ==
null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
171 mState = STATE_PICTURE_TAKEN;
182 public void onCaptureProgressed(CameraCaptureSession
s, CaptureRequest
r, CaptureResult partialResult) {
183 process(partialResult);
187 public void onCaptureCompleted(CameraCaptureSession
s, CaptureRequest
r, TotalCaptureResult
result) {
193 mCameraManager = (CameraManager)
context.getSystemService(Context.CAMERA_SERVICE);
195 startBackgroundThread();
198 void startBackgroundThread() {
199 mBackgroundThread =
new HandlerThread(
"CameraBackground");
200 mBackgroundThread.start();
201 mBackgroundHandler =
new Handler(mBackgroundThread.getLooper());
204 void stopBackgroundThread() {
205 mBackgroundThread.quitSafely();
207 mBackgroundThread.join();
208 mBackgroundThread =
null;
209 mBackgroundHandler =
null;
210 }
catch (Exception e) {
215 @SuppressLint(
"MissingPermission")
216 public
boolean open(String cameraId) {
218 mCameraId = cameraId;
219 mCameraManager.openCamera(cameraId,mStateCallback,mBackgroundHandler);
221 }
catch (Exception e){
222 Log.w(
"QtCamera2",
"Failed to open camera:" + e);
230 ImageReader.OnImageAvailableListener mOnPhotoAvailableListener =
new ImageReader.OnImageAvailableListener() {
232 public void onImageAvailable(ImageReader reader) {
233 QtCamera2.this.onPhotoAvailable(mCameraId, reader.acquireLatestImage());
239 ImageReader.OnImageAvailableListener mOnImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
241 public void onImageAvailable(ImageReader reader) {
243 Image
img = reader.acquireLatestImage();
245 QtCamera2.this.onFrameAvailable(mCameraId,
img);
246 }
catch (IllegalStateException e) {
249 Log.e(
"QtCamera2",
"Image processing taking too long. Let's wait 0,5s more " + e);
252 QtCamera2.this.onFrameAvailable(mCameraId, reader.acquireLatestImage());
253 }
catch (IllegalStateException | InterruptedException e2) {
254 Log.e(
"QtCamera2",
"Will not wait anymore. Restart camera session. " + e2);
256 String cameraId = mCameraId;
258 addImageReader(mImageReader.getWidth(), mImageReader.getHeight(),
259 mImageReader.getImageFormat());
270 setFrameRate(minFps, maxFps);
275 if (mImageReader !=
null)
276 removeSurface(mImageReader.getSurface());
278 if (mCapturedPhotoReader !=
null)
279 removeSurface(mCapturedPhotoReader.getSurface());
282 mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
283 addSurface(mImageReader.getSurface());
285 mCapturedPhotoReader = ImageReader.newInstance(
width,
height,
format, MaxNumberFrames);
286 mCapturedPhotoReader.setOnImageAvailableListener(mOnPhotoAvailableListener, mBackgroundHandler);
287 addSurface(mCapturedPhotoReader.getSurface());
290 private void setFrameRate(
int minFrameRate,
int maxFrameRate) {
292 if (minFrameRate <= 0 || maxFrameRate <= 0)
295 mFpsRange =
new Range<>(minFrameRate, maxFrameRate);
299 if (mTargetSurfaces.contains(surface))
302 return mTargetSurfaces.add(surface);
306 return mTargetSurfaces.remove(surface);
310 mTargetSurfaces.clear();
314 if (mCameraDevice ==
null)
318 mCameraDevice.createCaptureSession(mTargetSurfaces, mCaptureStateCallback, mBackgroundHandler);
320 }
catch (Exception exception) {
321 Log.w(
"QtCamera2",
"Failed to create a capture session:" + exception);
326 public boolean start(
int template) {
328 if (mCameraDevice ==
null)
331 if (mCaptureSession ==
null)
334 synchronized (mStartMutex) {
336 mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(template);
337 mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
338 mAFMode = CaptureRequest.CONTROL_AF_MODE_OFF;
340 if (
mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
346 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
347 mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mTorchMode);
348 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
349 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mAFMode);
350 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CameraMetadata.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
351 if (mZoomFactor != 1.0
f)
352 mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion());
353 if (mFpsRange !=
null)
354 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mFpsRange);
355 mPreviewRequest = mPreviewRequestBuilder.build();
356 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
360 }
catch (Exception exception) {
361 Log.w(
"QtCamera2",
"Failed to start preview:" + exception);
368 synchronized (mStartMutex) {
370 if (
null != mCaptureSession) {
371 mCaptureSession.close();
372 mCaptureSession =
null;
374 if (
null != mCameraDevice) {
375 mCameraDevice.close();
376 mCameraDevice =
null;
379 mTargetSurfaces.clear();
380 }
catch (Exception exception) {
381 Log.w(
"QtCamera2",
"Failed to stop and close:" + exception);
387 private void capturePhoto() {
389 final CaptureRequest.Builder captureBuilder =
390 mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
391 captureBuilder.addTarget(mCapturedPhotoReader.getSurface());
392 captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
393 if (mZoomFactor != 1.0
f)
394 captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion());
396 CameraCaptureSession.CaptureCallback captureCallback
397 =
new CameraCaptureSession.CaptureCallback() {
399 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest
request,
400 TotalCaptureResult
result) {
404 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
405 CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
406 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
407 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
408 mPreviewRequest = mPreviewRequestBuilder.build();
409 mState = STATE_PREVIEW;
410 mCaptureSession.setRepeatingRequest(mPreviewRequest,
413 }
catch (CameraAccessException e) {
419 mCaptureSession.capture(captureBuilder.build(), captureCallback, mBackgroundHandler);
420 }
catch (CameraAccessException e) {
427 if (mAFMode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
428 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
429 mState = STATE_WAITING_LOCK;
430 mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
434 }
catch (CameraAccessException e) {
435 Log.w(
"QtCamera2",
"Cannot get access to the camera: " + e);
441 if (mExifDataHandler !=
null)
444 Log.e(
"QtCamera2",
"No Exif data that could be saved to " +
path);
447 private Rect getScalerCropRegion()
450 float zoomRatio = 1.0f;
451 if (mZoomFactor != 0.0
f)
452 zoomRatio = 1.0f/mZoomFactor;
453 int croppedWidth = activePixels.width() - (int)(activePixels.width() * zoomRatio);
454 int croppedHeight = activePixels.height() - (int)(activePixels.height() * zoomRatio);
455 return new Rect(croppedWidth/2, croppedHeight/2, activePixels.width() - croppedWidth/2,
456 activePixels.height() - croppedHeight/2);
461 synchronized (mStartMutex) {
462 mZoomFactor = factor;
465 Log.w(
"QtCamera2",
"Cannot set zoom on invalid camera");
469 mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion());
470 mPreviewRequest = mPreviewRequestBuilder.build();
473 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
474 }
catch (Exception exception) {
475 Log.w(
"QtCamera2",
"Failed to set zoom:" + exception);
481 synchronized (mStartMutex) {
484 if (flashModeValue < 0) {
485 Log.w(
"QtCamera2",
"Unknown flash mode");
488 mFlashMode = flashModeValue;
493 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
494 mPreviewRequest = mPreviewRequestBuilder.build();
497 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
498 }
catch (Exception exception) {
499 Log.w(
"QtCamera2",
"Failed to set flash mode:" + exception);
504 private int getTorchModeValue(
boolean mode)
506 return mode ? CameraMetadata.FLASH_MODE_TORCH : CameraMetadata.FLASH_MODE_OFF;
511 synchronized (mStartMutex) {
512 mTorchMode = getTorchModeValue(torchMode);
515 mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mTorchMode);
516 mPreviewRequest = mPreviewRequestBuilder.build();
519 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
520 }
catch (Exception exception) {
521 Log.w(
"QtCamera2",
"Failed to set flash mode:" + exception);