Android CameraX is designed to help you simplify the development of camera applications. As development of CameraX continues, camera app developers have shown us their passion and enthusiasm, and many great ideas have been incorporated into the current API, such as the laudable CameraX Extensions API. Recently we have taken input from the developer community and refactored the extensions, and now there is a new ExtensionsManager, you can use these extensions with just two lines of code! This article will describe how to use the Extensions API in your application.

CameraX Extensions

Android devices come with powerful cameras, and manufacturers have put a lot of effort into incorporating many cutting-edge functional features or special effects into these camera devices. In the past, these powerful features were only available from the device’s native camera app. Now, with the CameraX Extensions API, third-party developers can access these powerful camera features through a common, simple interface.

What CameraX Extensions covers

Version 1.0.0 of CameraX Extensions includes some of the most common built-in camera effects:

  • BOKEH (out-of-focus imaging) : Make foreground people sharper when taking photos in portrait mode.
  • HDR (High Dynamic Range) : Take photos with different Auto Exposure (AE) configurations to get the best results.
  • NIGHT : Capture the best still images in low-light conditions (usually at night).
  • FACE RETOUCH : Retouch face skin tones, contours, etc. when shooting still images.
  • AUTO (Auto) : Automatically adjusts the final image according to the surrounding scenery.

Let’s take a look at a couple of photos taken on an Android phone with the effects provided by the CameraX Extensions API enabled and disabled when taking photos.

Example of BOKEH mode

△ Fig. 1: The BOKEH effect is enabled for the photo on the right.

HDR mode example

△ Figure 2: The right photo is enabled with HDR effect.

NIGHT pattern example

△ Figure 3: The NIGHT special effect is enabled for the right photo.

The visual difference is obvious. You can use the CameraX Extensions API to implement these image effects in your own applications.

Now let’s see how to integrate CameraX’s API into your application.

Extensions API

In an existing CameraX application, you can first add CameraX Extensions by introducing the camera-extensions Jetpack library:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dependencies {
    // 与 Extensions 库版本号相匹配的 CameraX 核心库
    implementation 'androidx.camera:camera-core:1.1.0-alpha08'
    implementation 'androidx.camera:camera-camera2:1.1.0-alpha08'
    implementation 'androidx.camera:camera-lifecycle:1.1.0-alpha08'

    // CameraX Extensions 库
    implementation 'androidx.camera:camera-extensions:1.0.0-alpha28'

    // 其他依赖项
    implementation('androidx.concurrent:concurrent-futures-ktx:1.1.0')
        
}

Next, integrate Extensions by following these steps. 1:

  1. Get the [ExtensionsManager](https://developer.android.google.cn/reference/androidx/camera/extensions/ExtensionsManager

) instance. 2. check if the target device supports the required extension mode. 3. get an extension-enabled CameraSelector. 4. Call bindToLifecycle with the extension-enabled CameraSelector.

Get the ExtensionsManager instance

The first step is to get an instance of ExtensionsManager using the getInstance(Context) API of the extension library. This API returns a ListenableFuture, which we can use in the Kotlin pending function to get the result using await() to avoid blocking the main thread. (Note: Using await() on ListenableFuture requires the introduction of the androidx.concurrent:concurrent-futures-ktx: 1.1.0 dependency).

1
2
// 创建扩展管理器(使用 Jetpack Concurrent 库)
val extensionsManager =  ExtensionsManager.getInstance(context).await()

With ExtensionsManager, you can determine whether a device supports a particular extension mode and get a CameraSelector for it with extensions enabled. Please note the following points:

  • ExtensionsManager is a process-wide global resource: only one instance of ExtensionsManager exists in a process.
  • ExtensionsManager always exists: CameraX provides a valid instance of ExtensionsManager regardless of whether the underlying device supports extensions or not.

Check extended mode availability

With the ExtensionsManager, use the isExtensionAvailable(CameraProvider, CameraSelector, int) function to check the availability of the extension: If any camera filtered by CameraSelector exists on the device that supports the queried extension, then true is returned, otherwise false is returned.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 获取相机设备来检查是否支持扩展
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

// 检查是否支持 BOKEH
 if (extensionsManager.isExtensionAvailable(
    cameraProvider,
    cameraSelector,
    ExtensionMode.BOKEH
   )) {
   ...
}

Get the extended CameraSelector enabled

Once you have confirmed that the device supports this extension mode, you can use the getExtensionEnabledCameraSelector(CameraProvider, CameraSelector, int) function to get an extension-enabled CameraSelector. This function returns the extension-enabled CameraSelector, which contains all the details about the specified extension mode.

1
2
3
val bokehCameraSelector = extensionsManager
                          .getExtensionEnabledCameraSelector(
                              cameraProvider, cameraSelector, ExtensionMode.BOKEH)

Call bindToLifecycle() with the extended CameraSelector enabled

The last step is to use bindToLifecycle() to bind your use case to an extended CameraSelector. Use the extended CameraSelector as you would a normal CameraSelector, for example with DEFAULT_BACK_CAMERA or DEFAULT_FRONT_CAMERA. When using the CameraSelector binding use case with extensions enabled, CameraX enables the specified extension mode directly on the camera. For example, when bound to Preview, the extended effect is applied to the preview or to the image captured by the bound ImageCapture.

1
2
3
4
5
6
7
8
9
// 将开启了 BOKEH 的相机选择器绑定到用例上
val imageCapture = ImageCapture.Builder().build()
val preview = Preview.Builder().build()
cameraProvider.bindToLifecycle(
                lifecycleOwner,
                bokehCameraSelector,
                imageCapture,
                preview
            )

Sample code using Extensions API

The full code of the Extensions API example is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
fun onCreate() {
    lifecycleScope.launch {
        // 创建 cameraProvider
        val cameraProvider = ProcessCameraProvider.getInstance(context).await() 

        // 创建 extensionsManager(使用 Jetpack Concurrent 库)
        val extensionsManager = 
                ExtensionsManager.getInstance(context).await()

        // 获取相机设备来检查是否支持扩展        
        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

        // 检查是否支持 BOKEH
        if (extensionsManager.isExtensionAvailable(
                cameraProvider,
                cameraSelector,
                ExtensionMode.BOKEH
            )) {
            // 在启用不同扩展模式之前解除所有用例的绑定
            cameraProvider.unbindAll()

            // 获取启用了 BOKEH 的相机选择器
            val bokehCameraSelector = extensionsManager
                    .getExtensionEnabledCameraSelector(
                cameraProvider,
                cameraSelector,
                ExtensionMode.BOKEH
            )

            // 将开启了 BOKEH 的相机选择器绑定到用例上
            val imageCapture = ImageCapture.Builder().build()
            val preview = Preview.Builder().build()
            cameraProvider.bindToLifecycle(
                lifecycleOwner,
                bokehCameraSelector,
                imageCapture,
                preview
            )
        }
    }
}

Extensions API dependency on core modules

The CameraX Extensions API is implemented in the camera-extensions library and it relies on the CameraX core modules ( core, camera2 and lifecycle). When using CameraX Extensions, be sure to use the version from the same distribution as the CameraX core module you are using. For example, to use camera-extensions:1.0.0-alpha28, you must include the 1.0.0-alpha08 versions of camera-lifecycle, camera-core and camera-camera2 in your application’s dependency list, as they were released on released in the same package on August 18, 2021.

Devices that support expansion

In order to be able to use the CameraX Extensions API, device manufacturers need to implement the CameraX Vendor Extensions Interface. You can find a partial list of devices that support the CameraX Extensions API on the CameraX Devices page. Please note that this is not an exhaustive list. If your device is listed, but the availability check returns false, you may need to update your device to the latest ROM version from the manufacturer.

In addition to the list of devices that support extensions, starting with Android 12, you can also determine if your device supports CameraX Extensions by checking the Android property ro.camerax.extensions.enabled.

Remove old Extensions API

An older version of the Extensions API released in August 2019 is now deprecated. This older version of the Extensions API provides extender classes that require extension-related configuration to be applied to each Preview and ImageCapture use case. The old extender design could cause developers to forget to enable extension mode on Preview or ImageCapture and could lead to unintended behavior.

The new CameraX Extensions library was introduced in 1.0.0-alpha26. The newer Extensions API makes it easier to use by switching the extension binding from the use case to the target camera. Please be sure to migrate to take advantage of the new Extensions API.

A special thanks to the amazing Android camera developers and device manufacturers who helped implement the CameraX Extensions API! If you’d like to stay up to date on CameraX developments, please join the Android CameraX Discussion Group.