Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit f959157

Browse files
slightfootMichael Klimushyn
authored and
Michael Klimushyn
committed
[video_player] Fixes video initialization future stall. (#2134)
Fixes issue where `initialize()` `Future` stalls when failing to load source data and does not throw error. For example if the network is offline and you try to load a video. The video will attempt to load the source on Android through ExoPlayer. This error of failing to load is reported through the event stream to the client. However, this does not complete the initialization `Completer` leaving the `Future stalled waiting for the completer to complete which it never will.
1 parent 58f2698 commit f959157

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed

packages/video_player/video_player/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.10.5+1
2+
3+
* Fixes issue where `initialize()` `Future` stalls when failing to load source
4+
data and does not throw an error.
5+
16
## 0.10.5
27

38
* Support `web` by default.

packages/video_player/video_player/lib/video_player.dart

+3
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
272272
final PlatformException e = obj;
273273
value = VideoPlayerValue.erroneous(e.message);
274274
_timer?.cancel();
275+
if (!initializingCompleter.isCompleted) {
276+
initializingCompleter.completeError(obj);
277+
}
275278
}
276279

277280
_eventSubscription = VideoPlayerPlatform.instance

packages/video_player/video_player/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: video_player
22
description: Flutter plugin for displaying inline video with other Flutter
33
widgets on Android and iOS.
4-
version: 0.10.5
4+
version: 0.10.5+1
55
homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
66

77
flutter:

packages/video_player/video_player/test/video_player_test.dart

+44-13
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,21 @@ void main() {
134134
});
135135
});
136136

137+
test('init errors', () async {
138+
final VideoPlayerController controller = VideoPlayerController.network(
139+
'http://testing.com/invalid_url',
140+
);
141+
try {
142+
dynamic error;
143+
fakeVideoPlayerPlatform.forceInitError = true;
144+
await controller.initialize().catchError((dynamic e) => error = e);
145+
final PlatformException platformEx = error;
146+
expect(platformEx.code, equals('VideoError'));
147+
} finally {
148+
fakeVideoPlayerPlatform.forceInitError = false;
149+
}
150+
});
151+
137152
test('file', () async {
138153
final VideoPlayerController controller =
139154
VideoPlayerController.file(File('a.avi'));
@@ -437,6 +452,7 @@ class FakeVideoPlayerPlatform {
437452
List<MethodCall> calls = <MethodCall>[];
438453
List<Map<String, dynamic>> dataSourceDescriptions = <Map<String, dynamic>>[];
439454
final Map<int, FakeVideoEventStream> streams = <int, FakeVideoEventStream>{};
455+
bool forceInitError = false;
440456
int nextTextureId = 0;
441457
final Map<int, Duration> _positions = <int, Duration>{};
442458

@@ -447,8 +463,8 @@ class FakeVideoPlayerPlatform {
447463
initialized.complete(true);
448464
break;
449465
case 'create':
450-
streams[nextTextureId] = FakeVideoEventStream(
451-
nextTextureId, 100, 100, const Duration(seconds: 1));
466+
streams[nextTextureId] = FakeVideoEventStream(nextTextureId, 100, 100,
467+
const Duration(seconds: 1), forceInitError);
452468
final Map<dynamic, dynamic> dataSource = call.arguments;
453469
dataSourceDescriptions.add(dataSource.cast<String, dynamic>());
454470
return Future<Map<String, int>>.sync(() {
@@ -481,7 +497,8 @@ class FakeVideoPlayerPlatform {
481497
}
482498

483499
class FakeVideoEventStream {
484-
FakeVideoEventStream(this.textureId, this.width, this.height, this.duration) {
500+
FakeVideoEventStream(this.textureId, this.width, this.height, this.duration,
501+
this.initWithError) {
485502
eventsChannel = FakeEventsChannel(
486503
'flutter.io/videoPlayer/videoEvents$textureId', onListen);
487504
}
@@ -490,16 +507,20 @@ class FakeVideoEventStream {
490507
int width;
491508
int height;
492509
Duration duration;
510+
bool initWithError;
493511
FakeEventsChannel eventsChannel;
494512

495513
void onListen() {
496-
final Map<String, dynamic> initializedEvent = <String, dynamic>{
497-
'event': 'initialized',
498-
'duration': duration.inMilliseconds,
499-
'width': width,
500-
'height': height,
501-
};
502-
eventsChannel.sendEvent(initializedEvent);
514+
if (!initWithError) {
515+
eventsChannel.sendEvent(<String, dynamic>{
516+
'event': 'initialized',
517+
'duration': duration.inMilliseconds,
518+
'width': width,
519+
'height': height,
520+
});
521+
} else {
522+
eventsChannel.sendError('VideoError', 'Video player had error XYZ');
523+
}
503524
}
504525
}
505526

@@ -522,13 +543,23 @@ class FakeEventsChannel {
522543
}
523544

524545
void sendEvent(dynamic event) {
546+
_sendMessage(const StandardMethodCodec().encodeSuccessEnvelope(event));
547+
}
548+
549+
void sendError(String code, [String message, dynamic details]) {
550+
_sendMessage(const StandardMethodCodec().encodeErrorEnvelope(
551+
code: code,
552+
message: message,
553+
details: details,
554+
));
555+
}
556+
557+
void _sendMessage(ByteData data) {
525558
// TODO(jackson): This has been deprecated and should be replaced
526559
// with `ServicesBinding.instance.defaultBinaryMessenger` when it's
527560
// available on all the versions of Flutter that we test.
528561
// ignore: deprecated_member_use
529562
defaultBinaryMessenger.handlePlatformMessage(
530-
eventsMethodChannel.name,
531-
const StandardMethodCodec().encodeSuccessEnvelope(event),
532-
(ByteData data) {});
563+
eventsMethodChannel.name, data, (ByteData data) {});
533564
}
534565
}

0 commit comments

Comments
 (0)