CameraX 是一个 Jetpack 支持库,旨在帮助您简化相机应用的开发工作。它提供一致且易用的 API 接口,适用于大多数 Android 设备,并可向后兼容至 Android 5.0 (API 级别 21)。我们将在本文中介绍 CameraX 1.1 的多项功能,比如视频功能。
如果您更喜欢通过视频了解此内容,请在此处查看:
Bilibili 视频链接
https://www.bilibili.com/video/BV1z3411Y7UJ/
CameraX
https://developer.android.google.cn/training/camerax
CameraX 概览
CameraX 是一个为了简化编写相机应用而设计的支持库,它所提供的高级 API 可以让开发者专注于和用户交互而非相机的内部实现。我们一直在探索并修复其背后复杂的兼容性问题,让每个新版本都得以在更多的设备上稳定运行。
CameraX 可以很方便地实现普通照片视频的拍摄功能,而 Camera2 则可以对拍摄流程进行特殊控制,例如实现多重曝光或全手动捕获;
CameraX 旨在消除不同设备间的差异并在不同设备上进行了测试,而 Camera2 则需要应用来管理不同设备间的差异并测试其行为;
CameraX 提升了代码开发速度,让您更专注于用户界面和体验流程,而 Camera2 则用于更深入地开发以创造基于相机的定制功能;
CameraX 发布新版本频繁,而 Camera2 则随着 Android 的版本而更新;
CameraX 可以在您不熟悉相机的情况下也能够进行开发,而 Camera2 则需要您对相机的专业知识有更深层次的了解。
CameraX 基于主要的使用场景来构建,比如实时预览相机、检索缓冲区以进行分析和拍摄照片,在 CameraX 1.1 版本中还加入了视频拍摄功能。我们来看一个简单的 CameraX 示例:
fun bindPreview(cameraProvider : ProcessCameraProvider) {
// 使用 CameraX 创建 Preview 用例
var preview : Preview = Preview.Builder().build()
// 创建 cameraSelector,它会在设备上搜索所需的相机
var cameraSelector : CameraSelector = CameraSelector.Builder()
// 在本例中,我们选择搜索后置相机
.requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
// 从 CameraX 的 CameraView 包中获取 previewView 的句柄
// 利用此方法可以轻松的将相机内容添加到视图上
preview.setSurfaceProvider(previewView.getSurfaceProvider())
// 将 preview 与其生命周期绑定
var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner,
cameraSelector, preview)
}
△ CameraX 代码示例
CameraX 是生命周期感知型组件,这意味着它将自动处理应用的生命周期事件来实现开始、停止、暂停和恢复。现在,应用启动时屏幕上便会显示实时预览。
我们已于 2021 年 5 月发布了 1.0 稳定版本,目前正在开发 1.1 Alpha 版本并且很快将会进入 Beta 阶段。并且我们一如既往地不断为新增设备推出兼容性修复程序,例如 1.0.1 和 1.0.2。
视频拍摄
// 创建 Recorder
val recorder = Recorder.Builder()
// 我们可以在此处使用 setQualitySelector 设置视频质量
.setQualitySelector(...)
.build()
// 使用新创建的 Recorder 创建 VideoCapture
val videoCapture = VideoCapture.withOutput(recorder)
// 将其与生命周期绑定
cameraProvider.bindToLifecycle(
this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
// 设定 VideoRecordEvent 监听器
val videoRecordEventListener = Consumer<VideoRecordEvent>{
when (it) {
is VideoRecordEvent.Start -> {}
is VideoRecordEvent.Finalize -> {}
is VideoRecordEvent.Status -> {
// status 事件将会在录制时持续更新
val stats: RecordingStats = it.recordingStats
// RecordingStats 中包含录制文件的尺寸和时长
}
is VideoRecordEvent.Pause -> {}
is VideoRecordEvent.Resume -> {}
// 指定输出
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
.setContentValues(contentValues)
.build()
// 准备录制
val activeRecording = videoCapture.output.prepareRecording(this, mediaStoreOutput)
// 关联事件监听器
.withEventListener(ContextCompat.getMainExecutor(this), videoRecordEventListener)
// 启用音频 (前提是此应用已获得音频权限)
.withAudioEnabled()
// 开始录制
.start()
△ 视频拍摄示例
YUV 至 RGB 的转换
另一个呼声很高的功能是 YUV 到 RGB 的转换,我们来了解一下此功能。
△ YUV 格式 (图左) 转换至 RGB 格式 (图右)
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(1280, 720))
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
在示例代码中,我们创建了 ImageAnalysis 实例,为图像缓冲区指定了所需的分辨率和背压策略,并调用新的 setOutputImageFormat 方法以请求 RGBA 8888 格式的输出。现在,ImageAnalysis 输出的帧为 RGBA 8888 数据而不再是 YUV 格式。
修复单像素漂移
CameraX Extensions API
我们来看看如何使用 CameraX Extensions API:
// 获取后置相机列表
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// 检查所有的后置相机中是否有支持焦外虚化
if (extensionsManager.isExtensionAvailable(
cameraProvider,
cameraSelector,
ExtensionMode.BOKEH
)) {
// 创建扩展 cameraSelector,我们提供了相机并指定焦外虚化模式
// 它将开始在后台搜索支持焦外虚化的后置相机
val bokehCameraSelector = extensionsManager.getExtensionCameraSelector(
cameraProvider,
cameraSelector,
ExtensionMode.BOKEH
)
// 创建 imageCapture 和 preview
val imageCapture = ImageCapture.Builder().builder()
val preview = Preview.Builder().build()
// 使用 bokehCameraSelector 将它们绑定到生命周期
cameraProvider.bindToLifecycle(lifecycleOwner,
bokehCameraSelector,
imageCapture,
preview
)
}
在上面的例子中,imageCapture 输出的图像将会具有焦外虚化效果,如果设备支持的话,preview 也将预览焦外虚化效果。
如需详细了解,请参阅我们之前的推文《使用 CameraX Extensions API 将特效应用到照片上》。
曝光补偿
// 创建变量来跟踪 exposureIndex 值
var exposureIndex = 0
// 使用 cameraSelector 将 imageCapture 和 preview 绑定到生命周期
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
getCameraSelector(),
preview,
imageCapture
)
// 为视图中的按钮添加点击事件
evButton.setOnclickListener {
// 检查有效的范围以防止可能的异常
val range = camera.cameraInfo.exposureState.exposureCompensationRange
if (range.contains(exposureIndex + 1)) {
// 调用 camera.cameraControl 的 setExposureCompenstation() 方法来设置曝光补偿
camera.cameraControl.setExposureCompenstation(++exposureIndex)
// 使用 exposureCompensationStep 来实现从 index 到 EV 到转换
val ev = camera.cameraInfo.exposureState.exposureCompensationStep.toFloat() * exposureIndex
Log.i("CameraXLog", "EV: $ev")
}
}
其中 exposureIndex 是一个与设备无关的数字,它将以硬件允许的最小步长递增或递减曝光值,因此可以在不同的设备上以类似的方式运作。如果您想向用户展示 EV 值,可以获取 exposureCompensationStep 来实现转换。
如需了解 CameraX 中曝光补偿 API 的应用背景和调用方法,请参阅我们之前的推文《CameraX 曝光补偿 API 入门指南》。
平滑缩放
CameraX 1.1 的更多功能
接下来介绍我们在 1.1 中添加的更多功能。
CameraState API 现在可以提供诸如另一个应用正在使用相机或者正处于勿扰模式等更多有关相机状态的信息,使得应用能够围绕不同的相机时间来设计更好的界面和用户体验流程。Image Analysis 现在可以提供超过 1080p 的图像。Logging API 可以更详细的调试日志并改善了错误报告。Coordinate Transformation API 可以将不同用例间的坐标关联起来,如果您在 imageAnalysis 缓冲区中定位了兴趣点,便可以方便地在图像捕捉的输出或预览中轻松找到它。您可以使用 CameraFilter API 来指定详细的规则以选择合适的相机。如果应用只需要前置或者后置相机,可以使用 AvailableCamerasLimiter 来加快启动时间。CameraControllerInfo 中可提供相机功能的更多详细信息。
设备兼容性
您可以在版本记录中看到每个版本中的详细变更,还可以在问题跟踪器中看已经修复的问题。
更多信息
CameraX 概览
https://developer.android.google.cn/training/camerax
CameraX 使用入门 Codelab
https://developer.android.google.cn/codelabs/camerax-getting-started
CameraX Github 样例代码
https://github.com/android/camera-samples
Android CameraX 讨论组
https://groups.google.com/a/android.com/g/camerax-developers
希望对 CameraX 1.1 版本的简要介绍对大家有所帮助,非常期待看到大家使用 CameraX 构建的功能!
推荐阅读