You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Flutter的camera包实现双摄像头同时录制视频?

前后摄像头同时录制视频的Flutter实现方案

核心思路

官方camera包本身没有提供一键双摄录制的API,但可以通过初始化两个独立的CameraController实例,分别绑定前置和后置摄像头,同时启动/停止录制来实现需求。

实现步骤

    1. 获取摄像头列表:调用availableCameras()获取设备上的所有摄像头,筛选出前置(CameraLensDirection.front)和后置(CameraLensDirection.back)摄像头。
    1. 初始化双控制器:分别为前后摄像头创建CameraController,配置分辨率、音频录制等参数。
    1. 权限处理:确保已申请相机权限和存储权限(Android需在AndroidManifest.xml,iOS需在Info.plist中配置)。
    1. 同步录制控制:通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 22:53:12