CameraX 1.3 Beta 版发布 | 为 Android 应用带来更强大的相机功能

CameraX 1.3 Beta 版发布 | 为 Android 应用带来更强大的相机功能

作者 / Donovan McMurray,Camera Developer Relations Engineer

CameraXAndroid Jetpack 的相机库,可帮助您为不同的 Android 版本和设备打造一致的出色体验,随着 CameraX 1.3 版本的推出,这个库的实用性也更上一层楼。CameraX 已用于越来越多的 Android 应用中,拥有非常广泛的应用场景,包括简单高效的相机交互、高级图像处理等等。

CameraX 1.3 提供更多高级功能。借助双并发摄像头功能,应用可以同时操控两个摄像头。此外,CameraX 1.3 提供一些新的 HDR 视频功能,让用户可以轻松拍出满意的作品。现在,您还可添加图形库转换效果 (例如使用 OpenGLVulkan) 到 Preview、ImageCapture 和 VideoCapture 用例中,以便采用滤镜和特效。此外,还有许多视频方面的功能改进等您发掘。

CameraX 1.3 Beta 版已正式推出,让我们即刻了解详情吧!

双并发摄像头

CameraX 让复杂的相机功能轻松易用,新的双并发摄像头功能也不例外。CameraX 会处理低层级细节工作,例如确保以正确的顺序打开和关闭并发摄像头视频串流。在 CameraX 中,绑定双并发摄像头与绑定单个摄像头并无太大差别。

首先,使用 getAvailableConcurrentCameraInfos() 确定哪些相机支持并发连接。常见情形是选择前置摄像头和后置摄像头。

var primaryCameraSelector: CameraSelector? = null
var secondaryCameraSelector: CameraSelector? = null

for (cameraInfos in cameraProvider.availableConcurrentCameraInfos) {
    primaryCameraSelector = cameraInfos.first {
        it.lensFacing == CameraSelector.LENS_FACING_FRONT
    }.cameraSelector
    secondaryCameraSelector = cameraInfos.first {
        it.lensFacing == CameraSelector.LENS_FACING_BACK
    }.cameraSelector

    if (primaryCameraSelector == null || secondaryCameraSelector == null) {
        // If either a primary or secondary selector wasn't found, reset both
        // to move on to the next list of CameraInfos.
        primaryCameraSelector = null
        secondaryCameraSelector = null
    } else {
        // If both primary and secondary camera selectors were found, we can
        // conclude the search.
        break
    }
}

if (primaryCameraSelector == null || secondaryCameraSelector == null) {
    // Front and back concurrent camera not available. Handle accordingly.
}

然后,为每个摄像头创建一个 SingleCameraConfig,依次传入各摄像头选择器、UseCaseGroupLifecycleOwner。然后,对您的 CameraProvider 调用 bindToLifecycle(),并将两项 SingleCameraConfig 加入列表中。

val primary = ConcurrentCamera.SingleCameraConfig(
    primaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val secondary = ConcurrentCamera.SingleCameraConfig(
    secondaryCameraSelector,
    useCaseGroup,
    lifecycleOwner
)

val concurrentCamera = cameraProvider.bindToLifecycle(
    listOf(primary, secondary)
)

为了确保兼容,双并发摄像头功能支持每个摄像头绑定最多 2 个用例,最高分辨率为 720p 或 1440p,具体取决于设备。

HDR 视频

CameraX 1.3 还新增了对 10 位视频流及 HDR 配置文件的支持,以便用户能够拍出细节、色彩、对比效果更胜以往的视频。您可以使用 VideoCapture.Builder.setDynamicRange() 方法进行多项配置。下面是一些预先配置的值:

  • HLG_10_BIT - 采用 HLG 编码的 10 位高动态范围。这是建议使用的 HDR 编码,因为所有支持 HDR 拍摄功能的设备都会支持 HLG10。您可参阅 检查 HDR 支持情况 指南以了解详情。

  • HDR10_10_BIT - 采用 HDR10 编码的 10 位高动态范围。

  • HDR10_PLUS_10_BIT - 采用 HDR10+ 编码的 10 位高动态范围。

  • DOLBY_VISION_10_BIT - 采用杜比视界 (Dolby Vision) 编码的 10 位高动态范围。

  • DOLBY_VISION_8_BIT - 采用杜比视界 (Dolby Vision) 编码的 8 位高动态范围。

首先,循环检查可获取的 CameraInfo,找出首个支持 HDR 的项。您可在此处添加其他摄像头选择标准。

var supportedHdrEncoding: DynamicRange? = null
val hdrCameraInfo = cameraProvider.availableCameraInfos
    .first { cameraInfo ->
        val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo)
        val supportedDynamicRanges = 
            videoCapabilities.getSupportedDynamicRanges()
        supportedHdrEncoding = supportedDynamicRanges.firstOrNull {
            it != DynamicRange.SDR  // Ensure an HDR encoding is found
        }
        return@first supportedDynamicRanges != null
    }

var cameraSelector = hdrCameraInfo?.cameraSelector ?: 
    CameraSelector.DEFAULT_BACK_CAMERA

然后,设置 RecorderVideoCapture 用例。如果您之前找到了 supportedHdrEncoding,还可调用 setDynamicRange() 为您的相机应用开启 HDR。

// Create a Recorder with Quality.HIGHEST, which will select the highest
// resolution compatible with the chosen DynamicRange.
val recorder = Recorder.Builder()
    .setQualitySelector(QualitySelector.from(Quality.HIGHEST))
    .build()
val videoCaptureBuilder = VideoCapture.Builder(recorder)
if (supportedHdrEncoding != null) {
    videoCaptureBuilder.setDynamicRange(supportedHdrEncoding!!)
}
val videoCapture = videoCaptureBuilder.build()

特效

CameraX 不仅让很多相机任务变得轻松,还提供了一些钩子 (hooks) 来实现高级或自定义功能。借助新的特效方法,您可将自定义图形库转换效果应用到 Preview、ImageCapture 和 VideoCapture 的帧中。

您可定义一个 CameraEffect 以将代码注入 CameraX 流水线并应用视觉特效,例如一项自定义的人像特效。当 通过构造函数创建您自己的 CameraEffect 时,您必须指定目标用例 (PREVIEWVIDEO_CAPTUREIMAGE_CAPTURE)。您还必须指定一项 SurfaceProcessor,为底层 Surface 实现 GPU 特效。建议使用 OpenGL 或 Vulkan 等图形 API 来访问 Surface。此进程会阻塞与 ImageCapture 关联的执行器 (Executor)。系统会默认使用一个内部 I/O 线程,或者您可使用 ImageCapture.Builder.setIoExecutor() 设置一个线程。注意: 性能取决于您的实现代码。对于 30fps 的输入,每帧的处理时间应在 30 毫秒以内,以避免丢帧。

您可使用一个 备选 CameraEffect 构造函数 来处理静态图像,因为在处理单幅图像时,可以接受更高的延迟。如果使用此构造函数,您需传入一项 ImageProcessor,执行相应进程方法以返回图像,详情您可以参阅 ImageProcessor.Request.getInputImage() 方法的说明。

在定义一个或多个 CameraEffect 后,您可将其添加到 CameraX 设置中。如果使用 CameraProvider,您应针对每个 CameraEffect 调用 UseCaseGroup.Builder.addEffect(),然后构建 UseCaseGroup 并将其传入 bindToLifecycle()。如果您使用 CameraController,您应将所有 CameraEffect 都传入 setEffects()

其他视频方面的功能

CameraX 1.3 还新增了许多备受期盼的其他视频功能,我们很高兴为其提供支持。

借助 VideoCapture.Builder.setMirrorMode(),您可控制何时让录像画面在水平方向镜像翻转。您可设置 MIRROR_MODE_OFF (默认)、MIRROR_MODE_ONMIRROR_MODE_ON_FRONT_ONLY (适用于匹配预览的镜像状态,该预览在前置摄像头上进行镜像)。注意: 对于仅使用前置摄像头的应用,MIRROR_MODE_ON 和 MIRROR_MODE_ON_FRONT_ONLY 是等效的。

PendingRecording.asPersistentRecording() 方法可防止视频因生命周期事件而停止,或因录像所用 Recorder 附加的 VideoCapture 用例被显式解除绑定而停止。如果您想绑定到另一个摄像头并使用该摄像头继续录像,便可使用这种方法。当启用这一选项时,您必须显式调用 Recording.stop()Recording.close() 来结束录像。

如果已通过 PendingRecording.withAudioEnabled() 将视频设为同时录制音频,可在录制过程中调用 Recording.mute()。您可以传入一个布尔值来指定将音频静音还是取消静音,这样 CameraX 便会在设为静音的部分中插入无声片段,以确保音频与视频保持一致。

AudioStats 新增了 getAudioAmplitude() 方法,用于向用户显示一个指示标识,以表明正在录制音频。在视频录制过程中,每个 VideoRecordEvent 都可用于访问 RecordingStats,而 RecordingStats 中包含 AudioStats 对象。

后续步骤

您可以阅读 CameraX 1.3 的完整版本 说明,详细了解本文所述的各项功能和其他信息!如果您已准备好体验 CameraX 1.3,可以将您项目的 CameraX 依赖项更新为 1.3.0-beta02 (或您阅读本文时的最新版本)。

如果想针对上述某项功能或 CameraX 的总体情况与我们分享反馈,您可以创建一个 CameraX issue。您也可以在 CameraX 讨论组 中与我们沟通交流。

版权声明

禁止一切形式的转载-禁止商用-禁止衍生 申请授权

脉脉不得语
脉脉不得语
Zhengzhou Website
Android Developer | https://androiddevtools.cn and https://androidweekly.io Funder | GDG Zhengzhou Funder & Ex Organizer | http://Toast.show(∞) Podcast Host

你已经成功订阅到 Android 开发技术周报
太棒了!接下来,完成检验以获得全部访问权限 Android 开发技术周报
欢迎回来!你已经成功登录了。
Unable to sign you in. Please try again.
成功!您的帐户已完全激活,您现在可以访问所有内容。
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.
🍗