Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
QtCamera2.java
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3package org.qtproject.qt.android.multimedia;
4
6
7import android.annotation.SuppressLint;
8import android.annotation.TargetApi;
9import android.content.Context;
10import android.graphics.Rect;
11import android.hardware.camera2.CameraAccessException;
12import android.hardware.camera2.CameraCaptureSession;
13import android.hardware.camera2.CameraDevice;
14import android.hardware.camera2.CameraMetadata;
15import android.hardware.camera2.CameraManager;
16import android.hardware.camera2.CaptureFailure;
17import android.hardware.camera2.CaptureResult;
18import android.hardware.camera2.CaptureRequest;
19import android.hardware.camera2.TotalCaptureResult;
20import android.media.Image;
21import android.media.ImageReader;
22import android.graphics.ImageFormat;
23import android.os.Handler;
24import android.os.HandlerThread;
25import android.util.Log;
26import android.view.Surface;
27import android.media.MediaCodec;
28import android.media.MediaCodecInfo;
29import android.media.MediaFormat;
30import java.util.ArrayList;
31import java.util.List;
32
33@TargetApi(23)
34public class QtCamera2 {
35
36 CameraDevice mCameraDevice = null;
37 QtVideoDeviceManager mVideoDeviceManager = null;
38 HandlerThread mBackgroundThread;
39 Handler mBackgroundHandler;
40 ImageReader mImageReader = null;
41 ImageReader mCapturedPhotoReader = null;
42 CameraManager mCameraManager;
43 CameraCaptureSession mCaptureSession;
44 CaptureRequest.Builder mPreviewRequestBuilder;
45 CaptureRequest mPreviewRequest;
46 String mCameraId;
47 List<Surface> mTargetSurfaces = new ArrayList<>();
48
49 private static final int STATE_PREVIEW = 0;
50 private static final int STATE_WAITING_LOCK = 1;
51 private static final int STATE_WAITING_PRECAPTURE = 2;
52 private static final int STATE_WAITING_NON_PRECAPTURE = 3;
53 private static final int STATE_PICTURE_TAKEN = 4;
54
55 private int mState = STATE_PREVIEW;
56 private Object mStartMutex = new Object();
57 private boolean mIsStarted = false;
58 private static int MaxNumberFrames = 10;
59 private int mFlashMode = CaptureRequest.CONTROL_AE_MODE_ON;
60 private int mTorchMode = CameraMetadata.FLASH_MODE_OFF;
61
62 native void onCameraOpened(String cameraId);
63 native void onCameraDisconnect(String cameraId);
64 native void onCameraError(String cameraId, int error);
65 CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
66 @Override
67 public void onOpened(CameraDevice cameraDevice) {
68 if (mCameraDevice != null)
69 mCameraDevice.close();
70 mCameraDevice = cameraDevice;
71 onCameraOpened(mCameraId);
72 }
73 @Override
74 public void onDisconnected(CameraDevice cameraDevice) {
75 cameraDevice.close();
76 if (mCameraDevice == cameraDevice)
77 mCameraDevice = null;
78 onCameraDisconnect(mCameraId);
79 }
80 @Override
81 public void onError(CameraDevice cameraDevice, int error) {
82 cameraDevice.close();
83 if (mCameraDevice == cameraDevice)
84 mCameraDevice = null;
85 onCameraError(mCameraId, error);
86 }
87 };
88
89 native void onCaptureSessionConfigured(String cameraId);
90 native void onCaptureSessionConfigureFailed(String cameraId);
91 CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() {
92 @Override
93 public void onConfigured(CameraCaptureSession cameraCaptureSession) {
94 mCaptureSession = cameraCaptureSession;
96 }
97
98 @Override
99 public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
101 }
102
103 @Override
104 public void onActive(CameraCaptureSession cameraCaptureSession) {
105 super.onActive(cameraCaptureSession);
106 onSessionActive(mCameraId);
107 }
108
109 @Override
110 public void onClosed(CameraCaptureSession cameraCaptureSession) {
111 super.onClosed(cameraCaptureSession);
112 onSessionClosed(mCameraId);
113 }
114 };
115
116 native void onSessionActive(String cameraId);
117 native void onSessionClosed(String cameraId);
118 native void onCaptureSessionFailed(String cameraId, int reason, long frameNumber);
119 CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
120 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
121 super.onCaptureFailed(session, request, failure);
122 onCaptureSessionFailed(mCameraId, failure.getReason(), failure.getFrameNumber());
123 }
124
125 private void process(CaptureResult result) {
126 switch (mState) {
127 case STATE_WAITING_LOCK: {
128 Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
129 if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
130 CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
131 Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
132 if (aeState == null ||
133 aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
134 mState = STATE_PICTURE_TAKEN;
135 capturePhoto();
136 } else {
137 try {
138 mPreviewRequestBuilder.set(
139 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
140 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
141 mState = STATE_WAITING_PRECAPTURE;
142 mCaptureSession.capture(mPreviewRequestBuilder.build(),
143 mCaptureCallback,
144 mBackgroundHandler);
145 } catch (CameraAccessException e) {
146 Log.w("QtCamera2", "Cannot get access to the camera: " + e);
147 }
148 }
149 }
150 break;
151 }
152 case STATE_WAITING_PRECAPTURE: {
153 Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
154 if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
155 mState = STATE_WAITING_NON_PRECAPTURE;
156 }
157 break;
158 }
159 case STATE_WAITING_NON_PRECAPTURE: {
160 Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
161 if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
162 mState = STATE_PICTURE_TAKEN;
163 capturePhoto();
164 }
165 break;
166 }
167 default:
168 break;
169 }
170 }
171
172 @Override
173 public void onCaptureProgressed(CameraCaptureSession s, CaptureRequest r, CaptureResult partialResult) {
174 process(partialResult);
175 }
176
177 @Override
178 public void onCaptureCompleted(CameraCaptureSession s, CaptureRequest r, TotalCaptureResult result) {
179 process(result);
180 }
181 };
182
183 public QtCamera2(Context context) {
184 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
185 mVideoDeviceManager = new QtVideoDeviceManager(context);
186 startBackgroundThread();
187 }
188
189 void startBackgroundThread() {
190 mBackgroundThread = new HandlerThread("CameraBackground");
191 mBackgroundThread.start();
192 mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
193 }
194
195 void stopBackgroundThread() {
196 mBackgroundThread.quitSafely();
197 try {
198 mBackgroundThread.join();
199 mBackgroundThread = null;
200 mBackgroundHandler = null;
201 } catch (Exception e) {
202 e.printStackTrace();
203 }
204 }
205
206 @SuppressLint("MissingPermission")
207 public boolean open(String cameraId) {
208 try {
209 mCameraId = cameraId;
210 mCameraManager.openCamera(cameraId,mStateCallback,mBackgroundHandler);
211 return true;
212 } catch (Exception e){
213 Log.w("QtCamera2", "Failed to open camera:" + e);
214 }
215
216 return false;
217 }
218
219 native void onPhotoAvailable(String cameraId, Image frame);
220
221 ImageReader.OnImageAvailableListener mOnPhotoAvailableListener = new ImageReader.OnImageAvailableListener() {
222 @Override
223 public void onImageAvailable(ImageReader reader) {
224 QtCamera2.this.onPhotoAvailable(mCameraId, reader.acquireLatestImage());
225 }
226 };
227
228 native void onFrameAvailable(String cameraId, Image frame);
229
230 ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
231 @Override
232 public void onImageAvailable(ImageReader reader) {
233 QtCamera2.this.onFrameAvailable(mCameraId, reader.acquireLatestImage());
234 }
235 };
236
237 public boolean addImageReader(int width, int height, int format) {
238
239 if (mImageReader != null)
240 removeSurface(mImageReader.getSurface());
241
242 mImageReader = ImageReader.newInstance(width, height, format, MaxNumberFrames);
243 mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
244 addSurface(mImageReader.getSurface());
245
246 mCapturedPhotoReader = ImageReader.newInstance(width, height, format, MaxNumberFrames);
247 mCapturedPhotoReader.setOnImageAvailableListener(mOnPhotoAvailableListener, mBackgroundHandler);
248 addSurface(mCapturedPhotoReader.getSurface());
249
250 return true;
251 }
252
253 public boolean addSurface(Surface surface) {
254 if (mTargetSurfaces.contains(surface))
255 return true;
256
257 return mTargetSurfaces.add(surface);
258 }
259
260 public boolean removeSurface(Surface surface) {
261 return mTargetSurfaces.remove(surface);
262 }
263
264 public void clearSurfaces() {
265 mTargetSurfaces.clear();
266 }
267
268 public boolean createSession() {
269 if (mCameraDevice == null)
270 return false;
271
272 try {
273 mCameraDevice.createCaptureSession(mTargetSurfaces, mCaptureStateCallback, mBackgroundHandler);
274 return true;
275 } catch (Exception exception) {
276 Log.w("QtCamera2", "Failed to create a capture session:" + exception);
277 }
278 return false;
279 }
280
281 public boolean start(int template) {
282
283 if (mCameraDevice == null)
284 return false;
285
286 if (mCaptureSession == null)
287 return false;
288
289 synchronized (mStartMutex) {
290 try {
291 mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(template);
292 mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
293
294 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
295 mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mTorchMode);
296 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
297 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
298 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CameraMetadata.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
299
300 mPreviewRequest = mPreviewRequestBuilder.build();
301 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
302 mIsStarted = true;
303 return true;
304
305 } catch (Exception exception) {
306 Log.w("QtCamera2", "Failed to start preview:" + exception);
307 }
308 return false;
309 }
310 }
311
312 public void stopAndClose() {
313 synchronized (mStartMutex) {
314 try {
315 if (null != mCaptureSession) {
316 mCaptureSession.close();
317 mCaptureSession = null;
318 }
319 if (null != mCameraDevice) {
320 mCameraDevice.close();
321 mCameraDevice = null;
322 }
323 mCameraId = "";
324 mTargetSurfaces.clear();
325 } catch (Exception exception) {
326 Log.w("QtCamera2", "Failed to stop and close:" + exception);
327 }
328 mIsStarted = false;
329 }
330 }
331
332 private void capturePhoto() {
333 try {
334 final CaptureRequest.Builder captureBuilder =
335 mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
336 captureBuilder.addTarget(mCapturedPhotoReader.getSurface());
337 captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
338
339 CameraCaptureSession.CaptureCallback captureCallback
340 = new CameraCaptureSession.CaptureCallback() {
341 @Override
342 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
343 TotalCaptureResult result) {
344 try {
345 // Reset the focus/flash and go back to the normal state of preview.
346 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
347 CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
348 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
349 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
350 mPreviewRequest = mPreviewRequestBuilder.build();
351 mState = STATE_PREVIEW;
352 mCaptureSession.setRepeatingRequest(mPreviewRequest,
353 mCaptureCallback,
354 mBackgroundHandler);
355 } catch (CameraAccessException e) {
356 e.printStackTrace();
357 }
358 }
359 };
360
361 mCaptureSession.capture(captureBuilder.build(), captureCallback, mBackgroundHandler);
362 } catch (CameraAccessException e) {
363 e.printStackTrace();
364 }
365 }
366
367 public void takePhoto() {
368 try {
369 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
370 mState = STATE_WAITING_LOCK;
371 mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
372 } catch (CameraAccessException e) {
373 Log.w("QtCamera2", "Cannot get access to the camera: " + e);
374 }
375 }
376
377 public void zoomTo(float factor)
378 {
379 synchronized (mStartMutex) {
380 if (!mIsStarted) {
381 Log.w("QtCamera2", "Cannot set zoom on invalid camera");
382 return;
383 }
384
385 Rect activePixels = mVideoDeviceManager.getActiveArraySize(mCameraId);
386 float zoomRatio = 1/factor;
387 int croppedWidth = activePixels.width() - (int)(activePixels.width() * zoomRatio);
388 int croppedHeight = activePixels.height() - (int)(activePixels.height() * zoomRatio);
389 Rect zoom = new Rect(croppedWidth/2, croppedHeight/2, activePixels.width() - croppedWidth/2,
390 activePixels.height() - croppedHeight/2);
391 mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoom);
392 mPreviewRequest = mPreviewRequestBuilder.build();
393
394 try {
395 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
396 } catch (Exception exception) {
397 Log.w("QtCamera2", "Failed to set zoom:" + exception);
398 }
399 }
400 }
401 public void setFlashMode(String flashMode)
402 {
403 synchronized (mStartMutex) {
404
405 int flashModeValue = mVideoDeviceManager.stringToControlAEMode(flashMode);
406 if (flashModeValue < 0) {
407 Log.w("QtCamera2", "Unknown flash mode");
408 return;
409 }
410 mFlashMode = flashModeValue;
411
412 if (!mIsStarted)
413 return;
414
415 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mFlashMode);
416 mPreviewRequest = mPreviewRequestBuilder.build();
417
418 try {
419 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
420 } catch (Exception exception) {
421 Log.w("QtCamera2", "Failed to set flash mode:" + exception);
422 }
423 }
424 }
425
426 private int getTorchModeValue(boolean mode)
427 {
428 return mode ? CameraMetadata.FLASH_MODE_TORCH : CameraMetadata.FLASH_MODE_OFF;
429 }
430
431 public void setTorchMode(boolean torchMode)
432 {
433 synchronized (mStartMutex) {
434 mTorchMode = getTorchModeValue(torchMode);
435
436 if (mIsStarted) {
437 mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, mTorchMode);
438 mPreviewRequest = mPreviewRequestBuilder.build();
439
440 try {
441 mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
442 } catch (Exception exception) {
443 Log.w("QtCamera2", "Failed to set flash mode:" + exception);
444 }
445 }
446 }
447 }
448}
Definition main.cpp:8
boolean addImageReader(int width, int height, int format)
double e
static void onFrameAvailable(JNIEnv *env, jobject obj, jstring cameraId, QtJniTypes::AndroidImage 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 onPhotoAvailable(JNIEnv *env, jobject obj, jstring cameraId, QtJniTypes::AndroidImage image)
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 * context
DBusConnection const char DBusError * error
GLenum mode
GLint GLsizei GLsizei height
GLboolean r
[2]
GLint GLsizei width
GLint GLsizei GLsizei GLenum format
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
file open(QIODevice::ReadOnly)
QFrame frame
[0]
QNetworkRequest request(url)