Skip to content

Commit 9c4c293

Browse files
kmod-midoriamantoux
authored andcommitted
fix(video_player): buffering state events missing on Android & Web (fixes flutter/flutter#28494) (flutter#2563)
(cherry picked from commit b2e9ca5)
1 parent 23edab2 commit 9c4c293

File tree

5 files changed

+141
-1
lines changed

5 files changed

+141
-1
lines changed

packages/video_player/video_player/CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
## 2.0.0-nullsafety.4
2+
3+
* Fixed an issue where `isBuffering` was not updating on Android.
4+
5+
## 2.0.0-nullsafety.3
6+
7+
* Dart null safety requires `2.12`.
8+
9+
## 2.0.0-nullsafety.2
10+
11+
* Bump SDK version.
12+
13+
## 2.0.0-nullsafety.1
14+
15+
* Merge master.
16+
17+
## 2.0.0-nullsafety
18+
19+
* Migration to null safety.
20+
21+
## 1.0.2
22+
23+
* Update Flutter SDK constraint.
24+
125
## 1.0.1
226

327
* Android: Dispose video players when app is closed.

packages/video_player/video_player/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,21 @@ public void onCancel(Object o) {
169169

170170
exoPlayer.addListener(
171171
new EventListener() {
172+
private boolean isBuffering = false;
173+
174+
public void setBuffering(boolean buffering) {
175+
if (isBuffering != buffering) {
176+
isBuffering = buffering;
177+
Map<String, Object> event = new HashMap<>();
178+
event.put("event", isBuffering ? "bufferingStart" : "bufferingEnd");
179+
eventSink.success(event);
180+
}
181+
}
172182

173183
@Override
174184
public void onPlaybackStateChanged(final int playbackState) {
175185
if (playbackState == Player.STATE_BUFFERING) {
186+
setBuffering(true);
176187
sendBufferingUpdate();
177188
} else if (playbackState == Player.STATE_READY) {
178189
if (!isInitialized) {
@@ -184,10 +195,15 @@ public void onPlaybackStateChanged(final int playbackState) {
184195
event.put("event", "completed");
185196
eventSink.success(event);
186197
}
198+
199+
if (playbackState != Player.STATE_BUFFERING) {
200+
setBuffering(false);
201+
}
187202
}
188203

189204
@Override
190205
public void onPlayerError(final ExoPlaybackException error) {
206+
setBuffering(false);
191207
if (eventSink != null) {
192208
eventSink.error("VideoError", "Video player had error " + error, null);
193209
}

packages/video_player/video_player/example/integration_test/video_player_test.dart

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
// TODO(amirh): Remove this once flutter_driver supports null safety.
6+
// https://github.com/flutter/flutter/issues/71379
7+
// @dart = 2.9
8+
import 'dart:async';
9+
10+
import 'package:flutter/foundation.dart';
511
import 'package:flutter/material.dart';
612
import 'package:integration_test/integration_test.dart';
713
import 'package:flutter_test/flutter_test.dart';
@@ -29,10 +35,58 @@ void main() {
2935
const Duration(seconds: 7, milliseconds: 540));
3036
});
3137

38+
testWidgets(
39+
'reports buffering status',
40+
(WidgetTester tester) async {
41+
VideoPlayerController networkController = VideoPlayerController.network(
42+
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
43+
);
44+
await networkController.initialize();
45+
// Mute to allow playing without DOM interaction on Web.
46+
// See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
47+
await networkController.setVolume(0);
48+
final Completer<void> started = Completer();
49+
final Completer<void> ended = Completer();
50+
bool startedBuffering = false;
51+
bool endedBuffering = false;
52+
networkController.addListener(() {
53+
if (networkController.value.isBuffering && !startedBuffering) {
54+
startedBuffering = true;
55+
started.complete();
56+
}
57+
if (startedBuffering &&
58+
!networkController.value.isBuffering &&
59+
!endedBuffering) {
60+
endedBuffering = true;
61+
ended.complete();
62+
}
63+
});
64+
65+
await networkController.play();
66+
await networkController.seekTo(const Duration(seconds: 5));
67+
await tester.pumpAndSettle(_playDuration);
68+
await networkController.pause();
69+
70+
expect(networkController.value.isPlaying, false);
71+
expect(networkController.value.position,
72+
(Duration position) => position > const Duration(seconds: 0));
73+
74+
await started;
75+
expect(startedBuffering, true);
76+
77+
await ended;
78+
expect(endedBuffering, true);
79+
},
80+
skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android),
81+
);
82+
3283
testWidgets(
3384
'can be played',
3485
(WidgetTester tester) async {
3586
await _controller.initialize();
87+
// Mute to allow playing without DOM interaction on Web.
88+
// See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
89+
await _controller.setVolume(0);
3690

3791
await _controller.play();
3892
await tester.pumpAndSettle(_playDuration);
@@ -58,6 +112,9 @@ void main() {
58112
'can be paused',
59113
(WidgetTester tester) async {
60114
await _controller.initialize();
115+
// Mute to allow playing without DOM interaction on Web.
116+
// See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
117+
await _controller.setVolume(0);
61118

62119
// Play for a second, then pause, and then wait a second.
63120
await _controller.play();
@@ -104,6 +161,6 @@ void main() {
104161

105162
await tester.pumpAndSettle();
106163
expect(_controller.value.isPlaying, true);
107-
});
164+
}, skip: kIsWeb); // Web does not support local assets.
108165
});
109166
}

packages/video_player/video_player_web/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## 2.0.0-nullsafety.2
2+
3+
* Fixed an issue where `isBuffering` was not updating on Web.
4+
5+
## 2.0.0-nullsafety.1
6+
7+
* Bump Dart SDK to support null safety.
8+
9+
## 2.0.0-nullsafety
10+
11+
* Migrate to null safety.
12+
13+
## 0.1.4+2
14+
15+
* Update Flutter SDK constraint.
16+
117
## 0.1.4+1
218

319
* Substitute `undefined_prefixed_name: ignore` analyzer setting by a `dart:ui` shim with conditional exports. [Issue](https://github.com/flutter/flutter/issues/69309).

packages/video_player/video_player_web/lib/video_player_web.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,17 @@ class _VideoPlayer {
156156
final int textureId;
157157
VideoElement videoElement;
158158
bool isInitialized = false;
159+
bool isBuffering = false;
160+
161+
void setBuffering(bool buffering) {
162+
if (isBuffering != buffering) {
163+
isBuffering = buffering;
164+
eventController.add(VideoEvent(
165+
eventType: isBuffering
166+
? VideoEventType.bufferingStart
167+
: VideoEventType.bufferingEnd));
168+
}
169+
}
159170

160171
void initialize() {
161172
videoElement = VideoElement()
@@ -176,10 +187,25 @@ class _VideoPlayer {
176187
isInitialized = true;
177188
sendInitialized();
178189
}
190+
setBuffering(false);
191+
});
192+
193+
videoElement.onCanPlayThrough.listen((dynamic _) {
194+
setBuffering(false);
195+
});
196+
197+
videoElement.onPlaying.listen((dynamic _) {
198+
setBuffering(false);
199+
});
200+
201+
videoElement.onWaiting.listen((dynamic _) {
202+
setBuffering(true);
203+
sendBufferingUpdate();
179204
});
180205

181206
// The error event fires when some form of error occurs while attempting to load or perform the media.
182207
videoElement.onError.listen((Event _) {
208+
setBuffering(false);
183209
// The Event itself (_) doesn't contain info about the actual error.
184210
// We need to look at the HTMLMediaElement.error.
185211
// See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error
@@ -192,6 +218,7 @@ class _VideoPlayer {
192218
});
193219

194220
videoElement.onEnded.listen((dynamic _) {
221+
setBuffering(false);
195222
eventController.add(VideoEvent(eventType: VideoEventType.completed));
196223
});
197224
}

0 commit comments

Comments
 (0)