Skip to content

Commit 25a0d9d

Browse files
authored
Merge pull request #912 from AerofoilsGmbH/android-buffering-workaround
Add workaround for invalid buffering info on android
2 parents 0a390f9 + 85cddc5 commit 25a0d9d

File tree

5 files changed

+89
-7
lines changed

5 files changed

+89
-7
lines changed

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ Chewie uses the `video_player` under the hood and wraps it in a friendly Materia
2424
8. 🧪 [Example](#-example)
2525
9.[Migrating from Chewie < 0.9.0](#-migrating-from-chewie--090)
2626
10. 🗺️ [Roadmap](#%EF%B8%8F-roadmap)
27-
11. 📱 [iOS warning](#-ios-warning-)
27+
11. ⚠️ [Android warning](#%EF%B8%8F-android-warning)
28+
12. 📱 [iOS warning](#-ios-warning)
2829

2930

3031
## 🚨 IMPORTANT!!! (READ THIS FIRST)
@@ -286,6 +287,42 @@ final playerWidget = Chewie(
286287
- [ ] Screen-Mirroring / Casting (Google Chromecast)
287288

288289

290+
## ⚠️ Android warning
291+
292+
There is an open [issue](https://github.com/flutter/flutter/issues/165149) that the buffering state of a video is not reported correctly. With this, the loading state is always triggered, hiding controls to play, pause or seek the video. A workaround was implemented until this is fixed, however it can't be perfect and still hides controls if seeking backwards while the video is paused, as a result of lack of correct buffering information (see #912).
293+
294+
Add the following to partly fix this behavior:
295+
296+
```dart
297+
// Your init code can be above
298+
videoController.addListener(yourListeningMethod);
299+
300+
// ...
301+
302+
bool wasPlayingBefore = false;
303+
void yourListeningMethod() {
304+
if (!videoController.value.isPlaying && !wasPlayingBefore) {
305+
// -> Workaround if seekTo another position while it was paused before.
306+
// On Android this might lead to infinite loading, so just play the
307+
// video again.
308+
videoController.play();
309+
}
310+
311+
wasPlayingBefore = videoController.value.isPlaying;
312+
313+
// ...
314+
}
315+
```
316+
317+
You can also disable the loading spinner entirely to fix this problem in a more _complete_ way, however will remove the loading indicator if a video is buffering.
318+
319+
```dart
320+
_chewieController = ChewieController(
321+
videoPlayerController: _videoPlayerController,
322+
progressIndicatorDelay: Platform.isAndroid ? const Duration(days: 1) : null,
323+
);
324+
```
325+
289326
## 📱 iOS warning
290327

291328
The video_player plugin used by chewie will only work in iOS simulators if you are on flutter 1.26.0 or above. You may need to switch to the beta channel `flutter channel beta`

lib/src/cupertino/cupertino_controls.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,9 +810,11 @@ class _CupertinoControlsState extends State<CupertinoControls>
810810
void _updateState() {
811811
if (!mounted) return;
812812

813+
final bool buffering = getIsBuffering(controller);
814+
813815
// display the progress bar indicator only after the buffering delay if it has been set
814816
if (chewieController.progressIndicatorDelay != null) {
815-
if (controller.value.isBuffering) {
817+
if (buffering) {
816818
_bufferingDisplayTimer ??= Timer(
817819
chewieController.progressIndicatorDelay!,
818820
_bufferingTimerTimeout,
@@ -823,7 +825,7 @@ class _CupertinoControlsState extends State<CupertinoControls>
823825
_displayBufferingIndicator = false;
824826
}
825827
} else {
826-
_displayBufferingIndicator = controller.value.isBuffering;
828+
_displayBufferingIndicator = buffering;
827829
}
828830

829831
setState(() {

lib/src/helpers/utils.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:video_player/video_player.dart';
3+
14
String formatDuration(Duration position) {
25
final ms = position.inMilliseconds;
36

@@ -30,3 +33,39 @@ String formatDuration(Duration position) {
3033

3134
return formattedTime;
3235
}
36+
37+
/// Gets the current buffering state of the video player.
38+
///
39+
/// For Android, it will use a workaround due to a [bug](https://github.com/flutter/flutter/issues/165149)
40+
/// affecting the `video_player` plugin, preventing it from getting the
41+
/// actual buffering state. This currently results in the `VideoPlayerController` always buffering,
42+
/// thus breaking UI elements.
43+
///
44+
/// For this, the actual buffer position is used to determine if the video is
45+
/// buffering or not. See Issue [#912](https://github.com/fluttercommunity/chewie/pull/912) for more details.
46+
bool getIsBuffering(VideoPlayerController controller) {
47+
final VideoPlayerValue value = controller.value;
48+
49+
if (defaultTargetPlatform == TargetPlatform.android) {
50+
if (value.isBuffering) {
51+
// -> Check if we actually buffer, as android has a bug preventing to
52+
// get the correct buffering state from this single bool.
53+
final int position = value.position.inMilliseconds;
54+
55+
// Special case, if the video is finished, we don't want to show the
56+
// buffering indicator anymore
57+
if (position >= value.duration.inMilliseconds) {
58+
return false;
59+
} else {
60+
final int buffer = value.buffered.lastOrNull?.end.inMilliseconds ?? -1;
61+
62+
return position >= buffer;
63+
}
64+
} else {
65+
// -> No buffering
66+
return false;
67+
}
68+
}
69+
70+
return value.isBuffering;
71+
}

lib/src/material/material_controls.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,9 +645,11 @@ class _MaterialControlsState extends State<MaterialControls>
645645
void _updateState() {
646646
if (!mounted) return;
647647

648+
final bool buffering = getIsBuffering(controller);
649+
648650
// display the progress bar indicator only after the buffering delay if it has been set
649651
if (chewieController.progressIndicatorDelay != null) {
650-
if (controller.value.isBuffering) {
652+
if (buffering) {
651653
_bufferingDisplayTimer ??= Timer(
652654
chewieController.progressIndicatorDelay!,
653655
_bufferingTimerTimeout,
@@ -658,7 +660,7 @@ class _MaterialControlsState extends State<MaterialControls>
658660
_displayBufferingIndicator = false;
659661
}
660662
} else {
661-
_displayBufferingIndicator = controller.value.isBuffering;
663+
_displayBufferingIndicator = buffering;
662664
}
663665

664666
setState(() {

lib/src/material/material_desktop_controls.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,11 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
581581
void _updateState() {
582582
if (!mounted) return;
583583

584+
final bool buffering = getIsBuffering(controller);
585+
584586
// display the progress bar indicator only after the buffering delay if it has been set
585587
if (chewieController.progressIndicatorDelay != null) {
586-
if (controller.value.isBuffering) {
588+
if (buffering) {
587589
_bufferingDisplayTimer ??= Timer(
588590
chewieController.progressIndicatorDelay!,
589591
_bufferingTimerTimeout,
@@ -594,7 +596,7 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
594596
_displayBufferingIndicator = false;
595597
}
596598
} else {
597-
_displayBufferingIndicator = controller.value.isBuffering;
599+
_displayBufferingIndicator = buffering;
598600
}
599601

600602
setState(() {

0 commit comments

Comments
 (0)