Closed scottynoshotty closed 7 months ago
please paste the log and more crash stacks because I have no ios device to test today
Hey I haven't been able to repro this yet, just seeing it in Crashlytics.
Hi Wang,
Just wanted to add more context, this has been a recurring crash in my Crashlytics since I brought in FVP. Only happens on iOS devices (I use Flutter default video player for Android). If you need any sample code let me know, I haven't repro'd locally.
Here is the code I am using to play the video
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:social_app/app/app.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:video_player/video_player.dart';
class PostVideo extends StatefulWidget {
final Post post;
final PostVideoController postVideoController;
const PostVideo({
super.key,
required this.post,
required this.postVideoController,
});
@override
State<PostVideo> createState() => _PostVideoState();
}
class _PostVideoState extends State<PostVideo> {
late final VideoPlayerController _videoController;
bool _videoReady = false;
@override
void initState() {
super.initState();
initVideo();
}
Future<void> initVideo() async {
Blob videoBlob = widget.post.blobs[1];
if (videoBlob.isPending && videoBlob.localFilePath.isNotEmpty) {
_videoController = VideoPlayerController.file(
File(
videoBlob.localFilePath,
),
);
} else {
_videoController = VideoPlayerController.networkUrl(
Uri.parse(
widget.post.blobs[1].hdMp4Url,
),
);
}
await _videoController.initialize();
widget.postVideoController._attach(_videoController);
widget.postVideoController._notifyPlaybackReady();
WakelockPlus.enable();
setState(() {
_videoReady = true;
});
}
@override
void dispose() async {
super.dispose();
await _videoController.dispose();
widget.postVideoController.detach();
WakelockPlus.disable();
}
@override
Widget build(BuildContext context) {
if (_videoReady) {
return _getVideoWidget();
} else {
return _getWaitingForVideoWidget();
}
}
Widget _getWaitingForVideoWidget() {
return Container(
width: double.infinity,
child: AspectRatio(
aspectRatio: widget.post.blobs[1].aspectRatio,
child: Container(
color: Theme.of(context).colorScheme.background,
),
),
);
}
Widget _getVideoWidget() {
return Container(
width: double.infinity,
child: GestureDetector(
onTap: widget.postVideoController.playOrPause,
child: AspectRatio(
aspectRatio: widget.post.blobs[1].aspectRatio,
child: VideoPlayer(
_videoController,
),
),
),
);
}
}
class PostVideoController {
VideoPlayerController? _controller;
VoidCallback? onPlaybackReady;
PostVideoController(VoidCallback onPlaybackReady) {
this.onPlaybackReady = onPlaybackReady;
}
void _notifyPlaybackReady() {
if (onPlaybackReady != null) {
onPlaybackReady!();
}
}
void _attach(VideoPlayerController controller) {
_controller = controller;
}
void detach() {
_controller = null;
}
void play() {
if (_controller != null && !_controller!.value.isPlaying) {
_controller!.play();
}
}
void pause() {
if (_controller != null && _controller!.value.isPlaying) {
_controller!.pause();
}
}
void playOrPause() {
if (_controller == null) {
return;
}
if (_controller!.value.isPlaying) {
pause();
} else {
play();
}
}
}
Also it looks like this issue is happening very regularly and is resulting in user visible crashes.
stack trace is required to analyze the crash. btw I fixed a crash issue, in your project dir run
flutter clean
pod cache clean mdk
find . -name Podfile.lock -delete
then build again
I will work on getting a stack trace and get back to you.
flutter: Bad state: Future already completed
flutter:
#0 _AsyncCompleter.complete (dart:async/future_impl.dart:43:31)
#1 VideoPlayerController.initialize.eventListener (package:video_player/video_player.dart:454:33)
#2 _RootZone.runUnaryGuarded (dart:async/zone.dart:1594:10)
#3 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
#4 _DelayedData.perform (dart:async/stream_impl.dart:515:14)
#5 _PendingEvents.handleNext (dart:async/stream_impl.dart:620:11)
#6 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:591:7)
#7 _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#8 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
I have figured out how to reproduce this.
In my app I click on a video post and it plays without issue. At the end of the video the playback stops (not set to loop mode). To replay the video you just tap the video and it calls
void play() {
if (_controller != null && !_controller!.value.isPlaying) {
_controller!.play();
}
}
and that starts the video from the beginning. When the video restarts the above stacktrace logs from the error that is thrown. The video still plays without issue and there does not appear to be any user visible problems.
This only happens when playing a video for the second time. The video loads fine initially and plays through without any exception being thrown.
It seems like the root issue is calling play() on a controller attached to a video which is already initialized
yes. official ios/android implementation becomes paused state and resources are not released when reaches end, initialized event is sent only once. but fvp releases internal resources when playback finished, calling play() again will initialize resources and send event again.
try fvp master branch code, the event is sent only once.
Thanks Wang, I am installing and pushing out to devices today. I will let you know if the issue continues.
Hey, looking at Crashlytics it looks like the error is fixed. Thanks!
VideoPlayerController.initialize.eventListener FlutterError - Bad state: Future already completed package:video_player/video_player.dart:454 Repetitive crashes
I am using FVP to override the iOS video player in my app. I have been seeing the above error in my Firebase Crashlytics consistently since I started using FVP (only occurs on iOS devices). This does not appear to result in any user visible issue but it is a repetitive crash.