[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.
diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index 013e20f..f96308b 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.10.5+1
+
+* Fixes issue where `initialize()` `Future` stalls when failing to load source
+  data and does not throw an error.
+
 ## 0.10.5
 
 * Support `web` by default.
diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart
index cb24ac8..c41b47c 100644
--- a/packages/video_player/video_player/lib/video_player.dart
+++ b/packages/video_player/video_player/lib/video_player.dart
@@ -272,6 +272,9 @@
       final PlatformException e = obj;
       value = VideoPlayerValue.erroneous(e.message);
       _timer?.cancel();
+      if (!initializingCompleter.isCompleted) {
+        initializingCompleter.completeError(obj);
+      }
     }
 
     _eventSubscription = VideoPlayerPlatform.instance
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index 43c9ead..33af287 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -1,7 +1,7 @@
 name: video_player
 description: Flutter plugin for displaying inline video with other Flutter
   widgets on Android and iOS.
-version: 0.10.5
+version: 0.10.5+1
 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
 
 flutter:
diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart
index d2a90b7..ebabacb 100644
--- a/packages/video_player/video_player/test/video_player_test.dart
+++ b/packages/video_player/video_player/test/video_player_test.dart
@@ -134,6 +134,21 @@
             });
       });
 
+      test('init errors', () async {
+        final VideoPlayerController controller = VideoPlayerController.network(
+          'http://testing.com/invalid_url',
+        );
+        try {
+          dynamic error;
+          fakeVideoPlayerPlatform.forceInitError = true;
+          await controller.initialize().catchError((dynamic e) => error = e);
+          final PlatformException platformEx = error;
+          expect(platformEx.code, equals('VideoError'));
+        } finally {
+          fakeVideoPlayerPlatform.forceInitError = false;
+        }
+      });
+
       test('file', () async {
         final VideoPlayerController controller =
             VideoPlayerController.file(File('a.avi'));
@@ -437,6 +452,7 @@
   List<MethodCall> calls = <MethodCall>[];
   List<Map<String, dynamic>> dataSourceDescriptions = <Map<String, dynamic>>[];
   final Map<int, FakeVideoEventStream> streams = <int, FakeVideoEventStream>{};
+  bool forceInitError = false;
   int nextTextureId = 0;
   final Map<int, Duration> _positions = <int, Duration>{};
 
@@ -447,8 +463,8 @@
         initialized.complete(true);
         break;
       case 'create':
-        streams[nextTextureId] = FakeVideoEventStream(
-            nextTextureId, 100, 100, const Duration(seconds: 1));
+        streams[nextTextureId] = FakeVideoEventStream(nextTextureId, 100, 100,
+            const Duration(seconds: 1), forceInitError);
         final Map<dynamic, dynamic> dataSource = call.arguments;
         dataSourceDescriptions.add(dataSource.cast<String, dynamic>());
         return Future<Map<String, int>>.sync(() {
@@ -481,7 +497,8 @@
 }
 
 class FakeVideoEventStream {
-  FakeVideoEventStream(this.textureId, this.width, this.height, this.duration) {
+  FakeVideoEventStream(this.textureId, this.width, this.height, this.duration,
+      this.initWithError) {
     eventsChannel = FakeEventsChannel(
         'flutter.io/videoPlayer/videoEvents$textureId', onListen);
   }
@@ -490,16 +507,20 @@
   int width;
   int height;
   Duration duration;
+  bool initWithError;
   FakeEventsChannel eventsChannel;
 
   void onListen() {
-    final Map<String, dynamic> initializedEvent = <String, dynamic>{
-      'event': 'initialized',
-      'duration': duration.inMilliseconds,
-      'width': width,
-      'height': height,
-    };
-    eventsChannel.sendEvent(initializedEvent);
+    if (!initWithError) {
+      eventsChannel.sendEvent(<String, dynamic>{
+        'event': 'initialized',
+        'duration': duration.inMilliseconds,
+        'width': width,
+        'height': height,
+      });
+    } else {
+      eventsChannel.sendError('VideoError', 'Video player had error XYZ');
+    }
   }
 }
 
@@ -522,13 +543,23 @@
   }
 
   void sendEvent(dynamic event) {
+    _sendMessage(const StandardMethodCodec().encodeSuccessEnvelope(event));
+  }
+
+  void sendError(String code, [String message, dynamic details]) {
+    _sendMessage(const StandardMethodCodec().encodeErrorEnvelope(
+      code: code,
+      message: message,
+      details: details,
+    ));
+  }
+
+  void _sendMessage(ByteData data) {
     // TODO(jackson): This has been deprecated and should be replaced
     // with `ServicesBinding.instance.defaultBinaryMessenger` when it's
     // available on all the versions of Flutter that we test.
     // ignore: deprecated_member_use
     defaultBinaryMessenger.handlePlatformMessage(
-        eventsMethodChannel.name,
-        const StandardMethodCodec().encodeSuccessEnvelope(event),
-        (ByteData data) {});
+        eventsMethodChannel.name, data, (ByteData data) {});
   }
 }