捕获系统通常会录制视频和音频流,压缩它们,将两个流进行复用,然后将生成的流写入磁盘。
在 CameraX 中,视频捕获的解决方案是 VideoCapture
用例
如图 2 所示,CameraX 视频捕获包含一些高级架构组件
SurfaceProvider
用于视频源。AudioSource
用于音频源。- 两个编码器用于对视频/音频进行编码和压缩。
- 一个媒体混合器用于混合两个流。
- 一个文件保存器用于写入结果。
VideoCapture API 抽象了复杂的捕获引擎,并为应用程序提供了更简单、更直接的 API。
VideoCapture API 概述
VideoCapture
是一个 CameraX 用例,可以单独使用,也可以与其他用例结合使用。支持的具体组合取决于相机硬件功能,但在所有设备上,Preview
和 VideoCapture
都是有效的用例组合。
VideoCapture API 包含以下与应用程序通信的对象
VideoCapture
是顶级用例类。VideoCapture
使用CameraSelector
和其他 CameraX UseCases 绑定到LifecycleOwner
。有关这些概念和用法的更多信息,请参阅 CameraX 架构。- 一个
Recorder
是 VideoOutput 的实现,与VideoCapture
紧密耦合。Recorder
用于执行视频和音频捕获。应用程序创建来自Recorder
的录制。 - 一个
PendingRecording
配置录制,提供启用音频和设置事件监听器等选项。您必须使用Recorder
创建PendingRecording
。PendingRecording
不会录制任何内容。 - 一个
Recording
执行实际录制。您必须使用PendingRecording
创建Recording
。
图 3 显示了这些对象之间的关系
图例
- 使用
QualitySelector
创建Recorder
。 - 使用其中一个
OutputOptions
配置Recorder
。 - 如果需要,使用
withAudioEnabled()
启用音频。 - 使用
VideoRecordEvent
监听器调用start()
以开始录制。 - 使用
pause()
/resume()
/stop()
在Recording
上控制录制。 - 在您的事件监听器中响应
VideoRecordEvents
。
详细的 API 列表在 源代码中的 current.txt 中。
使用 VideoCapture API
要将 CameraX VideoCapture
用例集成到您的应用中,请执行以下操作
- 绑定
VideoCapture
。 - 准备和配置录制。
- 启动和控制运行时录制。
以下部分概述了您可以在每个步骤中执行的操作以获得端到端录制会话。
绑定 VideoCapture
要绑定 VideoCapure
用例,请执行以下操作
- 创建一个
Recorder
对象。 - 创建
VideoCapture
对象。 - 绑定到
Lifecycle
。
CameraX VideoCapture API 遵循构建器设计模式。应用程序使用 Recorder.Builder
创建 Recorder
。您还可以通过 QualitySelector
对象配置 Recorder
的视频分辨率。
CameraX Recorder
支持以下预定义的 Qualities
用于视频分辨率
Quality.UHD
用于 4K 超高清视频尺寸 (2160p)Quality.FHD
用于全高清视频尺寸 (1080p)Quality.HD
用于高清视频尺寸 (720p)Quality.SD
用于 SD 视频尺寸 (480p)
请注意,CameraX 也可以在应用程序授权时选择其他分辨率。
每个选择的精确视频尺寸取决于相机和编码器的功能。有关更多信息,请参阅 CamcorderProfile
的文档。
应用程序可以通过创建一个 QualitySelector
来配置分辨率。您可以使用以下方法之一创建 QualitySelector
通过使用
fromOrderedList()
提供一些首选分辨率,并包含一个回退策略以防不支持任何首选分辨率。CameraX 可以根据所选摄像头的功能确定最佳回退匹配,有关更多详细信息,请参阅
QualitySelector
的FallbackStrategy 规范
。例如,以下代码请求录制时支持的最高分辨率,如果不支持任何请求的分辨率,则授权 CameraX 选择最接近 Quality.SD 分辨率的分辨率val qualitySelector = QualitySelector.fromOrderedList( listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD), FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
首先查询相机功能,然后使用
QualitySelector::from()
从支持的分辨率中选择。val cameraInfo = cameraProvider.availableCameraInfos.filter { Camera2CameraInfo .from(it) .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK } val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0]) val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD) .filter { supportedQualities.contains(it) } // Use a simple ListView with the id of simple_quality_list_view viewBinding.simpleQualityListView.apply { adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, filteredQualities.map { it.qualityToString() }) // Set up the user interaction to manually show or hide the system UI. setOnItemClickListener { _, _, position, _ -> // Inside View.OnClickListener, // convert Quality.* constant to QualitySelector val qualitySelector = QualitySelector.from(filteredQualities[position]) // Create a new Recorder/VideoCapture for the new quality // and bind to lifecycle val recorder = Recorder.Builder() .setQualitySelector(qualitySelector).build() // ... } } // A helper function to translate Quality to a string fun Quality.qualityToString() : String { return when (this) { Quality.UHD -> "UHD" Quality.FHD -> "FHD" Quality.HD -> "HD" Quality.SD -> "SD" else -> throw IllegalArgumentException() } }
请注意,从
QualitySelector.getSupportedQualities()
返回的功能保证适用于VideoCapture
用例或VideoCapture
和Preview
用例的组合。当与ImageCapture
或ImageAnalysis
用例一起绑定时,当请求的相机上不支持所需的组合时,CameraX 仍可能绑定失败。
获得 QualitySelector
后,应用程序可以创建一个 VideoCapture
对象并执行绑定。请注意,此绑定与其他用例相同
val recorder = Recorder.Builder()
.setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
.build()
val videoCapture = VideoCapture.withOutput(recorder)
try {
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
请注意,bindToLifecycle()
返回一个 Camera
对象。有关控制相机输出(如缩放和曝光)的更多信息,请参阅 本指南。
Recorder
选择最适合系统的格式。最常见的视频编解码器是 H.264 AVC),容器格式为 MPEG-4。
配置和创建录制
从 Recorder
中,应用程序可以创建录制对象以执行视频和音频捕获。应用程序通过执行以下操作创建录制
- 使用
prepareRecording()
配置OutputOptions
。 - (可选) 启用音频录制。
- 使用
start()
注册VideoRecordEvent
监听器,并开始视频捕获。
当您调用 start()
函数时,Recorder
会返回一个 Recording
对象。您的应用程序可以使用此 Recording
对象完成捕获或执行其他操作,例如暂停或恢复。
Recorder
一次支持一个 Recording
对象。在先前 Recording
对象上调用 Recording.stop()
或 Recording.close()
后,您可以开始新的录制。
让我们更详细地了解这些步骤。首先,应用程序使用 Recorder.prepareRecording()
为 Recorder 配置 OutputOptions
。 Recorder
支持以下类型的 OutputOptions
FileDescriptorOutputOptions
用于捕获到FileDescriptor
中。FileOutputOptions
用于捕获到File
中。MediaStoreOutputOptions
用于捕获到MediaStore
中。
所有 OutputOptions
类型都允许您使用 setFileSizeLimit()
设置最大文件大小。其他选项特定于各个输出类型,例如 FileDescriptorOutputOptions
的 ParcelFileDescriptor
。
prepareRecording()
返回一个 PendingRecording
对象,这是一个中间对象,用于创建相应的 Recording
对象。 PendingRecording
是一个瞬态类,在大多数情况下应该是不可见的,并且很少被应用程序缓存。
应用程序可以进一步配置录制,例如
- 使用
withAudioEnabled()
启用音频。 - 使用
start(Executor, Consumer<VideoRecordEvent>)
注册监听器以接收视频录制事件。 - 允许录制在与其附加的 VideoCapture 重新绑定到另一个相机时持续录制,使用
PendingRecording.asPersistentRecording()
。
要开始录制,请调用 PendingRecording.start()
。CameraX 将 PendingRecording
转换为 Recording
,将录制请求排队,并将新创建的 Recording
对象返回给应用程序。一旦在相应的 Camera 设备上开始录制,CameraX 就会发送一个 VideoRecordEvent.EVENT_TYPE_START
事件。
以下示例显示了如何将视频和音频录制到 MediaStore
文件中
// Create MediaStoreOutputOptions for our recorder
val name = "CameraX-recording-" +
SimpleDateFormat(FILENAME_FORMAT, Locale.US)
.format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
.setContentValues(contentValues)
.build()
// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
.prepareRecording(context, mediaStoreOutput)
.withAudioEnabled()
.start(ContextCompat.getMainExecutor(this), captureListener)
虽然相机预览默认在前置摄像头上镜像,但默认情况下,VideoCapture 录制的视频不会镜像。使用 CameraX 1.3,现在可以镜像视频录制,以便前置摄像头预览和录制的视频匹配。
有三个 MirrorMode 选项:MIRROR_MODE_OFF、MIRROR_MODE_ON 和 MIRROR_MODE_ON_FRONT_ONLY。为了与相机预览对齐,Google 建议使用 MIROR_MODE_ON_FRONT_ONLY,这意味着后置摄像头的镜像功能未启用,但前置摄像头的镜像功能已启用。有关 MirrorMode 的更多信息,请参阅 MirrorMode 常量
。
此代码片段显示了如何使用 MIRROR_MODE_ON_FRONT_ONLY
调用 VideoCapture.Builder.setMirrorMode()
。有关更多信息,请参阅 setMirrorMode()
。
Kotlin
val recorder = Recorder.Builder().build() val videoCapture = VideoCapture.Builder(recorder) .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY) .build() useCases.add(videoCapture);
Java
Recorder.Builder builder = new Recorder.Builder(); if (mVideoQuality != QUALITY_AUTO) { builder.setQualitySelector( QualitySelector.from(mVideoQuality)); } VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build()) .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY) .build(); useCases.add(videoCapture);
控制活动录制
您可以使用以下方法暂停、恢复和停止正在进行的 Recording
请注意,无论录制处于暂停还是活动状态,您都可以调用 stop()
来终止 Recording
。
如果您已使用 PendingRecording.start()
注册了 EventListener
,则 Recording
将通过使用 VideoRecordEvent
进行通信。
VideoRecordEvent.EVENT_TYPE_STATUS
用于录制统计信息,例如当前文件大小和录制时间跨度。VideoRecordEvent.EVENT_TYPE_FINALIZE
用于录制结果,并包含最终文件 URI 以及任何相关错误等信息。
一旦您的应用收到指示录制会话成功的 EVENT_TYPE_FINALIZE
,您就可以从 OutputOptions
中指定的位置访问捕获的视频。
其他资源
要了解有关 CameraX 的更多信息,请参阅以下其他资源