捕获系统通常会录制视频流和音频流,对其进行压缩,对这两个流进行多路复用,然后将生成的流写入磁盘。
![视频和音频捕获系统概念图](https://developer.android.google.cn/static/images/training/camera/camerax/conceptual-capturing-system.png?authuser=0000&hl=hr)
在 CameraX 中,用于视频捕获的解决方案是 VideoCapture
用例:
![展示 CameraX 如何处理 VideoCapture 用例的概念图](https://developer.android.google.cn/static/images/training/camera/camerax/videocapture-use-case.png?authuser=0000&hl=hr)
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 展示了这些对象之间的关系:
![展示 VideoCapture 用例中发生的交互的示意图](https://developer.android.google.cn/static/images/training/camera/camerax/videocapture-roles-interaction.png?authuser=0000&hl=hr)
图例:
- 使用
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)); } VideoCaptureR<ecorder >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,请参阅下面列出的其他资源: