What’s new in CameraX 1.4.0 and a sneak peek of Jetpack Compose assist

What’s new in CameraX 1.4.0 and a sneak peek of Jetpack Compose assist



Posted by Scott Nien – Software program Engineer (scottnien@)

Get able to degree up your Android digital camera apps! CameraX 1.4.0 simply dropped with a load of superior new options and enhancements. We’re speaking expanded HDR capabilities, preview stabilization and the versatile impact framework, and a complete lot of cool stuff to discover. We may also discover methods to seamlessly combine CameraX with Jetpack Compose! Let’s dive in and see how these enhancements can take your digital camera app to the subsequent degree.

HDR preview and Extremely HDR

A split-screen image compares Standard Dynamic Range (SDR) and High Dynamic Range (HDR) image quality side-by-side using a singular image of a detailed landscape. The HDR side is more vivid and vibrant.

Excessive Dynamic Vary (HDR) is a game-changer for pictures, capturing a wider vary of sunshine and element to create stunningly sensible photographs. With CameraX 1.3.0, we introduced you HDR video recording capabilities, and now in 1.4.0, we’re taking it even additional! Prepare for HDR Preview and Extremely HDR. These thrilling additions empower you to ship a good richer visible expertise to your customers.

HDR Preview

This new characteristic permits you to allow HDR on Preview with no need to bind a VideoCapture use case. That is particularly helpful for apps that use a single preview stream for each displaying preview on show and video recording with an OpenGL pipeline.

To completely allow the HDR, it’s essential to guarantee your OpenGL pipeline is able to processing the precise dynamic vary format after which examine the digital camera functionality.

See following code snippet for instance to allow HLG10 which is the baseline HDR normal that machine makers should assist on cameras with 10-bit output.

// Declare your OpenGL pipeline supported dynamic vary format. 
val openGLPipelineSupportedDynamicRange = setOf(
     DynamicRange.SDR, 
     DynamicRange.HLG_10_BIT
)
// Test digital camera dynamic vary capabilities. 
val isHlg10Supported =  
     cameraProvider.getCameraInfo(cameraSelector)
           .querySupportedDynamicRanges(openGLPipelineSupportedDynamicRange)
           .accommodates(DynamicRange.HLG_10_BIT)

val preview = Preview.Builder().apply {
     if (isHlg10Supported) {
        setDynamicRange(DynamicRange.HLG_10_BIT)
     }
}

Extremely HDR

Introducing Extremely HDR, a brand new format in Android 14 that lets customers seize stunningly sensible images with unbelievable dynamic vary. And the most effective half? CameraX 1.4.0 makes it extremely straightforward so as to add Extremely HDR seize to your app with just some traces of code:

val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
val cameraInfo = cameraProvider.getCameraInfo(cameraSelector)
val isUltraHdrSupported = 
      ImageCapture.getImageCaptureCapabilities(cameraInfo)
                  .supportedOutputFormats
                  .accommodates(ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR)

val imageCapture = ImageCapture.Builder().apply {
    if (isUltraHdrSupported) {
        setOutputFormat(ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR)
    }
}.construct()

Jetpack Compose assist

Whereas this submit focuses on 1.4.0, we’re excited to announce the Jetpack Compose assist in CameraX 1.5.0 alpha. We’re including assist for a Composable Viewfinder constructed on high of AndroidExternalSurface and AndroidEmbeddedExternalSurface. The CameraXViewfinder Composable hooks up a show floor to a CameraX Preview use case, dealing with the complexities of rotation, scaling and Floor lifecycle so that you don’t must.

// in construct.gradle 
implementation ("androidx.digital camera:camera-compose:1.5.0-alpha03")


class PreviewViewModel : ViewModel() {
    personal val _surfaceRequests = MutableStateFlow<SurfaceRequest?>(null)

    val surfaceRequests: StateFlow<SurfaceRequest?>
        get() = _surfaceRequests.asStateFlow()

    personal enjoyable produceSurfaceRequests(previewUseCase: Preview) {
        // All the time publish new SurfaceRequests from Preview
        previewUseCase.setSurfaceProvider { newSurfaceRequest ->
            _surfaceRequests.worth = newSurfaceRequest
        }
    }

    // ...
}

@Composable
enjoyable MyCameraViewfinder(
    viewModel: PreviewViewModel,
    modifier: Modifier = Modifier
) {
    val currentSurfaceRequest: SurfaceRequest? by
        viewModel.surfaceRequests.collectAsState()

    currentSurfaceRequest?.let { surfaceRequest ->
        CameraXViewfinder(
            surfaceRequest = surfaceRequest,
            implementationMode = ImplementationMode.EXTERNAL, // Or EMBEDDED
            modifier = modifier        
        )
    }
}

Kotlin-friendly APIs

CameraX is getting much more Kotlin-friendly! In 1.4.0, we have launched two new droop features to streamline digital camera initialization and picture seize.

// CameraX initialization 
val cameraProvider = ProcessCameraProvider.awaitInstance()

val imageProxy = imageCapture.takePicture() 
// Processing imageProxy
imageProxy.shut()

Preview Stabilization and Mirror mode

Preview Stabilization

Preview stabilization mode was added in Android 13 to allow the stabilization on all non-RAW streams, together with previews and MediaCodec enter surfaces. In comparison with the earlier video stabilization mode, which can have inconsistent FoV (Area of View) between the preview and recorded video, this new preview stabilization mode ensures consistency and thus offers a greater person expertise. For apps that report the preview instantly for video recording, this mode can be the one method to allow stabilization.

Observe the code under to allow preview stabilization. Please word that after preview stabilization is turned on, it isn’t solely utilized to the Preview but additionally to the VideoCapture whether it is certain as effectively.

val isPreviewStabilizationSupported =  
    Preview.getPreviewCapabilities(cameraProvider.getCameraInfo(cameraSelector))
        .isStabilizationSupported
val preview = Preview.Builder().apply {
    if (isPreviewStabilizationSupported) {
      setPreviewStabilizationEnabled(true)
    }
}.construct()

MirrorMode

Whereas CameraX 1.3.0 launched mirror mode for VideoCapture, we have now introduced this helpful characteristic to Preview in 1.4.0. That is particularly helpful for units with outer shows, permitting you to create a extra pure selfie expertise when utilizing the rear digital camera.

To allow the mirror mode, merely name Preview.Builder.setMirrorMode APIs. This characteristic is supported for Android 13 and above.

Actual-time Impact

CameraX 1.3.0 launched the CameraEffect framework, providing you with the facility to customise your digital camera output with OpenGL. Now, in 1.4.0, we’re taking it a step additional. Along with making use of your individual customized results, now you can leverage a set of pre-built results offered by CameraX and Media3, making it simpler than ever to boost your app’s digital camera options.

Overlay Impact

The brand new camera-effects artifact goals to offer ready-to-use impact implementations, beginning with the OverlayEffect. This impact allows you to draw overlays on high of digital camera frames utilizing the acquainted Canvas API.

The next pattern code reveals methods to detect the QR code and draw the form of the QR code as soon as it’s detected.

By default, drawing is carried out in floor body coordinates. However what if it’s essential to use digital camera sensor coordinates? No drawback! OverlayEffect offers the Body#getSensorToBufferTransform perform, permitting you to use the mandatory transformation matrix to your overlayCanvas.

On this instance, we use CameraX’s MLKit Imaginative and prescient APIs (MlKitAnalyzer) and specify COORDINATE_SYSTEM_SENSOR to acquire QR code nook factors in sensor coordinates. This ensures correct overlay placement no matter machine orientation or display facet ratio.

// in construct.gradle 
implementation ("androidx.digital camera:camera-effects:1.4.1}")      
implementation ("androidx.digital camera:camera-mlkit-vision:1.4.1")

var qrcodePoints: Array<Level>? = null
var qrcodeTimestamp = 0L
val qrcodeBoxEffect 
    = OverlayEffect(
        PREVIEW /* utilized on the preview solely */,
        5, /* maintain a number of frames within the queue so we are able to match evaluation consequence 
              with preview body */, 
        Handler(Looper.getMainLooper()), {}
      )

enjoyable initCamera() {
    qrcodeBoxEffect.setOnDrawListener { body ->
        if(body.timestamp != qrcodeTimestamp) {
            // Don't change the drawing if the body doesn’t match the evaluation 
            // consequence.
            return@setOnDrawListener true
        }
        body.overlayCanvas.drawColor(Coloration.TRANSPARENT, PorterDuff.Mode.CLEAR)
        qrcodePoints?.let {
            // Utilizing sensor coordinates to attract.
            body.overlayCanvas.setMatrix(body.sensorToBufferTransform)
            val path = android.graphics.Path().apply {
                it.forEachIndexed { index, level ->
                    if (index == 0) {
                        moveTo(level.x.toFloat(), level.y.toFloat())
                    } else {
                        lineTo(level.x.toFloat(), level.y.toFloat())
                    }
                 }
                 lineTo(it[0].x.toFloat(), it[0].y.toFloat())
            }
            body.overlayCanvas.drawPath(path, paint)
        }
        true
    }

    val imageAnalysis = ImageAnalysis.Builder()
        .construct()
        .apply {
            setAnalyzer(executor,
                MlKitAnalyzer(
                    listOf(barcodeScanner!!),
                    COORDINATE_SYSTEM_SENSOR,
                    executor
                ) { consequence ->
                    val barcodes = consequence.getValue(barcodeScanner!!)
                    qrcodePoints = 
                        barcodes?.takeIf { it.dimension > 0}?.get(0)?.cornerPoints
                    // monitor the timestamp of the evaluation consequence and launch the 
                    // preview body.
                    qrcodeTimestamp = consequence.timestamp
                    qrcodeBoxEffect.drawFrameAsync(qrcodeTimestamp)
                }
            )
        }

    val useCaseGroup = UseCaseGroup.Builder()
          .addUseCase(preview)
          .addUseCase(imageAnalysis)
          .addEffect(qrcodeBoxEffect)
          .construct()

    cameraProvider.bindToLifecycle(
        lifecycleOwner, cameraSelector, usecaseGroup)
  }

Here’s what the impact appears to be like like:

A black and white view from inside a coffee shop looking out at a city street.  The bottom of the photo shows the edge of a table with a laptop and two buttons labeled 'BACK' and 'RECORD'

Display screen Flash

Taking selfies in low mild simply bought simpler with CameraX 1.4.0! This launch introduces a strong new characteristic: display flash. As a substitute of counting on a standard LED flash which most selfie cameras don’t have, display flash cleverly makes use of your cellphone’s show. By momentarily turning the display vivid white, it offers a burst of illumination that helps seize clear and vibrant selfies even in difficult lighting situations.

Integrating display flash into your CameraX app is versatile and easy. You will have two essential choices:

      1. Implement the ScreenFlash interface: This offers you full management over the display flash habits. You’ll be able to customise the colour, depth, length, and another facet of the flash. That is supreme in case you want a extremely tailor-made answer.

      2. Use the built-in implementation: For a fast and straightforward answer, leverage the pre-built display flash performance in ScreenFlashView or PreviewView. This implementation handles all of the heavy lifting for you.

In the event you’re already utilizing PreviewView in your app, enabling display flash is extremely easy. Simply allow it instantly on the PreviewView occasion. In the event you want extra management or aren’t utilizing PreviewView, you need to use ScreenFlashView instantly.

This is a code instance demonstrating methods to allow display flash:

// case 1: PreviewView + CameraX core API.
previewView.setScreenFlashWindow(exercise.getWindow());
imageCapture.screenFlash = previewView.screenFlash
imageCapture.setFlashMode(ImageCapture.FLASH_MODE_SCREEN)

// case 2: PreviewView + CameraController
previewView.setScreenFlashWindow(exercise.getWindow());
cameraController.setImageCaptureFlashMode(ImageCapture.FLASH_MODE_SCREEN);

// case 3 : use ScreenFlashView 
screenFlashView.setScreenFlashWindow(exercise.getWindow());
imageCapture.setScreenFlash(screenFlashView.getScreenFlash());
imageCapture.setFlashMode(ImageCapture.FLASH_MODE_SCREEN);

Digital camera Extensions new options

Digital camera Extensions APIs goal to assist apps to entry the cutting-edge capabilities beforehand out there solely on built-in digital camera apps. And the ecosystem is rising quickly! In 2024, we have seen main gamers like Pixel, Samsung, Xiaomi, Oppo, OnePlus, Vivo, and Honor all embrace Digital camera Extensions, notably for Night time Mode and Bokeh Mode. CameraX 1.4.0 takes this even additional by including assist for brand-new Android 15 Digital camera Extensions options, together with:

    • Postview: Gives a preview of the captured picture nearly immediately earlier than the long-exposure pictures are accomplished
    • Seize Course of Progress: Shows a progress indicator so customers understand how lengthy capturing and processing will take, enhancing the expertise for options like Night time Mode
    • Extensions Energy: Permits customers to fine-tune the depth of the utilized impact

Beneath is an instance of the improved UX that makes use of postview and seize course of progress options on Samsung S24 Extremely.

moving image capturing process progress features on Samsung S24 Ultra

to understand how this may be carried out? See the pattern code under:

val extensionsCameraSelector =  
    extensionsManager
        .getExtensionEnabledCameraSelector(DEFAULT_BACK_CAMERA, extensionMode)
val isPostviewSupported = ImageCapture.getImageCaptureCapabilities(                   
    cameraProvider.getCameraInfo(extensionsCameraSelector)
).isPostviewSupported
val imageCapture = ImageCapture.Builder().apply {
    setPostviewEnabled(isPostviewSupported)
}.construct()

imageCapture.takePicture(outputfileOptions, executor,  
    object : OnImageSavedCallback {
        override enjoyable onImageSaved(outputFileResults: OutputFileResults) {
            // remaining picture saved. 
        }
        override enjoyable onPostviewBitmapAvailable(bitmap: Bitmap) {
            // Postview bitmap is obtainable.
        }
        override enjoyable onCaptureProcessProgressed(progress: Int) {
            // seize course of progress replace 
        }
}

Essential: In case your app bumped into the CameraX Extensions difficulty on Pixel 9 sequence units, please use CameraX 1.4.1 as an alternative. This launch fixes a crucial difficulty that prevented Night time Mode from working appropriately with takePicture.

What’s Subsequent

We hope you take pleasure in this new launch. Our mission is to make digital camera improvement a pleasure, eradicating the friction and ache factors so you possibly can deal with innovation. With CameraX, you possibly can simply harness the facility of Android’s digital camera capabilities and construct really wonderful app experiences.

Have questions or wish to join with the CameraX crew? Be part of the CameraX developer dialogue group or file a bug report:

We will’t wait to see what you create!

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.