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 保存。
VideoCapture 录制流程架构 VideoCapture 录制架构与文件管理流程 VideoCapture.Builder 配置 绑定 CameraProvider + Lifecycle startRecording() Recording File / 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 输出文件管理

每次录制成功后应在回调中记录文件路径,定期清理过期视频以节省存储空间。建议采用以下策略:

  1. 在 onVideoSaved 中将 URI 存入数据库或 SharedPreferences(用于后续预览或分享)。
  2. 使用 File.delete() 或 ContentResolver 删除超过 7 天或数量超过 50 个的视频。
  3. 在 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 —— 原因:写入失败,通常由磁盘空间不足或路径权限错误引起。解决方案:提前检查可用空间并捕获异常。

通过本章的实践,你已经能独立实现一个带有完整文件管理策略的视频录制模块。下一阶段将围绕音频录制与视频的同步处理展开。