捕获系统通常会录制视频流和音频流,对其进行压缩,对这两个流进行多路复用,然后将生成的流写入磁盘。
在 CameraX 中,用于视频捕获的解决方案是 VideoCapture 用例:
VideoCapture 用例的概念图。如图 2 所示,CameraX 视频捕获包括几个高级架构组件:
SurfaceProvider,表示视频来源。AudioSource,表示音频来源。- 用于对视频/音频进行编码和压缩的两个编码器。
- 用于对两个流进行多路复用的媒体复用器。
- 用于写出结果的文件保存器。
VideoCapture API 会对复杂的捕获引擎进行抽象化处理,为应用提供更加简单且直观的 API。
VideoCapture API 概述
VideoCapture 是一种 CameraX 用例,既可以单独使用,也可以与其他用例搭配使用。受支持的具体组合取决于相机硬件功能,不过 Preview 和 VideoCapture 这一用例组合适用于所有设备。
VideoCapture API 包含可与应用通信的以下对象:
VideoCapture是顶级用例类。VideoCapture通过CameraSelector和其他 CameraX 用例绑定到LifecycleOwner。如需详细了解这些概念和用法,请参阅 CameraX 架构。Recorder是与VideoCapture紧密耦合的 VideoOutput 实现。Recorder用于执行视频和音频捕获操作。应用通过Recorder创建录制对象。PendingRecording会配置录制对象,同时提供启用音频和设置事件监听器等选项。您必须使用Recorder来创建PendingRecording。PendingRecording不会录制任何内容。Recording会执行实际录制操作。您必须使用PendingRecording来创建Recording。
图 3 展示了这些对象之间的关系:
图例:
- 使用
QualitySelector创建Recorder。 - 使用其中一个
OutputOptions配置Recorder。 - 如果需要,使用
withAudioEnabled()启用音频。 - 使用
VideoRecordEvent监听器调用start()以开始录制。 - 针对
Recording使用pause()/resume()/stop()来控制录制操作。 - 在事件监听器内响应
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,适用于标清视频大小 (480p)
请注意,获得应用授权后,CameraX 还可以选择其他分辨率。
每个选项对应的确切视频大小取决于相机和编码器的功能。如需了解详情,请参阅 CamcorderProfile 的文档。
应用可以通过创建 QualitySelector 来配置分辨率。您可以使用以下方法之一创建 QualitySelector:
使用
fromOrderedList()提供几个首选分辨率,并包含一个后备策略,以备在不支持任何首选分辨率时使用。CameraX 可以根据所选相机的功能确定最佳后备匹配项。如需了解详情,请参阅
QualitySelector的FallbackStrategy specification。例如,以下代码会请求支持的最高录制分辨率;如果所有请求分辨率都不受支持,则授权 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() 来设置文件大小上限。其他选项特定于单个输出类型,例如 ParcelFileDescriptor 特定于 FileDescriptorOutputOptions。
prepareRecording() 会返回 PendingRecording 对象,该对象是一个中间对象,用于创建相应的 Recording 对象。PendingRecording 是一个瞬态类,在大多数情况下应不可见,并且很少被应用缓存。
应用可以进一步配置录制对象,例如:
- 使用
withAudioEnabled()启用音频。 - 使用
start(Executor, Consumer<VideoRecordEvent>)注册监听器,以接收视频录制事件。 - 允许在连接 VideoCapture 时连续录制
映射到另一个摄像头,
PendingRecording.asPersistentRecording()。
要开始录制,请调用 PendingRecording.start()。CameraX 会将 PendingRecording 转换为 Recording,将录制请求加入队列,并将新创建的 Recording 对象返回给应用。一旦在相应相机设备上开始录制,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 constants。
以下代码段展示了如何调用
VideoCapture.Builder.setMirrorMode()(使用 MIRROR_MODE_ON_FRONT_ONLY)。对于
如需了解详情,请参阅 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,请参阅下面列出的其他资源: