4. 录制视频功能实现:配置 VideoCapture 用例、录制开始与停止、输出文件管理
本章聚焦于 CameraX 中 VideoCapture 用例的完整接入流程。你将掌握如何配置视频拍摄参数、控制录制启停以及管理输出文件的生命周期。内容涵盖核心 API、实现步骤、调试技巧和常见陷阱,帮助你在实战中快速实现稳定可靠的高清录制功能。
4.1 原理与架构
VideoCapture 是 CameraX 负责视频录制的用例,它与 Preview 用例共享同一实时数据流。录制时,CameraX 内部通过 MediaCodec 将相机帧编码为 H.264/H.265 等格式,并写入 MediaMuxer 输出到 MP4 容器。架构上,开发者只需通过 Recording 对象控制录制状态,无需直接操作编码器或复用器。
关键组件
VideoCapture:绑定到 CameraX 生命周期的视频捕获用例。VideoCapture.Builder:配置视频分辨率、帧率、码率等参数。Recording:由VideoCapture的startRecording()创建,负责录制控制。File / ContentValues:指定输出文件路径或通过 MediaStore 保存。
4.2 配置 VideoCapture 用例
使用 VideoCapture.Builder 设置视频参数,建议先从默认配置开始,再根据设备能力调整。
val videoCapture = VideoCapture.Builder()
.setBitRate(10_000_000) // 10 Mbps
.setFrameRate(30) // 30fps
.setTargetResolution(Size(1920, 1080))
.setVideoQuality(Quality.SD) // 低分辨率备用
.build()
| 参数 | 说明 | 推荐值 |
|---|---|---|
setBitRate | 视频码率 (bps) | 8~16 Mbps (1080p30) |
setFrameRate | 目标帧率 | 30 或 60 |
setTargetResolution | 最终分辨率 (可能被降级) | 1920x1080 |
setVideoQuality | 备用质量等级 | Quality.HD 或 Quality.SD |
调试建议: 通过 CameraProvider.availableCameraInfos 获取支持的分辨率列表,打印到 Log 以便对比。
4.3 录制开始与停止
录制由 VideoCapture.startRecording() 触发,生成 Recording 对象。停止时调用 Recording.stop()。核心代码片段如下:
// 假设 videoCapture 已通过 ProcessCameraProvider 绑定成功
val recording = videoCapture.startRecording(
outputFileOptions, // 输出配置
ContextCompat.getMainExecutor(context),
object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(outputFileUri: Uri) {
Log.d("CameraVideo", "视频保存成功: $outputFileUri")
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
Log.e("CameraVideo", "录制错误 [$videoCaptureError]: $message")
}
}
)
// 停止录制(例如在按钮点击或生命周期暂停时)
recording.stop()
outputFileOptions 可以是以下两种形式:
- File 方式:指定应用私有目录下的文件
- MediaStore 方式:自动添加到系统图库(适合公开共享)
// File 方式
val file = File(context.filesDir, "videos/video_${System.currentTimeMillis()}.mp4")
file.parentFile?.mkdirs()
val outputFileOptions = VideoCapture.OutputFileOptions.Builder(file).build()
// MediaStore 方式
val contentValues = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, "video_${System.currentTimeMillis()}.mp4")
put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
}
val outputFileOptions = VideoCapture.OutputFileOptions.Builder(
context.contentResolver,
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
4.4 输出文件管理
每次录制成功后应在回调中记录文件路径,定期清理过期视频以节省存储空间。建议采用以下策略:
- 在
onVideoSaved中将 URI 存入数据库或 SharedPreferences(用于后续预览或分享)。 - 使用
File.delete()或 ContentResolver 删除超过 7 天或数量超过 50 个的视频。 - 在
Application启动时触发一次清理任务,避免碎片堆积。
常见错误:未及时释放 Recording 对象会导致 MediaMuxer 无法关闭,造成文件损坏。务必在 Activity/Fragment 的 onPause 中调用 recording?.stop(),并置为 null。
4.5 调试与性能优化
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 录制后文件为 0KB | 未正确设置写入权限或路径错误 | 检查运行时权限(Android 13+ 的 READ_MEDIA_VIDEO)/ 确认目录可写 |
| 帧率严重下降 | 码率过高或分辨率超过设备上限 | 降低 bitRate 或 targetResolution,通过 CameraInfo 查询能力上限 |
| 录制时画面卡顿 | CPU/GPU 过载,或未使用高性能编码器 | 尝试启用 VideoCapture.Builder.setEncoderProfiler() 选择硬件编码器 |
| 录制完成时 ANR | 回调直接在主线程执行了繁重 I/O | 确保回调中仅做轻量操作,文件写入由 CameraX 内部异步完成 |
4.6 部署注意事项
- AndroidManifest.xml 必须声明
CAMERA权限和RECORD_AUDIO权限(如需音频录制)。 - Android 11+ 推荐使用
MANAGE_EXTERNAL_STORAGE或 MediaStore 方式避免分区存储问题。 - 在
build.gradle(app) 中确保 CameraX 版本一致(建议 1.3+):
implementation "androidx.camera:camera-core:1.3.1"
implementation "androidx.camera:camera-camera2:1.3.1"
implementation "androidx.camera:camera-lifecycle:1.3.1"
implementation "androidx.camera:camera-video:1.3.1"
4.7 常见错误集中排查
错误码 3: VideoCapture.ERROR_SOURCE_IS_ACTIVE —— 原因:同一时刻只能有一个活跃录制。解决方案:调用 recording.stop() 后再启动新录制。
错误码 5: VideoCapture.ERROR_FILE_IO —— 原因:写入失败,通常由磁盘空间不足或路径权限错误引起。解决方案:提前检查可用空间并捕获异常。
通过本章的实践,你已经能独立实现一个带有完整文件管理策略的视频录制模块。下一阶段将围绕音频录制与视频的同步处理展开。