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 all 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](#%EF%B8%8F-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
39 changes: 39 additions & 0 deletions lib/src/helpers/utils.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:video_player/video_player.dart';

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

Expand Down Expand Up @@ -30,3 +33,39 @@

return formattedTime;
}

/// Gets the current buffering state of the video player.
///
/// For Android, it will use a workaround due to a [bug](https://github.com/flutter/flutter/issues/165149)
/// affecting the `video_player` plugin, preventing it from getting the
/// actual buffering state. This currently results in the `VideoPlayerController` always buffering,
/// thus breaking UI elements.
///
/// For this, the actual buffer position is used to determine if the video is
/// buffering or not. See Issue [#912](https://github.com/fluttercommunity/chewie/pull/912) for more details.
bool getIsBuffering(VideoPlayerController controller) {
final VideoPlayerValue value = controller.value;

if (defaultTargetPlatform == TargetPlatform.android) {
if (value.isBuffering) {
// -> 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 53 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L53

Added line #L53 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 57 in lib/src/helpers/utils.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L57

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

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

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L60

Added line #L60 was not covered by tests

return position >= buffer;

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

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L62

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

return value.isBuffering;

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

View check run for this annotation

Codecov / codecov/patch

lib/src/helpers/utils.dart#L70

Added line #L70 was not covered by tests
}
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