如何用Flutter的camera包实现双摄像头同时录制视频?
前后摄像头同时录制视频的Flutter实现方案
核心思路
官方camera包本身没有提供一键双摄录制的API,但可以通过初始化两个独立的CameraController实例,分别绑定前置和后置摄像头,同时启动/停止录制来实现需求。
实现步骤
- 获取摄像头列表:调用
availableCameras()获取设备上的所有摄像头,筛选出前置(CameraLensDirection.front)和后置(CameraLensDirection.back)摄像头。
- 获取摄像头列表:调用
- 初始化双控制器:分别为前后摄像头创建
CameraController,配置分辨率、音频录制等参数。
- 初始化双控制器:分别为前后摄像头创建
- 权限处理:确保已申请相机权限和存储权限(Android需在
AndroidManifest.xml,iOS需在Info.plist中配置)。
- 权限处理:确保已申请相机权限和存储权限(Android需在
- 同步录制控制:通过
Future.wait()同时启动/停止两个控制器的录制操作,保证时序一致。
- 同步录制控制:通过
关键注意事项
- 性能优化:双摄录制会占用更多CPU和内存,建议选择中等分辨率(
ResolutionPreset.medium),避免在低端设备上出现卡顿或崩溃。 - 文件管理:为前后视频文件设置不同的命名规则(如添加时间戳),防止文件覆盖。
- 生命周期管理:页面销毁时必须调用两个控制器的
dispose()方法,释放资源避免内存泄漏。 - 异常处理:录制过程中可能出现权限不足、设备不支持等问题,需添加try-catch捕获异常。
示例代码
import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; late List<CameraDescription> _cameras; late CameraController _frontController; late CameraController _backController; bool _isRecording = false; String? _frontVideoPath; String? _backVideoPath; class DualCameraRecorder extends StatefulWidget { const DualCameraRecorder({super.key}); @override State<DualCameraRecorder> createState() => _DualCameraRecorderState(); } class _DualCameraRecorderState extends State<DualCameraRecorder> { @override void initState() { super.initState(); _initializeCameras(); } Future<void> _initializeCameras() async { _cameras = await availableCameras(); // 绑定前置摄像头 _frontController = CameraController( _cameras.firstWhere((cam) => cam.lensDirection == CameraLensDirection.front), ResolutionPreset.medium, enableAudio: true, ); // 绑定后置摄像头 _backController = CameraController( _cameras.firstWhere((cam) => cam.lensDirection == CameraLensDirection.back), ResolutionPreset.medium, enableAudio: true, ); // 并行初始化两个控制器 await Future.wait([ _frontController.initialize(), _backController.initialize(), ]); if (!mounted) return; setState(() {}); } Future<void> _startRecording() async { if (_isRecording) return; // 获取存储路径 final appDir = await getExternalStorageDirectory(); final timestamp = DateTime.now().millisecondsSinceEpoch; _frontVideoPath = '${appDir?.path}/front_recording_$timestamp.mp4'; _backVideoPath = '${appDir?.path}/back_recording_$timestamp.mp4'; try { // 同步启动录制 await Future.wait([ _frontController.startVideoRecording(_frontVideoPath!), _backController.startVideoRecording(_backVideoPath!), ]); setState(() => _isRecording = true); } catch (e) { debugPrint('启动录制失败: $e'); } } Future<void> _stopRecording() async { if (!_isRecording) return; try { // 同步停止录制 await Future.wait([ _frontController.stopVideoRecording(), _backController.stopVideoRecording(), ]); setState(() => _isRecording = false); // 录制完成后的逻辑,比如保存到相册 debugPrint('前置视频路径: $_frontVideoPath'); debugPrint('后置视频路径: $_backVideoPath'); } catch (e) { debugPrint('停止录制失败: $e'); } } @override void dispose() { // 释放控制器资源 _frontController.dispose(); _backController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { // 等待控制器初始化完成 if (!_frontController.value.isInitialized || !_backController.value.isInitialized) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } return Scaffold( appBar: AppBar(title: const Text('双摄像头录制')), body: Column( children: [ // 双预览窗口 Expanded( child: Row( children: [ Expanded(child: CameraPreview(_frontController)), Expanded(child: CameraPreview(_backController)), ], ), ), // 录制控制按钮 Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: ElevatedButton( onPressed: _isRecording ? _stopRecording : _startRecording, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), textStyle: const TextStyle(fontSize: 18), ), child: Text(_isRecording ? '停止录制' : '开始录制'), ), ), ], ), ); } } void main() async { WidgetsFlutterBinding.ensureInitialized(); _cameras = await availableCameras(); runApp(const MaterialApp(home: DualCameraRecorder())); }
内容的提问来源于stack exchange,提问作者Nikunj Agrawal
相关产品推荐
相关产品推荐

