Skip to content

Add workaround for invalid buffering info on android #912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Chewie uses the `video_player` under the hood and wraps it in a friendly Materia
8. 🧪 [Example](#-example)
9. ⏪ [Migrating from Chewie < 0.9.0](#-migrating-from-chewie--090)
10. 🗺️ [Roadmap](#%EF%B8%8F-roadmap)
11. 📱 [iOS warning](#-ios-warning-)
11. ⚠️ [Android warning](#-android-warning-)
12. 📱 [iOS warning](#-ios-warning-)


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


## ⚠️ Android warning

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).

Add the following to partly fix this behavior:

```dart
// Your init code can be above
videoController.addListener(yourListeningMethod);

// ...

bool wasPlayingBefore = false;
void yourListeningMethod() {
if (!videoController.value.isPlaying && !wasPlayingBefore) {
// -> Workaround if seekTo another position while it was paused before.
// On Android this might lead to infinite loading, so just play the
// video again.
videoController.play();
}

wasPlayingBefore = videoController.value.isPlaying;

// ...
}
```

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.

```dart
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
progressIndicatorDelay: Platform.isAndroid ? const Duration(days: 1) : null,
);
```

## 📱 iOS warning

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`
Expand Down
6 changes: 4 additions & 2 deletions lib/src/cupertino/cupertino_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -810,9 +810,11 @@ class _CupertinoControlsState extends State<CupertinoControls>
void _updateState() {
if (!mounted) return;

final bool buffering = getIsBuffering(controller);

// display the progress bar indicator only after the buffering delay if it has been set
if (chewieController.progressIndicatorDelay != null) {
if (controller.value.isBuffering) {
if (buffering) {
_bufferingDisplayTimer ??= Timer(
chewieController.progressIndicatorDelay!,
_bufferingTimerTimeout,
Expand All @@ -823,7 +825,7 @@ class _CupertinoControlsState extends State<CupertinoControls>
_displayBufferingIndicator = false;
}
} else {
_displayBufferingIndicator = controller.value.isBuffering;
_displayBufferingIndicator = buffering;
}

setState(() {
Expand Down
31 changes: 31 additions & 0 deletions lib/src/helpers/utils.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import 'dart:io';

import 'package:video_player/video_player.dart';

String formatDuration(Duration position) {
final ms = position.inMilliseconds;

Expand Down Expand Up @@ -30,3 +34,30 @@

return formattedTime;
}

bool getIsBuffering(VideoPlayerController controller) {
final VideoPlayerValue value = controller.value;

if (Platform.isAndroid) {
if (value.isBuffering) {

Check warning on line 42 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L42

Added line #L42 was not covered by tests
// -> Check if we actually buffer, as android has a bug preventing to
// get the correct buffering state from this single bool.
final int position = value.position.inMilliseconds;

Check warning on line 45 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L45

Added line #L45 was not covered by tests

// Special case, if the video is finished, we don't want to show the
// buffering indicator anymore
if (position >= value.duration.inMilliseconds) {

Check warning on line 49 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L49

Added line #L49 was not covered by tests
return false;
} else {
final int buffer = value.buffered.lastOrNull?.end.inMilliseconds ?? -1;

Check warning on line 52 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L52

Added line #L52 was not covered by tests

return position >= buffer;

Check warning on line 54 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L54

Added line #L54 was not covered by tests
}
} else {
// -> No buffering
return false;
}
}

return value.isBuffering;
}
6 changes: 4 additions & 2 deletions lib/src/material/material_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,11 @@ class _MaterialControlsState extends State<MaterialControls>
void _updateState() {
if (!mounted) return;

final bool buffering = getIsBuffering(controller);

// display the progress bar indicator only after the buffering delay if it has been set
if (chewieController.progressIndicatorDelay != null) {
if (controller.value.isBuffering) {
if (buffering) {
_bufferingDisplayTimer ??= Timer(
chewieController.progressIndicatorDelay!,
_bufferingTimerTimeout,
Expand All @@ -658,7 +660,7 @@ class _MaterialControlsState extends State<MaterialControls>
_displayBufferingIndicator = false;
}
} else {
_displayBufferingIndicator = controller.value.isBuffering;
_displayBufferingIndicator = buffering;
}

setState(() {
Expand Down
6 changes: 4 additions & 2 deletions lib/src/material/material_desktop_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -581,9 +581,11 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
void _updateState() {
if (!mounted) return;

final bool buffering = getIsBuffering(controller);

// display the progress bar indicator only after the buffering delay if it has been set
if (chewieController.progressIndicatorDelay != null) {
if (controller.value.isBuffering) {
if (buffering) {
_bufferingDisplayTimer ??= Timer(
chewieController.progressIndicatorDelay!,
_bufferingTimerTimeout,
Expand All @@ -594,7 +596,7 @@ class _MaterialDesktopControlsState extends State<MaterialDesktopControls>
_displayBufferingIndicator = false;
}
} else {
_displayBufferingIndicator = controller.value.isBuffering;
_displayBufferingIndicator = buffering;
}

setState(() {
Expand Down