Flutter录制时延迟播放视频,VideoPlayer无法初始化求助
解决录制过程中VideoPlayer无法初始化的问题
你的核心问题在于录制中的视频文件处于被相机进程占用的状态,此时VideoPlayer尝试读取不完整的锁定文件,会导致初始化卡住,既不触发成功回调也不抛出明确错误。结合你的代码,给出以下具体修复方案:
关键问题分析
- 文件锁定与不完整:调用
startVideoRecording后,相机一直在向文件写入数据,此时文件处于占用状态且内容不完整,VideoPlayer无法解析这类文件。 - 错误处理覆盖不全:原代码用
.then().catchError()的链式调用,若初始化过程中出现未被捕获的异步错误,无法打印日志;改用async/await+try-catch能更全面捕获异常。 - 播放时机错误:延迟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
相关产品推荐
相关产品推荐

