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

Flutter录制时延迟播放视频,VideoPlayer无法初始化求助

解决录制过程中VideoPlayer无法初始化的问题

你的核心问题在于录制中的视频文件处于被相机进程占用的状态,此时VideoPlayer尝试读取不完整的锁定文件,会导致初始化卡住,既不触发成功回调也不抛出明确错误。结合你的代码,给出以下具体修复方案:

关键问题分析

  1. 文件锁定与不完整:调用startVideoRecording后,相机一直在向文件写入数据,此时文件处于占用状态且内容不完整,VideoPlayer无法解析这类文件。
  2. 错误处理覆盖不全:原代码用.then().catchError()的链式调用,若初始化过程中出现未被捕获的异步错误,无法打印日志;改用async/await+try-catch能更全面捕获异常。
  3. 播放时机错误:延迟5秒后直接播放,但此时录制可能仍在进行,文件并未完成写入。

修复方案

方案1:录制停止后再触发播放(推荐)

将播放逻辑移到_stopRecording中,确保文件写入完成后再初始化VideoPlayer:

修改_startRecording,移除其中的_playDelayedVideo()调用:

Future<void> _startRecording() async {
  if (_isRecording) return;

  _currentRecordingPath = await _getTemporaryFilePath();
  await _cameraController.startVideoRecording();
  setState(() {
    _isRecording = true;
  });
  // 移除这里的_playDelayedVideo()
}

修改_stopRecording,添加延迟播放逻辑:

Future<void> _stopRecording() async {
  if (!_isRecording) return;

  await _cameraController.stopVideoRecording();
  setState(() {
    _isRecording = false;
  });
  // 录制停止后启动延迟播放
  _playDelayedVideo();
}

方案2:优化_playDelayedVideo的错误处理与等待逻辑

如果需要在录制过程中尝试播放(不推荐,文件可能不完整),至少要确保错误能被捕获,并且等待录制停止:

Future<void> _playDelayedVideo() async {
  if (_currentRecordingPath == null) {
    print('No recording path set');
    return;
  }

  print('Current recording path: $_currentRecordingPath');
  final file = File(_currentRecordingPath!);
  if (!await file.exists()) {
    print('File does not exist');
    return;
  }

  print('Waiting for delay and recording stop');
  // 等待延迟+录制停止
  await Future.wait([
    Future.delayed(Duration(seconds: 5)),
    // 等待录制状态变为false
    Future.doWhile(() => Future.delayed(Duration(milliseconds: 100)).then((_) => _isRecording))
  ]);
  print('Delay and recording completed');

  try {
    _videoPlayerController = VideoPlayerController.file(file);
    await _videoPlayerController!.initialize();
    print('VideoPlayer initialized');
    setState(() {
      _videoPlayerController!.play();
      _videoPlayerController!.setLooping(true);
    });
  } catch (error, stackTrace) {
    print('Error initializing VideoPlayer: $error');
    print('Stack trace: $stackTrace');
  }
}

完整修改后的代码

import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';

class CameraScreen extends StatefulWidget {
  final CameraDescription camera;

  CameraScreen({required this.camera});

  @override
  _CameraScreenState createState() => _CameraScreenState();
}

class _CameraScreenState extends State<CameraScreen> {
  late CameraController _cameraController;
  late Future<void> _initializeControllerFuture;
  VideoPlayerController? _videoPlayerController; // 去掉late,因为初始化为null
  String? _currentRecordingPath;
  Duration delay = Duration(seconds: 5);
  bool _isRecording = false;

  @override
  void initState() {
    super.initState();
    _cameraController = CameraController(
      widget.camera,
      ResolutionPreset.high,
    );
    _initializeControllerFuture = _cameraController.initialize();
    _videoPlayerController = null;
  }

  @override
  void dispose() {
    _cameraController.dispose();
    _videoPlayerController?.dispose();
    super.dispose();
  }

  Future<String> _getTemporaryFilePath() async {
    final directory = await getTemporaryDirectory();
    return '${directory.path}/${DateTime.now().millisecondsSinceEpoch}.mp4';
  }

  Future<void> _startRecording() async {
    if (_isRecording) return;

    _currentRecordingPath = await _getTemporaryFilePath();
    await _cameraController.startVideoRecording();
    setState(() {
      _isRecording = true;
    });
  }

  Future<void> _stopRecording() async {
    if (!_isRecording) return;

    await _cameraController.stopVideoRecording();
    setState(() {
      _isRecording = false;
    });
    // 录制停止后启动延迟播放
    _playDelayedVideo();
  }

  Future<void> _playDelayedVideo() async {
    if (_currentRecordingPath == null) {
      print('No recording path set');
      return;
    }

    print('Current recording path: $_currentRecordingPath');
    final file = File(_currentRecordingPath!);
    if (!await file.exists()) {
      print('File does not exist');
      return;
    }

    print('Waiting for delay');
    await Future.delayed(Duration(seconds: 5));
    print('Delay completed');

    try {
      // 先释放旧的控制器
      await _videoPlayerController?.dispose();
      _videoPlayerController = VideoPlayerController.file(file);
      await _videoPlayerController!.initialize();
      print('VideoPlayer initialized');
      setState(() {
        _videoPlayerController!.play();
        _videoPlayerController!.setLooping(true);
      });
    } catch (error, stackTrace) {
      print('Error initializing VideoPlayer: $error');
      print('Stack trace: $stackTrace');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Camera Screen')),
      body: Stack(
        children: [
          // 延迟视频播放(居中)
          if (_videoPlayerController != null &&
              _videoPlayerController!.value.isInitialized)
            Center(
              child: AspectRatio(
                aspectRatio: _videoPlayerController!.value.aspectRatio,
                child: VideoPlayer(_videoPlayerController!),
              ),
            ),

          // 实时相机预览(左上角)
          Positioned(
            top: 10,
            left: 10,
            width: 150,
            height: 150,
            child: FutureBuilder<void>(
              future: _initializeControllerFuture,
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  return ClipRRect(
                    borderRadius: BorderRadius.circular(10),
                    child: CameraPreview(_cameraController),
                  );
                } else {
                  return Container(
                    width: 150,
                    height: 150,
                    color: Colors.black,
                    child: Center(child: CircularProgressIndicator()),
                  );
                }
              },
            ),
          ),

          // 底部控制按钮
          Positioned(
            bottom: 20,
            left: 0,
            right: 0,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  icon: Icon(Icons.fiber_manual_record, color: Colors.red),
                  onPressed: _startRecording,
                ),
                IconButton(
                  icon: Icon(Icons.stop, color: Colors.black),
                  onPressed: _stopRecording,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

额外注意事项

  • 每次初始化新的VideoPlayerController前,记得释放旧的控制器,避免内存泄漏。
  • 若需要在录制过程中预览,考虑使用相机的实时预览而非录制文件,录制文件必须等待写入完成才能被正确解析。

内容的提问来源于stack exchange,提问作者andy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 14:05:54