This is the 14th day of my participation in the August More Text Challenge. For details, see:August is more challenging

Android custom Camera2 camera

Custom Camera2 camera

The development process

4. Turn on the camera

val cameraStateCallback = CameraStateCallback()
cameraManager.openCamera(cameraId, cameraStateCallback, mainHandler)

......

 private inner class CameraStateCallback : CameraDevice.StateCallback() {
        @MainThread
        override fun onOpened(camera: CameraDevice){ cameraDeviceFuture!! .set(camera) cameraCharacteristicsFuture!! .set(getCameraCharacteristics(camera.id))
        }

        @MainThread
        override fun onClosed(camera: CameraDevice){}@MainThread
        override fun onDisconnected(camera: CameraDevice){ cameraDeviceFuture!! .set(camera)
            closeCamera()
        }

        @MainThread
        override fun onError(camera: CameraDevice, error: Int){ cameraDeviceFuture!! .set(camera)
            closeCamera()
        }
    }
Copy the code

cameraManager.openCamera(@NonNull String cameraId,@NonNull final CameraDevice.StateCallback callback, @nullable Handler Handler)

  • CameraId: The unique identifier of the camera
  • Callback: callback for changes in the device connection state
  • Handler: The handler object for callback execution. Null is passed to use the current main thread handler

Where the CameraStateCallback callback:

  • OnOpened: the camera is opened successfully. You can start using the camera and create the Capture session
  • OnDisconnected: To call back this method when the camera is disconnected, an operation to release the camera is required
  • OnError: When the camera fails to be turned on, release the camera
  • OnClosed: callback method after calling camera.close ()

5. Create a Capture Session

val sessionStateCallback = SessionStateCallback()
......
valcameraDevice = cameraDeviceFuture? .get() cameraDevice? .createCaptureSession(outputs, sessionStateCallback, mainHandler) ......private inner class SessionStateCallback : CameraCaptureSession.StateCallback() {
        @MainThread
        override fun onConfigureFailed(session: CameraCaptureSession){ captureSessionFuture!! .set(session)
        }

        @MainThread
        override fun onConfigured(session: CameraCaptureSession){ captureSessionFuture!! .set(session)
        }

        @MainThread
        override fun onClosed(session: CameraCaptureSession){}}Copy the code

This piece of code core method is mCameraDevice createCaptureSession () to create the Capture session, it accepts three parameters:

  • Outputs: The surface collection for receiving image data, where a Preview surface is passed in
  • The callback: listens for the Session state CameraCaptureSession. StateCallback object
  • Handler: used to perform CameraCaptureSession. StateCallback handler object, pass in null use the main thread of the current handler

6. Create CaptureRequest

CaptureRequest is the information carrier for submitting Capture request to CameraCaptureSession, which contains the parameters configuration of this Capture and the Surface for receiving image data

if(cameraDevice ! =null) {
  previewImageRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
  captureImageRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
}

......

valcameraDevice = cameraDeviceFuture? .get(a)valcaptureSession = captureSessionFuture? .get(a)val previewImageRequestBuilder = previewImageRequestBuilder!!
val captureImageRequestBuilder = captureImageRequestBuilder!!
if(cameraDevice ! =null&& captureSession ! =null) {
  val previewSurface = previewSurface!!
  val previewDataSurface = previewDataSurface
  previewImageRequestBuilder.addTarget(previewSurface)
  // Avoid missing preview frame while capturing image.
  captureImageRequestBuilder.addTarget(previewSurface)
  if(previewDataSurface ! =null) {
    previewImageRequestBuilder.addTarget(previewDataSurface)
    // Avoid missing preview data while capturing image.
    captureImageRequestBuilder.addTarget(previewDataSurface)
  }
  val previewRequest = previewImageRequestBuilder.build()
  captureSession.setRepeatingRequest(previewRequest, RepeatingCaptureStateCallback(), mainHandler)
}

......

private inner class RepeatingCaptureStateCallback : CameraCaptureSession.CaptureCallback() {
  @MainThread
  override fun onCaptureStarted(session: CameraCaptureSession, request: CaptureRequest, timestamp: Long, frameNumber: Long) {
    super.onCaptureStarted(session, request, timestamp, frameNumber)
  }

  @MainThread
  override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
    super.onCaptureCompleted(session, request, result)
  }
}
Copy the code

In addition to the mode configuration, CaptureRequest can be configured with many other information, such as image format, image resolution, sensor controls, flash controls, and 3A(autofocus-AF, autoexposure -AE, and autowhite Balance -AWB) controls. This can be set in the callback to createCaptureSession, and finally the CaptureRequest object is generated through the build() method.

7. Preview

In Camera2, the preview function is realized by repeated Capture. Each Capture will display the preview screen on the corresponding Surface. The Capture operation succeeds repeatedly. Procedure

captureSession.setRepeatingRequest(previewRequest, RepeatingCaptureStateCallback(), mainHandler)

This method takes three parameters:

  • Request: Indicates a CaptureRequest object
  • Listener: callback for listening on the Capture status
  • Handler: used to perform CameraCaptureSession. CaptureCallback handler object, pass in null use the main thread of the current handler

Stop use preview mCaptureSession. StopRepeating () method.

8. Photo

So once you’ve set up request, session, you can actually start taking pictures

val captureImageRequest = captureImageRequestBuilder.build()
captureSession.capture(captureImageRequest, CaptureImageStateCallback(), mainHandler)

......

private inner class CaptureImageStateCallback : CameraCaptureSession.CaptureCallback() {

  @MainThread
  override fun onCaptureStarted(session: CameraCaptureSession, request: CaptureRequest, timestamp: Long, frameNumber: Long) {
    super.onCaptureStarted(session, request, timestamp, frameNumber)
    // Play the shutter click sound.cameraHandler? .post { mediaActionSound.play(MediaActionSound.SHUTTER_CLICK) } }@MainThread
  override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
    super.onCaptureCompleted(session, request, result)
    captureResults.put(result)
  }
}
Copy the code

CaptureSession. The capture () method also has three parameters, and mCaptureSession setRepeatingRequest:

  • Request: Indicates a CaptureRequest object
  • Listener: callback for listening on the Capture status
  • Handler: used to perform CameraCaptureSession. CaptureCallback handler object, pass in null use the main thread of the current handler

9. Turn off the camera

As with other hardware resources, call cameraDevice.close () to turn off camera recycling resources when we no longer need to use the camera. The operation of turning off the camera is very important, because if you keep occupying the camera resources, other camera-based functions will not work properly, and in serious cases, other camera-related apps will not work properly. When the camera is fully closed by CameraStateCallback. OnCllosed () method to inform you that the camera has been closed. So when is the best time to turn off the camera? Personal advice is to always turn off the camera during onPause(), because the camera page is no longer the focus of the user’s attention at this point, and in most cases the camera can already be turned off.

cameraDevice? .close() previewDataImageReader? .close() jpegImageReader? .close()Copy the code

Successively close CaptureSession, CameraDevice and ImageReader to release resources.

Recommendations for migrating from Camera1 to Camera2

If your project is using Camera1 and you are planning to migrate from Camera1 to Camera2, the following suggestions will help you:

  1. Camera1 strictly distinguishes between previewing and taking pictures, while Camera2 abstracts both processes into the Capture behavior, so it is recommended that you do not use Camera2 with too much thinking, to avoid thinking constraints that can not take full advantage of the flexible API of Camera2;
  2. Just like Camera1, some API calls for Camera2 are time consuming, so it is recommended that you use a separate thread to perform all camera operations and avoid calling Camera2 API directly on the main thread. HandlerThread is a good choice.
  3. You can think of Camera1 as a subset of Camera2, which means that anything Camera1 can do, Camera2 can do, and the other way around doesn’t necessarily work;
  4. If your application needs to be compatible with both Camera1 and Camera2, I personally recommend maintaining them separately, as Camera1’s poor API design is likely to make the most of Camera2’s flexible API. In addition, the pain of mixing two things that are completely incompatible in design may be far greater than the convenience, and writing redundant code may be more enjoyable;
  5. Officially, the performance of Camera2 is better, but running Camera2 on some of the earlier machines is not much better;
  6. When the Supported Hardware Level of the device is lower than FULL, it is recommended to use Camera1, because the Camera2 below FULL Level can provide almost the same functions as Camera1, so it is better to choose a more stable Camera1.