产品动态

超越单一功能:CameraX 1.5 保证功能组合

6 分钟阅读时间
Tahsin Masrur
软件工程师

现代相机应用的特点是功能强大且相互重叠。用户希望录制具有惊艳 HDR 效果的视频,以 60 FPS 的帧速率捕捉流畅的动作,并使用预览防抖功能获得流畅的画面,而且通常希望同时实现这些功能。

作为开发者,我们知道现实情况更加复杂。如何保证特定设备实际上支持给定的组合?到目前为止,启用多项功能通常是一种冒险。您可以检查对单个功能的支持情况,但将它们组合在一起可能会导致未定义的行为,或者更糟糕的是,导致相机会话失败。 这种不确定性迫使开发者采取保守的做法,这会阻止功能强大的设备上的用户获得最佳体验。

例如,很少有高端设备能够可靠地同时支持 HDR 和 60 FPS 视频。因此,大多数应用都会避免同时启用这两项功能,以防止在大多数手机上出现不良的用户体验。

为了解决这个问题,我们推出了 CameraX 中的功能组 - 一种旨在消除这种猜测的新 API。您现在可以在配置相机之前查询是否支持特定功能组合,或者只需告知 CameraX 您的优先级,让它为您启用最受支持的组合。

CameraX 新手

在我们深入了解新的功能组 API 之前,让我们快速回顾一下 CameraX 是什么。CameraX 是一个 Jetpack 支持库,旨在帮助您简化相机应用的开发工作。它提供一致且易于使用的 API Surface,适用于大多数 Android 设备,并可向后兼容至 Android 6.0(API 级别 23)。如果您是 CameraX 新手,我们建议您查看官方文档并尝试Codelab以开始使用。

您可以使用功能组 API 构建哪些内容

您不再需要冒险尝试功能组合,并且可以放心地提供最佳相机体验,例如在功能强大的硬件(例如 Pixel 10 Pro)上同时使用 HDR 和 60 FPS 视频,同时优雅地避免在无法支持该组合的设备上出现错误。

unnamed.png

Pixel 10 Pro 同时启用 HDR 和 60 FPS

unnamed (1).png

在无法同时运行 HDR 和 60 FPS 的旧设备上,仅启用 HDR,而停用 60 FPS 选项。

借助功能组 API,您可以:

  • 构建更智能的动态界面: 根据实时硬件支持情况,智能地启用或停用界面中的设置。例如,如果用户启用了 HDR,而该设备不支持该组合,您可以立即将 60 FPS 选项灰显并停用。
unsupported-features-disabled.gif
  • 提供可靠的“高质量”模式:  使用所需功能的优先列表配置相机。CameraX 会自动查找并启用任何给定设备最受支持的组合,确保获得出色的结果,而无需复杂的设备专用逻辑。
  • 防止相机会话失败: 通过提前验证支持情况,您可以防止相机尝试配置不受支持的组合,从而消除常见的崩溃来源并提供流畅的用户体验。

工作原理:核心组件

新 API 以 SessionConfigCameraInfo 的关键新增功能为中心。

  1. GroupableFeature:此 API 引入了一组预定义的可分组功能,例如 HDR_HLG10、FPS_60、PREVIEW_STABILIZATION 和 IMAGE_ULTRA_HDR。由于计算限制,只有一组特定的功能可以与此 API 提供的高度可靠性分组。 我们正在积极努力扩展此列表,并将在未来的版本中引入对更多功能的支持。
  2. 新 SessionConfig 参数: 此类用于启动相机会话,现在接受两个新参数:
    • requiredFeatureGroup:用于必须 支持的功能,以确保配置成功 - 非常适合用户明确启用的功能,例如切换“HDR”开关。为了确保确定性和一致的体验,如果请求的组合不受支持,bindToLifecycle 调用将抛出 IllegalArgumentException,而不是默默忽略功能请求。应使用 CameraInfo#isFeatureGroupSupported API(详见下文)提前查询此结果。
    • preferredFeatureGroup:用于所需但可选的功能,例如当您想要实现默认的“高质量”模式时。您需要提供一个按优先级排序的所需功能列表,CameraX 会自动启用设备支持的优先级最高的组合。
  3. **CameraInfo#isFeatureGroupSupported()** :这是用于明确检查是否支持功能组的核心查询方法,非常适合在应用界面中仅向用户提供受支持的功能选项。您需要向其传递 SessionConfig,它会返回一个布尔值,指明是否支持该组合。如果您打算将 SessionConfig 与所需功能绑定,应先使用此 API 确保其受支持。

实际应用中的实现

让我们看看如何使用这些组件来打造更好的相机体验。

场景 1:“尽力而为”高质量模式

如果您想默认启用最佳功能,可以向 preferredFeatureGroup 提供优先列表。在此示例中,我们告知 CameraX 优先考虑 HDR,然后是 60 FPS,最后是预览防抖。CameraX 会处理检查所有可能组合并选择设备支持的最佳组合的复杂性。

例如,如果设备可以同时处理 HDR 和 60 FPS,但不能同时处理预览防抖,CameraX 将启用前两项并舍弃第三项。这样,您就可以获得最佳体验,而无需编写复杂的设备专用检查。

  cameraProvider.bindToLifecycle(

    lifecycleOwner,

    cameraSelector,

    SessionConfig(

        useCases = listOf(preview, videoCapture),

        // The order of features in this list determines their priority. 

        // CameraX will enable the best-supported combination based on these

        // priorities: HDR_HLG10 > FPS_60 > Preview Stabilization.  

        preferredFeatureGroup =

           listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),

    ).apply {

        // (Optional) Get a callback with the enabled features

        // to update your UI. 

        setFeatureSelectionListener { selectedFeatures ->

            updateUiIndicators(selectedFeatures)

        }

    }

)

对于此代码段,CameraX 将尝试按以下优先级顺序启用功能组合,并选择设备完全支持的第一个组合:

  1. HDR + 60 FPS + 预览防抖
  2. HDR + 60 FPS
  3. HDR + 预览防抖
  4. HDR
  5. 60 FPS + 预览防抖
  6. 60 FPS
  7. 预览防抖
  8. 以上功能均不支持

场景 2:构建响应式界面

如需创建响应用户选择并防止用户选择不受支持的功能组合的界面,您可以直接查询支持情况。以下函数会检查哪些功能与用户的 当前选择不兼容,以便您停用相应的界面元素。

  /**

 * Returns a list of features that are NOT supported in combination

 * with the currently selected features.

 */

fun getUnsupportedFeatures(

    currentFeatures: Set<GroupableFeature>

): Set<GroupableFeature> {

    val unsupportedFeatures = mutableSetOf<GroupableFeature>()

    val appFeatureOptions = setOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION)


    // Iterate over every available feature option in your app. 

    appFeatureOptions.forEach { featureOption ->

        // Skip features the user has already selected. 

        if (currentFeatures.contains(featureOption)) return@forEach


        // Check if adding this new feature is supported. 

        val isSupported = cameraInfo.isFeatureGroupSupported(

            SessionConfig(

                useCases = useCases,

                // Check the new feature on top of existing ones.

                requiredFeatureGroup = currentFeatures + featureOption

            )

        )


        if (!isSupported) {

            unsupportedFeatures.add(featureOption)

        }

    }


    return unsupportedFeatures

}

然后,您可以将此逻辑连接到 ViewModel 或界面控制器,以响应用户输入并使用保证可正常工作的配置重新绑定相机。

  // Invoked when user turns some feature on/off.

fun onFeatureChange(currentFeatures: Set<GroupableFeature>) {

    // Identify features that are unsupported with the current selection.

    val unsupportedFeatures = getUnsupportedFeatures(currentFeatures)



    // Update app UI so that users can't enable them.

    updateDisabledFeatures(unsupportedFeatures)



    // Since the UI now only allows selecting supported feature combinations, 

    // `currentFeatures` is always valid. This allows setting

    // `requiredFeatureGroup` directly, without needing to re-check for

    // support or set a feature selection listener.  

    cameraProvider.bindToLifecycle(

        lifecycleOwner,

        cameraSelector,

        SessionConfig(

            useCases = listOf(preview, videoCapture),

            requiredFeatureGroup = currentFeatures,

        )

    )

}

如需在实际应用中查看这些概念,您可以探索 我们的内部测试应用。它提供了上述“尽力而为”和“响应式界面”场景的完整实现。

请注意:这是一个测试应用,而不是正式支持的示例。虽然它是功能组 API 的绝佳参考,但尚未针对生产用途进行完善。

立即开始使用

功能组 API 消除了使用高级相机功能时的不确定性。通过提供一种确定性的方式来查询功能支持情况,您可以放心地构建功能更强大、更可靠的相机应用。

该 API 在 CameraX 1.5 中以实验性功能的形式提供,计划在 1.6 版本中完全稳定,并且会提供更多支持和改进。

如需了解详情,请查看官方文档。我们迫不及待地想看看您会创造出什么,并期待您的反馈。请通过以下渠道分享您的想法并报告任何问题:

继续阅读