Android Dev

Android Camera2 – Methods to Use the Camera2 API to Take Photographs and Movies

All of us use the digicam on our telephones and we use it a l-o-t. There are even some functions which have built-in the digicam as a characteristic.

On one finish, there’s a commonplace approach of interacting with the digicam. On the opposite, there’s a solution to customise your interplay with the digicam. This distinction is a crucial one to make. And that’s the place Camera2 is available in.

What’s Camera2?

Whereas it has been accessible since API stage 21, the Camera2 API has bought to be one of many extra complicated items of structure builders need to cope with.

This API and its predecessor have been put in place so builders might harness the facility of interacting with the digicam inside their functions.

Just like how there’s a solution to work together with the microphone or the amount of the machine, the Camera2 API provides you the instruments to work together with the machine’s digicam.

Normally, if you wish to person the Camera2 API, it will in all probability be for extra than simply taking an image or recording a video. It is because the API lets you might have in depth management of the digicam by exposing numerous courses that can should be configured per particular machine.

Even should you’ve handled the digicam beforehand, it’s such a drastic change from the previous digicam API, that you just may as nicely neglect all that .

There are a ton of sources on the market that attempt to showcase easy methods to use this API immediately, however a few of them could also be outdated and a few don’t current the entire image.

So, as an alternative of attempting to fill within the lacking items by your self, this text will (hopefully) be your one cease store for interacting with the Camera2 API.

Camera2 Use Instances

Earlier than we dive into something, it is very important perceive that should you solely wish to use the digicam to take an image or to file a video, you do not want to hassle your self with the Camera2 API.

The first cause to make use of the Camera2 API is that if your utility requires some customized interplay with the digicam or its performance.

If you’re fascinated with doing the previous as an alternative of the latter, I am going to counsel that you just go to the next documentation from Google:

  1. Take Photos
  2. Capture Video

There you will see that all the required steps you want to take to seize nice pictures and movies along with your digicam. However on this article, the principle focus can be on easy methods to use Camera2.

Now, there are some issues we have to add to our manifest file:

Digicam permissions:

<uses-permission android:identify="android.permission.CAMERA" />

Digicam characteristic:

<uses-feature android:identify="android.{hardware}.digicam" />

You’ll have to cope with checking if the digicam permission has been granted or not, however since this matter has been lined broadly, we gained’t be coping with that on this article.

Methods to Arrange the Camera2 API Elements

The Camera2 API introduces a number of new interfaces and courses. Let’s break down every of them so we are able to higher perceive easy methods to use them.

Image
Have a look at all these parts

First off, we’ll begin with the TextureView.

Camera2 TextureView Part

A TextureView is a UI part that you just use to show a content material stream (assume video). We have to use a TextureView to show the feed from the digicam, whether or not it is a preview or earlier than taking the image/video.

Two properties which might be essential to make use of relating to the TextureView are:

  • The SurfaceTexture discipline
  • The SurfaceTextureListener interface

The primary is the place the content material will get displayed, and the second has 4 callbacks:

  1. onSurfaceTextureAvailable
  2. onSurfaceTextureSizeChanged
  3. onSurfaceTextureUpdated
  4. onSurfaceTextureDestroyed
personal val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
        override enjoyable onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, peak: Int) {

        }
        override enjoyable onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, peak: Int) {

        }

        override enjoyable onSurfaceTextureDestroyed(texture: SurfaceTexture) {

        }
        override enjoyable onSurfaceTextureUpdated(texture: SurfaceTexture) {

        }
}

The primary callback is essential when utilizing the digicam. It is because we wish to be notified when the SurfaceTexture is offered so we are able to begin displaying the feed on it.

Remember that solely as soon as the TextureView is connected to a window does it grow to be accessible.

Interacting with the digicam has modified because the earlier API. Now, we now have the CameraManager. It is a system service that enables us to work together with CameraDevice objects.

The strategies you wish to pay shut consideration to are:

After we all know that the TextureView is offered and prepared, we have to name openCamera to open a connection to the digicam. This methodology takes in three arguments:

  1. CameraId – String
  2. CameraDevice.StateCallback
  3. A Handler

The CameraId argument signifies which digicam we wish to connect with. In your telephone, there are primarily two cameras, the entrance and the again. Every has its personal distinctive id. Normally, it’s both a zero or a one.

How can we get the digicam id? We use the CameraManager’s getCamerasIdList methodology. It can return an array of string sort of all of the digicam ids recognized from the machine.

val cameraManager: CameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraIds: Array<String> = cameraManager.cameraIdList
var cameraId: String = ""
for (id in cameraIds) {
    val cameraCharacteristics = cameraManager.getCameraCharacteristics(id)
    
    if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) 
      proceed
    }

    val previewSize = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!.getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.peak * it.width }!!
    val imageReader = ImageReader.newInstance(previewSize.width, previewSize.peak, ImageFormat.JPEG, 1)
    imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
    cameraId = id
}

The subsequent arguments are callbacks to the digicam state after we attempt to open it. If you consider it, there can solely be a number of outcomes for this motion:

  • The digicam manages to open efficiently
  • The digicam disconnects
  • Some error happens

And that’s what you will see that contained in the CameraDevice.StateCallback:

 personal val cameraStateCallback = object : CameraDevice.StateCallback() {
        override enjoyable onOpened(digicam: CameraDevice) {

        }

        override enjoyable onDisconnected(cameraDevice: CameraDevice) {

        }

        override enjoyable onError(cameraDevice: CameraDevice, error: Int) {
            val errorMsg = when(error) {
                ERROR_CAMERA_DEVICE -> "Deadly (machine)"
                ERROR_CAMERA_DISABLED -> "Machine coverage"
                ERROR_CAMERA_IN_USE -> "Digicam in use"
                ERROR_CAMERA_SERVICE -> "Deadly (service)"
                ERROR_MAX_CAMERAS_IN_USE -> "Most cameras in use"
                else -> "Unknown"
            }
            Log.e(TAG, "Error when attempting to attach digicam $errorMsg")
        }
    }

The third argument offers with the place this work will occur. Since we don’t wish to occupy the principle thread, it’s higher to do that work within the background.

That’s why we have to cross a Handler to it. It will be sensible to have this handler occasion instantiated with a thread of our selecting so we are able to delegate work to it.

personal lateinit var backgroundHandlerThread: HandlerThread
personal lateinit var backgroundHandler: Handler

 personal enjoyable startBackgroundThread() {
    backgroundHandlerThread = HandlerThread("CameraVideoThread")
    backgroundHandlerThread.begin()
    backgroundHandler = Handler(
        backgroundHandlerThread.looper)
}

personal enjoyable stopBackgroundThread() {
    backgroundHandlerThread.quitSafely()
    backgroundHandlerThread.be part of()
}

With the whole lot that we now have accomplished, we are able to now name openCamera:

cameraManager.openCamera(cameraId, cameraStateCallback,backgroundHandler)

Then within the onOpened callback, we are able to begin to cope with the logic on easy methods to current the digicam feed to the person by way of the TextureView.

Image
_Photo by [Unsplash](https://unsplash.com/@markusspiske?utm_source=medium&utm_medium=referral” rel=”photo-creator noopener”>Markus Spiske on <a href=”https://unsplash.com?utm_source=medium&utmmedium=referral” rel=”photo-source noopener)

Methods to Present a Preview of the Feed

We have our digicam (cameraDevice) and our TextureView to point out the feed. However we have to join them to one another so we are able to present a preview of the feed.

To try this, we can be utilizing the SurfaceTexture property of TextureView and we can be constructing a CaptureRequest.

val surfaceTexture : SurfaceTexture? = textureView.surfaceTexture 

val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId) 
val previewSize = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
  .getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.peak * it.width }!!

surfaceTexture?.setDefaultBufferSize(previewSize.width, previewSize.peak) 

val previewSurface: Floor = Floor(surfaceTexture)

captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) 
captureRequestBuilder.addTarget(previewSurface) 

cameraDevice.createCaptureSession(listOf(previewSurface, imageReader.floor), captureStateCallback, null) 

Within the code above, first we get the surfaceTexture from our TextureView. Then we use the cameraCharacteristics object to get the checklist of all output sizes. To get the specified measurement, we set it for the surfaceTexture.

Subsequent, we create a captureRequest the place we cross in TEMPLATE_PREVIEW. We add our enter floor to the captureRequest.

Lastly, we begin a captureSession with our enter and output surfaces, captureStateCallback, and cross in null for the handler

So what is that this captureStateCallback? Should you keep in mind the diagram from the start of this text, it’s a part of the CameraCaptureSession which we’re beginning. This object tracks the progress of the captureRequest with the next callbacks:

  • onConfigured
  • onConfigureFailed
personal val captureStateCallback = object : CameraCaptureSession.StateCallback() {
        override enjoyable onConfigureFailed(session: CameraCaptureSession) {

        }
        override enjoyable onConfigured(session: CameraCaptureSession) {

        }
}

When the cameraCaptureSession is configured efficiently, we set a repeating request for the session to permit us to point out the preview repeatedly.

To try this, we use the session object we get within the callback:

 session.setRepeatingRequest(captureRequestBuilder.construct(), null, backgroundHandler)

You’ll acknowledge our captureRequestBuilder object that we created earlier as the primary argument for this methodology. We enact the construct methodology so the ultimate parameter handed in is a CaptureRequest.

The second argument is a CameraCaptureSession.captureCallback listener, however since we don’t wish to do something with the captured photographs (since this can be a preview), we cross in null.

The third argument is a handler, and right here we use our personal backgroundHandler. That is additionally why we handed in null within the earlier part, because the repeating request will run on the background thread.

Image
_Photo by [Unsplash](https://unsplash.com/@dicky_juwono?utm_source=medium&utm_medium=referral” rel=”photo-creator noopener”>Dicky Jiang on <a href=”https://unsplash.com?utm_source=medium&utmmedium=referral” rel=”photo-source noopener)

Methods to Take a Image

Having a dwell preview of the digicam is superior, however most customers will in all probability wish to do one thing with it. A few of the logic that we’ll write to take an image can be much like what we did within the earlier part.

  1. We’ll create a captureRequest
  2. We’ll use an ImageReader and its listener to assemble the picture taken
  3. Utilizing our cameraCaptureSession, we are going to invoke the seize methodology
val orientations : SparseIntArray = SparseIntArray(4).apply {
    append(Floor.ROTATION_0, 0)
    append(Floor.ROTATION_90, 90)
    append(Floor.ROTATION_180, 180)
    append(Floor.ROTATION_270, 270)
}

val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
captureRequestBuilder.addTarget(imageReader.floor)

val rotation = windowManager.defaultDisplay.rotation
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, orientations.get(rotation))
cameraCaptureSession.seize(captureRequestBuilder.construct(), captureCallback, null)

However what is that this ImageReader? Effectively, an ImageReader supplies entry to picture information that’s rendered onto a floor. In our case, it’s the floor of the TextureView.

Should you take a look at the code snippet from the earlier part, you’ll discover we now have already outlined an ImageReader there.

val cameraManager: CameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraIds: Array<String> = cameraManager.cameraIdList
var cameraId: String = ""
for (id in cameraIds) {
    val cameraCharacteristics = cameraManager.getCameraCharacteristics(id)
    
    if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) 
      proceed
    }

    val previewSize = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!.getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.peak * it.width }!!
    val imageReader = ImageReader.newInstance(previewSize.width, previewSize.peak, ImageFormat.JPEG, 1)
    imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
    cameraId = id
}

As you’ll be able to see above, we instantiate an ImageReader by passing in a width and peak, the picture format we want our picture to be in and the variety of photographs that it may well seize.

A property the ImageReader class has is a listener referred to as onImageAvailableListener. This listener will get triggered as soon as a photograph is taken (since we handed in its floor because the output supply for our seize request).

val onImageAvailableListener = object: ImageReader.OnImageAvailableListener{
        override enjoyable onImageAvailable(reader: ImageReader) {
            val picture: Picture = reader.acquireLatestImage()
        }
    }

⚠️ Be certain to shut the picture after processing it or else you won’t be able to take one other picture.

Image
_Photo by [Unsplash](https://unsplash.com/@jakobowens1?utm_source=medium&utm_medium=referral” rel=”photo-creator noopener”>Jakob Owens on <a href=”https://unsplash.com?utm_source=medium&utmmedium=referral” rel=”photo-source noopener)

Methods to Document a Video

To file a video, we have to work together with a brand new object referred to as MediaRecorder. The media recorder object is in control of recording audio and video and we can be utilizing it just do that.

Earlier than we do something, we have to setup the media recorder. There are numerous configurations to cope with and they have to be within the right order or else exceptions can be thrown.

Under is an instance of a choice of configurations that can permit us to seize video (with out audio).

enjoyable setupMediaRecorder(width: Int, peak: Int) {
  val mediaRecorder: MediaRecorder = MediaRecorder()
  mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
  mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
  mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
  mediaRecorder.setVideoSize(videoSize.width, videoSize.peak)
  mediaRecorder.setVideoFrameRate(30)
  mediaRecorder.setOutputFile(PATH_TO_FILE)
  mediaRecorder.setVideoEncodingBitRate(10_000_000)
  mediaRecorder.put together()
}

Take note of the setOutputFile methodology because it expects a path to the file which can retailer our video. On the finish of setting all these configurations we have to name put together.

Observe that the mediaRecorder additionally has a begin methodology and we should name put together earlier than calling it.

After organising our mediaRecoder, we have to create a seize request and a seize session.

enjoyable startRecording() {
        val surfaceTexture : SurfaceTexture? = textureView.surfaceTexture
        surfaceTexture?.setDefaultBufferSize(previewSize.width, previewSize.peak)
        val previewSurface: Floor = Floor(surfaceTexture)
        val recordingSurface = mediaRecorder.floor
        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
        captureRequestBuilder.addTarget(previewSurface)
        captureRequestBuilder.addTarget(recordingSurface)

        cameraDevice.createCaptureSession(listOf(previewSurface, recordingSurface), captureStateVideoCallback, backgroundHandler)
    }

Just like organising the preview or taking {a photograph}, we now have to outline our enter and output surfaces.

Right here we’re making a Floor object from the surfaceTexture of the TextureView and in addition taking the floor from the media recorder. We’re passing within the TEMPLATE_RECORD worth when making a seize request.

Our captureStateVideoCallback is of the identical sort we used for the nonetheless picture, however contained in the onConfigured callback we name media recorder’s begin methodology.

val captureStateVideoCallback = object : CameraCaptureSession.StateCallback() {
      override enjoyable onConfigureFailed(session: CameraCaptureSession) {

      }

      override enjoyable onConfigured(session: CameraCaptureSession) {
          session.setRepeatingRequest(captureRequestBuilder.construct(), null, backgroundHandler)
          mediaRecorder.begin()
      }
  }

Now we’re recording a video, however how can we cease recording? For that, we can be utilizing the cease and reset strategies on the mediaRecorder object:

mediaRecorder.cease()
mediaRecorder.reset()

Conclusion

That was so much to course of. So should you made it right here, congratulations! There isn’t a approach round it – solely by getting your palms soiled with the code will you begin to perceive how the whole lot connects collectively.

You might be greater than inspired to take a look at all of the code featured on this article under :

Keep in mind that that is simply the tip of the iceberg in terms of the Camera2 API. There are a number of different issues you are able to do, like capturing a sluggish movement video, switching between the back and front cameras, controlling the main target, and rather more.

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button

Adblock Detected

Please consider supporting us by disabling your ad blocker